]> arthur.barton.de Git - netdata.git/blob - src/macos_sysctl.c
Add IPv4 UDP charts to macOS plugin
[netdata.git] / src / macos_sysctl.c
1 #include "common.h"
2 #include <sys/sysctl.h>
3 // NEEDED BY: do_bandwidth
4 #include <net/route.h>
5 // NEEDED BY do_tcp...
6 #include <sys/socketvar.h>
7 #include <netinet/tcp_var.h>
8 #include <netinet/tcp_fsm.h>
9 // NEEDED BY do_udp..., do_ip...
10 #include <netinet/ip_var.h>
11 // NEEDED BY do_udp...
12 #include <netinet/udp.h>
13 #include <netinet/udp_var.h>
14
15 #define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var))
16
17 // MacOS calculates load averages once every 5 seconds
18 #define MIN_LOADAVG_UPDATE_EVERY 5
19
20 int getsysctl(const char *name, void *ptr, size_t len);
21
22 int do_macos_sysctl(int update_every, usec_t dt) {
23     (void)dt;
24
25     static int do_loadavg = -1, do_swap = -1, do_bandwidth = -1,
26                do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1, do_ecn = -1,
27                do_tcpext_syscookies = -1, do_tcpext_ofo = -1, do_tcpext_connaborts = -1,
28                do_udp_packets = -1, do_udp_errors = -1;
29
30
31     if (unlikely(do_loadavg == -1)) {
32         do_loadavg              = config_get_boolean("plugin:macos:sysctl", "enable load average", 1);
33         do_swap                 = config_get_boolean("plugin:macos:sysctl", "system swap", 1);
34         do_bandwidth            = config_get_boolean("plugin:macos:sysctl", "bandwidth", 1);
35         do_tcp_packets          = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP packets", 1);
36         do_tcp_errors           = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP errors", 1);
37         do_tcp_handshake        = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP handshake issues", 1);
38         do_ecn                  = config_get_boolean_ondemand("plugin:macos:sysctl", "ECN packets", CONFIG_ONDEMAND_ONDEMAND);
39         do_tcpext_syscookies    = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND);
40         do_tcpext_ofo           = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND);
41         do_tcpext_connaborts    = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND);
42         do_udp_packets          = config_get_boolean("plugin:macos:sysctl", "ipv4 UDP packets", 1);
43         do_udp_errors           = config_get_boolean("plugin:macos:sysctl", "ipv4 UDP errors", 1);
44     }
45
46     RRDSET *st;
47
48     int system_pagesize = getpagesize(); // wouldn't it be better to get value directly from hw.pagesize?
49     int i, n;
50     int common_error = 0;
51     size_t size;
52
53     // NEEDED BY: do_loadavg
54     static usec_t last_loadavg_usec = 0;
55     struct loadavg sysload;
56
57     // NEEDED BY: do_swap
58     struct xsw_usage swap_usage;
59
60     // NEEDED BY: do_bandwidth
61     int mib[6];
62     static char *ifstatdata = NULL;
63     char *lim, *next;
64     struct if_msghdr *ifm;
65     struct iftot {
66         u_long  ift_ibytes;
67         u_long  ift_obytes;
68     } iftot = {0, 0};
69
70     // NEEDED BY: do_tcp...
71     struct tcpstat tcpstat;
72     uint64_t tcps_states[TCP_NSTATES];
73
74     // NEEDED BY: do_udp...
75     struct udpstat udpstat;
76
77     if (last_loadavg_usec <= dt) {
78         if (likely(do_loadavg)) {
79             if (unlikely(GETSYSCTL("vm.loadavg", sysload))) {
80                 do_loadavg = 0;
81                 error("DISABLED: system.load");
82             } else {
83
84                 st = rrdset_find_bytype("system", "load");
85                 if (unlikely(!st)) {
86                     st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE);
87                     rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
88                     rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
89                     rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
90                 }
91                 else rrdset_next(st);
92
93                 rrddim_set(st, "load1", (collected_number) ((double)sysload.ldavg[0] / sysload.fscale * 1000));
94                 rrddim_set(st, "load5", (collected_number) ((double)sysload.ldavg[1] / sysload.fscale * 1000));
95                 rrddim_set(st, "load15", (collected_number) ((double)sysload.ldavg[2] / sysload.fscale * 1000));
96                 rrdset_done(st);
97             }
98         }
99
100         last_loadavg_usec = st->update_every * USEC_PER_SEC;
101     }
102     else last_loadavg_usec -= dt;
103
104     if (likely(do_swap)) {
105         if (unlikely(GETSYSCTL("vm.swapusage", swap_usage))) {
106             do_swap = 0;
107             error("DISABLED: system.swap");
108         } else {
109             st = rrdset_find("system.swap");
110             if (unlikely(!st)) {
111                 st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
112                 st->isdetail = 1;
113
114                 rrddim_add(st, "free",    NULL, 1, 1048576, RRDDIM_ABSOLUTE);
115                 rrddim_add(st, "used",    NULL, 1, 1048576, RRDDIM_ABSOLUTE);
116             }
117             else rrdset_next(st);
118
119             rrddim_set(st, "free", swap_usage.xsu_avail);
120             rrddim_set(st, "used", swap_usage.xsu_used);
121             rrdset_done(st);
122         }
123     }
124
125     // --------------------------------------------------------------------
126
127     if (likely(do_bandwidth)) {
128         mib[0] = CTL_NET;
129         mib[1] = PF_ROUTE;
130         mib[2] = 0;
131         mib[3] = AF_INET;
132         mib[4] = NET_RT_IFLIST2;
133         mib[5] = 0;
134         if (unlikely(sysctl(mib, 6, NULL, &size, NULL, 0))) {
135             error("MACOS: sysctl(%s...) failed: %s", "net interfaces", strerror(errno));
136             do_bandwidth = 0;
137             error("DISABLED: system.ipv4");
138         } else {
139             ifstatdata = reallocz(ifstatdata, size);
140             if (unlikely(sysctl(mib, 6, ifstatdata, &size, NULL, 0) < 0)) {
141                 error("MACOS: sysctl(%s...) failed: %s", "net interfaces", strerror(errno));
142                 do_bandwidth = 0;
143                 error("DISABLED: system.ipv4");
144             } else {
145                 lim = ifstatdata + size;
146                 iftot.ift_ibytes = iftot.ift_obytes = 0;
147                 for (next = ifstatdata; next < lim; ) {
148                     ifm = (struct if_msghdr *)next;
149                     next += ifm->ifm_msglen;
150
151                     if (ifm->ifm_type == RTM_IFINFO2) {
152                         struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
153
154                         iftot.ift_ibytes += if2m->ifm_data.ifi_ibytes;
155                         iftot.ift_obytes += if2m->ifm_data.ifi_obytes;
156                     }
157                 }
158                 st = rrdset_find("system.ipv4");
159                 if (unlikely(!st)) {
160                     st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
161
162                     rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
163                     rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
164                 }
165                 else rrdset_next(st);
166
167                 rrddim_set(st, "InOctets", iftot.ift_ibytes);
168                 rrddim_set(st, "OutOctets", iftot.ift_obytes);
169                 rrdset_done(st);
170             }
171         }
172     }
173
174     // --------------------------------------------------------------------
175
176     // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
177     if (likely(do_tcp_packets || do_tcp_errors || do_tcp_handshake || do_tcpext_connaborts || do_tcpext_ofo || do_tcpext_syscookies || do_ecn)) {
178         if (unlikely(GETSYSCTL("net.inet.tcp.stats", tcpstat))){
179             do_tcp_packets = 0;
180             error("DISABLED: ipv4.tcppackets");
181             do_tcp_errors = 0;
182             error("DISABLED: ipv4.tcperrors");
183             do_tcp_handshake = 0;
184             error("DISABLED: ipv4.tcphandshake");
185             do_tcpext_connaborts = 0;
186             error("DISABLED: ipv4.tcpconnaborts");
187             do_tcpext_ofo = 0;
188             error("DISABLED: ipv4.tcpofo");
189             do_tcpext_syscookies = 0;
190             error("DISABLED: ipv4.tcpsyncookies");
191             do_ecn = 0;
192             error("DISABLED: ipv4.ecnpkts");
193         } else {
194             if (likely(do_tcp_packets)) {
195                 st = rrdset_find("ipv4.tcppackets");
196                 if (unlikely(!st)) {
197                     st = rrdset_create("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets",
198                                        "packets/s",
199                                        2600, update_every, RRDSET_TYPE_LINE);
200
201                     rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL);
202                     rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL);
203                 } else
204                     rrdset_next(st);
205
206                 rrddim_set(st, "InSegs", tcpstat.tcps_rcvtotal);
207                 rrddim_set(st, "OutSegs", tcpstat.tcps_sndtotal);
208                 rrdset_done(st);
209             }
210
211             // --------------------------------------------------------------------
212
213             if (likely(do_tcp_errors)) {
214                 st = rrdset_find("ipv4.tcperrors");
215                 if (unlikely(!st)) {
216                     st = rrdset_create("ipv4", "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors",
217                                        "packets/s",
218                                        2700, update_every, RRDSET_TYPE_LINE);
219                     st->isdetail = 1;
220
221                     rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
222                     rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
223                     rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
224                 } else
225                     rrdset_next(st);
226
227                 rrddim_set(st, "InErrs", tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvshort);
228                 rrddim_set(st, "InCsumErrors", tcpstat.tcps_rcvbadsum);
229                 rrddim_set(st, "RetransSegs", tcpstat.tcps_sndrexmitpack);
230                 rrdset_done(st);
231             }
232
233             // --------------------------------------------------------------------
234
235             if (likely(do_tcp_handshake)) {
236                 st = rrdset_find("ipv4.tcphandshake");
237                 if (unlikely(!st)) {
238                     st = rrdset_create("ipv4", "tcphandshake", NULL, "tcp", NULL,
239                                        "IPv4 TCP Handshake Issues",
240                                        "events/s", 2900, update_every, RRDSET_TYPE_LINE);
241                     st->isdetail = 1;
242
243                     rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
244                     rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
245                     rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
246                     rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
247                 } else
248                     rrdset_next(st);
249
250                 rrddim_set(st, "EstabResets", tcpstat.tcps_drops);
251                 rrddim_set(st, "ActiveOpens", tcpstat.tcps_connattempt);
252                 rrddim_set(st, "PassiveOpens", tcpstat.tcps_accepts);
253                 rrddim_set(st, "AttemptFails", tcpstat.tcps_conndrops);
254                 rrdset_done(st);
255             }
256
257             // --------------------------------------------------------------------
258
259             if (do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop))) {
260                 do_tcpext_connaborts = CONFIG_ONDEMAND_YES;
261                 st = rrdset_find("ipv4.tcpconnaborts");
262                 if (unlikely(!st)) {
263                     st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE);
264
265                     rrddim_add(st, "TCPAbortOnData",    "baddata",     1, 1, RRDDIM_INCREMENTAL);
266                     rrddim_add(st, "TCPAbortOnClose",   "userclosed",  1, 1, RRDDIM_INCREMENTAL);
267                     rrddim_add(st, "TCPAbortOnMemory",  "nomemory",    1, 1, RRDDIM_INCREMENTAL);
268                     rrddim_add(st, "TCPAbortOnTimeout", "timeout",     1, 1, RRDDIM_INCREMENTAL);
269                 }
270                 else rrdset_next(st);
271
272                 rrddim_set(st, "TCPAbortOnData",    tcpstat.tcps_rcvpackafterwin);
273                 rrddim_set(st, "TCPAbortOnClose",   tcpstat.tcps_rcvafterclose);
274                 rrddim_set(st, "TCPAbortOnMemory",  tcpstat.tcps_rcvmemdrop);
275                 rrddim_set(st, "TCPAbortOnTimeout", tcpstat.tcps_persistdrop);
276                 rrdset_done(st);
277             }
278
279             // --------------------------------------------------------------------
280
281             if (do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && tcpstat.tcps_rcvoopack)) {
282                 do_tcpext_ofo = CONFIG_ONDEMAND_YES;
283                 st = rrdset_find("ipv4.tcpofo");
284                 if (unlikely(!st)) {
285                     st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE);
286
287                     rrddim_add(st, "TCPOFOQueue", "inqueue",  1, 1, RRDDIM_INCREMENTAL);
288                 }
289                 else rrdset_next(st);
290
291                 rrddim_set(st, "TCPOFOQueue",   tcpstat.tcps_rcvoopack);
292                 rrdset_done(st);
293             }
294
295             // --------------------------------------------------------------------
296
297             if (do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) {
298                 do_tcpext_syscookies = CONFIG_ONDEMAND_YES;
299
300                 st = rrdset_find("ipv4.tcpsyncookies");
301                 if (unlikely(!st)) {
302                     st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE);
303
304                     rrddim_add(st, "SyncookiesRecv",   "received",  1, 1, RRDDIM_INCREMENTAL);
305                     rrddim_add(st, "SyncookiesSent",   "sent",     -1, 1, RRDDIM_INCREMENTAL);
306                     rrddim_add(st, "SyncookiesFailed", "failed",   -1, 1, RRDDIM_INCREMENTAL);
307                 }
308                 else rrdset_next(st);
309
310                 rrddim_set(st, "SyncookiesRecv",   tcpstat.tcps_sc_recvcookie);
311                 rrddim_set(st, "SyncookiesSent",   tcpstat.tcps_sc_sendcookie);
312                 rrddim_set(st, "SyncookiesFailed", tcpstat.tcps_sc_zonefail);
313                 rrdset_done(st);
314             }
315
316             // --------------------------------------------------------------------
317
318             if (do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_ecn_recv_ce || tcpstat.tcps_ecn_not_supported))) {
319                 do_ecn = CONFIG_ONDEMAND_YES;
320                 st = rrdset_find("ipv4.ecnpkts");
321                 if (unlikely(!st)) {
322                     st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE);
323                     st->isdetail = 1;
324
325                     rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL);
326                     rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL);
327                 }
328                 else rrdset_next(st);
329
330                 rrddim_set(st, "InCEPkts", tcpstat.tcps_ecn_recv_ce);
331                 rrddim_set(st, "InNoECTPkts", tcpstat.tcps_ecn_not_supported);
332                 rrdset_done(st);
333             }
334
335         }
336     }
337
338     // --------------------------------------------------------------------
339
340     // see http://net-snmp.sourceforge.net/docs/mibs/udp.html
341     if (likely(do_udp_packets || do_udp_errors)) {
342         if (unlikely(GETSYSCTL("net.inet.udp.stats", udpstat))) {
343             do_udp_packets = 0;
344             error("DISABLED: ipv4.udppackets");
345             do_udp_errors = 0;
346             error("DISABLED: ipv4.udperrors");
347         } else {
348             if (likely(do_udp_packets)) {
349                 st = rrdset_find("ipv4.udppackets");
350                 if (unlikely(!st)) {
351                     st = rrdset_create("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets",
352                                        "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
353
354                     rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL);
355                     rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL);
356                 } else
357                     rrdset_next(st);
358
359                 rrddim_set(st, "InDatagrams", udpstat.udps_ipackets);
360                 rrddim_set(st, "OutDatagrams", udpstat.udps_opackets);
361                 rrdset_done(st);
362             }
363
364             // --------------------------------------------------------------------
365
366             if (likely(do_udp_errors)) {
367                 st = rrdset_find("ipv4.udperrors");
368                 if (unlikely(!st)) {
369                     st = rrdset_create("ipv4", "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s",
370                                        2701, update_every, RRDSET_TYPE_LINE);
371                     st->isdetail = 1;
372
373                     rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
374                     rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
375                     rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
376                     rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
377                     rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
378                 } else
379                     rrdset_next(st);
380
381                 rrddim_set(st, "InErrors", udpstat.udps_hdrops + udpstat.udps_badlen);
382                 rrddim_set(st, "NoPorts", udpstat.udps_noport);
383                 rrddim_set(st, "RcvbufErrors", udpstat.udps_fullsock);
384                 rrddim_set(st, "InCsumErrors", udpstat.udps_badsum + udpstat.udps_nosum);
385                 rrddim_set(st, "IgnoredMulti", udpstat.udps_filtermcast);
386                 rrdset_done(st);
387             }
388         }
389     }
390
391     return 0;
392 }
393
394 int getsysctl(const char *name, void *ptr, size_t len)
395 {
396     size_t nlen = len;
397
398     if (unlikely(sysctlbyname(name, ptr, &nlen, NULL, 0) == -1)) {
399         error("MACOS: sysctl(%s...) failed: %s", name, strerror(errno));
400         return 1;
401     }
402     if (unlikely(nlen != len)) {
403         error("MACOS: sysctl(%s...) expected %lu, got %lu", name, (unsigned long)len, (unsigned long)nlen);
404         return 1;
405     }
406     return 0;
407 }