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