]> arthur.barton.de Git - netdata.git/blob - src/plugin_nfacct.c
rename chart fields to avoid conflicts with backends; fixes #1962
[netdata.git] / src / plugin_nfacct.c
1 #include "common.h"
2
3 #ifdef INTERNAL_PLUGIN_NFACCT
4
5 #ifdef HAVE_LIBMNL
6 #include <libmnl/libmnl.h>
7
8
9 // ----------------------------------------------------------------------------
10 // DO_NFSTAT - collect netfilter connection tracker statistics via netlink
11 // example: https://github.com/formorer/pkg-conntrack-tools/blob/master/src/conntrack.c
12
13 #ifdef HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
14 #define DO_NFSTAT 1
15
16 #define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
17 #define RRD_TYPE_NET_STAT_CONNTRACK "netlink" // FIXME: should be "conntrack" when merged with the /proc plugin
18
19 #include <linux/netfilter/nfnetlink_conntrack.h>
20
21 static struct {
22     int update_every;
23     char *buf;
24     size_t buf_size;
25     struct mnl_socket *mnl;
26     struct nlmsghdr *nlh;
27     struct nfgenmsg *nfh;
28     unsigned int seq;
29     uint32_t portid;
30
31     struct nlattr *tb[CTA_STATS_MAX+1];
32     const char *attr2name[CTA_STATS_MAX+1];
33     kernel_uint_t metrics[CTA_STATS_MAX+1];
34
35     struct nlattr *tb_exp[CTA_STATS_EXP_MAX+1];
36     const char *attr2name_exp[CTA_STATS_EXP_MAX+1];
37     kernel_uint_t metrics_exp[CTA_STATS_EXP_MAX+1];
38 } nfstat_root = {
39         .update_every = 1,
40         .buf = NULL,
41         .buf_size = 0,
42         .mnl = NULL,
43         .nlh = NULL,
44         .nfh = NULL,
45         .seq = 0,
46         .portid = 0,
47         .tb = {},
48         .attr2name = {
49                 [CTA_STATS_SEARCHED]       = "searched",
50                 [CTA_STATS_FOUND]              = "found",
51                 [CTA_STATS_NEW]                = "new",
52                 [CTA_STATS_INVALID]            = "invalid",
53                 [CTA_STATS_IGNORE]             = "ignore",
54                 [CTA_STATS_DELETE]             = "delete",
55                 [CTA_STATS_DELETE_LIST]    = "delete_list",
56                 [CTA_STATS_INSERT]             = "insert",
57                 [CTA_STATS_INSERT_FAILED]  = "insert_failed",
58                 [CTA_STATS_DROP]               = "drop",
59                 [CTA_STATS_EARLY_DROP]     = "early_drop",
60                 [CTA_STATS_ERROR]              = "icmp_error",
61                 [CTA_STATS_SEARCH_RESTART] = "search_restart",
62         },
63         .metrics = {},
64         .tb_exp = {},
65         .attr2name_exp = {
66                 [CTA_STATS_EXP_NEW]            = "new",
67                 [CTA_STATS_EXP_CREATE]     = "created",
68                 [CTA_STATS_EXP_DELETE]     = "deleted",
69         },
70         .metrics_exp = {}
71 };
72
73
74 static int nfstat_init(int update_every) {
75     nfstat_root.update_every = update_every;
76
77     nfstat_root.buf_size = (size_t)MNL_SOCKET_BUFFER_SIZE;
78     nfstat_root.buf = mallocz(nfstat_root.buf_size);
79
80     nfstat_root.mnl  = mnl_socket_open(NETLINK_NETFILTER);
81     if(!nfstat_root.mnl) {
82         error("NFSTAT: mnl_socket_open() failed");
83         return 1;
84     }
85
86     nfstat_root.seq = (unsigned int)now_realtime_sec() - 1;
87
88     if(mnl_socket_bind(nfstat_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
89         error("NFSTAT: mnl_socket_bind() failed");
90         return 1;
91     }
92     nfstat_root.portid = mnl_socket_get_portid(nfstat_root.mnl);
93
94     return 0;
95 }
96
97 static void nfstat_cleanup() {
98     if(nfstat_root.mnl) {
99         mnl_socket_close(nfstat_root.mnl);
100         nfstat_root.mnl = NULL;
101     }
102
103     freez(nfstat_root.buf);
104     nfstat_root.buf = NULL;
105     nfstat_root.buf_size = 0;
106 }
107
108 static struct nlmsghdr * nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type, uint8_t family, uint32_t seq) {
109     struct nlmsghdr *nlh;
110     struct nfgenmsg *nfh;
111
112     nlh = mnl_nlmsg_put_header(buf);
113     nlh->nlmsg_type = (subsys << 8) | type;
114     nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
115     nlh->nlmsg_seq = seq;
116
117     nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
118     nfh->nfgen_family = family;
119     nfh->version = NFNETLINK_V0;
120     nfh->res_id = 0;
121
122     return nlh;
123 }
124
125 static int nfct_stats_attr_cb(const struct nlattr *attr, void *data) {
126     const struct nlattr **tb = data;
127     int type = mnl_attr_get_type(attr);
128
129     if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0)
130         return MNL_CB_OK;
131
132     if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
133         error("NFSTAT: mnl_attr_validate() failed");
134         return MNL_CB_ERROR;
135     }
136
137     tb[type] = attr;
138     return MNL_CB_OK;
139 }
140
141 static int nfstat_callback(const struct nlmsghdr *nlh, void *data) {
142     (void)data;
143
144     struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
145
146     mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, nfstat_root.tb);
147
148     // printf("cpu=%-4u\t", ntohs(nfg->res_id));
149
150     int i;
151     // add the metrics of this CPU into the metrics
152     for (i = 0; i < CTA_STATS_MAX+1; i++) {
153         if (nfstat_root.tb[i]) {
154             // printf("%s=%u ", nfstat_root.attr2name[i], ntohl(mnl_attr_get_u32(nfstat_root.tb[i])));
155             nfstat_root.metrics[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb[i]));
156         }
157     }
158     // printf("\n");
159
160     return MNL_CB_OK;
161 }
162
163 static int nfstat_collect_conntrack() {
164     // zero all metrics - we will sum the metrics of all CPUs later
165     int i;
166     for (i = 0; i < CTA_STATS_MAX+1; i++)
167         nfstat_root.metrics[i] = 0;
168
169     // prepare the request
170     nfstat_root.nlh = nfct_mnl_nlmsghdr_put(nfstat_root.buf, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS_CPU, AF_UNSPEC, nfstat_root.seq);
171
172     // send the request
173     if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
174         error("NFSTAT: mnl_socket_sendto() failed");
175         return 1;
176     }
177
178     // get the reply
179     ssize_t ret;
180     while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
181         if(mnl_cb_run(
182                 nfstat_root.buf
183                 , (size_t)ret
184                 , nfstat_root.nlh->nlmsg_seq
185                 , nfstat_root.portid
186                 , nfstat_callback
187                 , NULL
188         ) <= MNL_CB_STOP)
189             break;
190     }
191
192     // verify we run without issues
193     if (ret == -1) {
194         error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
195         return 1;
196     }
197
198     return 0;
199 }
200
201 static int nfexp_stats_attr_cb(const struct nlattr *attr, void *data)
202 {
203     const struct nlattr **tb = data;
204     int type = mnl_attr_get_type(attr);
205
206     if (mnl_attr_type_valid(attr, CTA_STATS_EXP_MAX) < 0)
207         return MNL_CB_OK;
208
209     if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
210         error("NFSTAT EXP: mnl_attr_validate() failed");
211         return MNL_CB_ERROR;
212     }
213
214     tb[type] = attr;
215     return MNL_CB_OK;
216 }
217
218 static int nfstat_callback_exp(const struct nlmsghdr *nlh, void *data) {
219     (void)data;
220
221     struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
222
223     mnl_attr_parse(nlh, sizeof(*nfg), nfexp_stats_attr_cb, nfstat_root.tb_exp);
224
225     int i;
226     for (i = 0; i < CTA_STATS_EXP_MAX+1; i++) {
227         if (nfstat_root.tb_exp[i]) {
228             nfstat_root.metrics_exp[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb_exp[i]));
229         }
230     }
231
232     return MNL_CB_OK;
233 }
234
235 static int nfstat_collect_conntrack_expectations() {
236     // zero all metrics - we will sum the metrics of all CPUs later
237     int i;
238     for (i = 0; i < CTA_STATS_EXP_MAX+1; i++)
239         nfstat_root.metrics_exp[i] = 0;
240
241     // prepare the request
242     nfstat_root.nlh = nfct_mnl_nlmsghdr_put(nfstat_root.buf, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET_STATS_CPU, AF_UNSPEC, nfstat_root.seq);
243
244     // send the request
245     if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
246         error("NFSTAT: mnl_socket_sendto() failed");
247         return 1;
248     }
249
250     // get the reply
251     ssize_t ret;
252     while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
253         if(mnl_cb_run(
254                 nfstat_root.buf
255                 , (size_t)ret
256                 , nfstat_root.nlh->nlmsg_seq
257                 , nfstat_root.portid
258                 , nfstat_callback_exp
259                 , NULL
260         ) <= MNL_CB_STOP)
261             break;
262     }
263
264     // verify we run without issues
265     if (ret == -1) {
266         error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
267         return 1;
268     }
269
270     return 0;
271 }
272
273 static int nfstat_collect() {
274     nfstat_root.seq++;
275
276     if(nfstat_collect_conntrack())
277         return 1;
278
279     if(nfstat_collect_conntrack_expectations())
280         return 1;
281
282     return 0;
283 }
284
285 static void nfstat_send_metrics() {
286
287     {
288         static RRDSET *st_new = NULL;
289         static RRDDIM *rd_new = NULL, *rd_ignore = NULL, *rd_invalid = NULL;
290
291         if(!st_new) {
292             st_new = rrdset_create_localhost(
293                     RRD_TYPE_NET_STAT_NETFILTER
294                     , RRD_TYPE_NET_STAT_CONNTRACK "_new"
295                     , NULL
296                     , RRD_TYPE_NET_STAT_CONNTRACK
297                     , NULL
298                     , "Connection Tracker New Connections"
299                     , "connections/s"
300                     , 3001
301                     , nfstat_root.update_every
302                     , RRDSET_TYPE_LINE
303             );
304
305             rd_new     = rrddim_add(st_new, nfstat_root.attr2name[CTA_STATS_NEW], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
306             rd_ignore  = rrddim_add(st_new, nfstat_root.attr2name[CTA_STATS_IGNORE], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
307             rd_invalid = rrddim_add(st_new, nfstat_root.attr2name[CTA_STATS_INVALID], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
308         }
309         else
310             rrdset_next(st_new);
311
312         rrddim_set_by_pointer(st_new, rd_new, (collected_number) nfstat_root.metrics[CTA_STATS_NEW]);
313         rrddim_set_by_pointer(st_new, rd_ignore, (collected_number) nfstat_root.metrics[CTA_STATS_IGNORE]);
314         rrddim_set_by_pointer(st_new, rd_invalid, (collected_number) nfstat_root.metrics[CTA_STATS_INVALID]);
315
316         rrdset_done(st_new);
317     }
318
319     // ----------------------------------------------------------------
320
321     {
322         static RRDSET *st_changes = NULL;
323         static RRDDIM *rd_inserted = NULL, *rd_deleted = NULL, *rd_delete_list = NULL;
324
325         if(!st_changes) {
326             st_changes = rrdset_create_localhost(
327                     RRD_TYPE_NET_STAT_NETFILTER
328                     , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
329                     , NULL
330                     , RRD_TYPE_NET_STAT_CONNTRACK
331                     , NULL
332                     , "Connection Tracker Changes"
333                     , "changes/s"
334                     , 3002
335                     , nfstat_root.update_every
336                     , RRDSET_TYPE_LINE
337             );
338             rrdset_flag_set(st_changes, RRDSET_FLAG_DETAIL);
339
340             rd_inserted = rrddim_add(st_changes, nfstat_root.attr2name[CTA_STATS_INSERT], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
341             rd_deleted = rrddim_add(st_changes, nfstat_root.attr2name[CTA_STATS_DELETE], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
342             rd_delete_list = rrddim_add(st_changes, nfstat_root.attr2name[CTA_STATS_DELETE_LIST], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
343         }
344         else
345             rrdset_next(st_changes);
346
347         rrddim_set_by_pointer(st_changes, rd_inserted, (collected_number) nfstat_root.metrics[CTA_STATS_INSERT]);
348         rrddim_set_by_pointer(st_changes, rd_deleted, (collected_number) nfstat_root.metrics[CTA_STATS_DELETE]);
349         rrddim_set_by_pointer(st_changes, rd_delete_list, (collected_number) nfstat_root.metrics[CTA_STATS_DELETE_LIST]);
350
351         rrdset_done(st_changes);
352     }
353
354     // ----------------------------------------------------------------
355
356     {
357         static RRDSET *st_search = NULL;
358         static RRDDIM *rd_searched = NULL, *rd_restarted = NULL, *rd_found = NULL;
359
360         if(!st_search) {
361             st_search = rrdset_create_localhost(
362                     RRD_TYPE_NET_STAT_NETFILTER
363                     , RRD_TYPE_NET_STAT_CONNTRACK "_search"
364                     , NULL
365                     , RRD_TYPE_NET_STAT_CONNTRACK
366                     , NULL
367                     , "Connection Tracker Searches"
368                     , "searches/s"
369                     , 3010
370                     , nfstat_root.update_every
371                     , RRDSET_TYPE_LINE
372             );
373             rrdset_flag_set(st_search, RRDSET_FLAG_DETAIL);
374
375             rd_searched = rrddim_add(st_search, nfstat_root.attr2name[CTA_STATS_SEARCHED], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
376             rd_restarted = rrddim_add(st_search, nfstat_root.attr2name[CTA_STATS_SEARCH_RESTART], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
377             rd_found = rrddim_add(st_search, nfstat_root.attr2name[CTA_STATS_FOUND], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
378         }
379         else
380             rrdset_next(st_search);
381
382         rrddim_set_by_pointer(st_search, rd_searched, (collected_number) nfstat_root.metrics[CTA_STATS_SEARCHED]);
383         rrddim_set_by_pointer(st_search, rd_restarted, (collected_number) nfstat_root.metrics[CTA_STATS_SEARCH_RESTART]);
384         rrddim_set_by_pointer(st_search, rd_found, (collected_number) nfstat_root.metrics[CTA_STATS_FOUND]);
385
386         rrdset_done(st_search);
387     }
388
389     // ----------------------------------------------------------------
390
391     {
392         static RRDSET *st_errors = NULL;
393         static RRDDIM *rd_error = NULL, *rd_insert_failed = NULL, *rd_drop = NULL, *rd_early_drop = NULL;
394
395         if(!st_errors) {
396             st_errors = rrdset_create_localhost(
397                     RRD_TYPE_NET_STAT_NETFILTER
398                     , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
399                     , NULL
400                     , RRD_TYPE_NET_STAT_CONNTRACK
401                     , NULL
402                     , "Connection Tracker Errors"
403                     , "events/s"
404                     , 3005
405                     , nfstat_root.update_every
406                     , RRDSET_TYPE_LINE
407             );
408             rrdset_flag_set(st_errors, RRDSET_FLAG_DETAIL);
409
410             rd_error = rrddim_add(st_errors, nfstat_root.attr2name[CTA_STATS_ERROR], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
411             rd_insert_failed = rrddim_add(st_errors, nfstat_root.attr2name[CTA_STATS_INSERT_FAILED], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
412             rd_drop = rrddim_add(st_errors, nfstat_root.attr2name[CTA_STATS_DROP], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
413             rd_early_drop = rrddim_add(st_errors, nfstat_root.attr2name[CTA_STATS_EARLY_DROP], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
414         }
415         else
416             rrdset_next(st_errors);
417
418         rrddim_set_by_pointer(st_errors, rd_error, (collected_number) nfstat_root.metrics[CTA_STATS_ERROR]);
419         rrddim_set_by_pointer(st_errors, rd_insert_failed, (collected_number) nfstat_root.metrics[CTA_STATS_INSERT_FAILED]);
420         rrddim_set_by_pointer(st_errors, rd_drop, (collected_number) nfstat_root.metrics[CTA_STATS_DROP]);
421         rrddim_set_by_pointer(st_errors, rd_early_drop, (collected_number) nfstat_root.metrics[CTA_STATS_EARLY_DROP]);
422
423         rrdset_done(st_errors);
424     }
425
426     // ----------------------------------------------------------------
427
428     {
429         static RRDSET *st_expect = NULL;
430         static RRDDIM *rd_new = NULL, *rd_created = NULL, *rd_deleted = NULL;
431
432         if(!st_expect) {
433             st_expect = rrdset_create_localhost(
434                     RRD_TYPE_NET_STAT_NETFILTER
435                     , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
436                     , NULL
437                     , RRD_TYPE_NET_STAT_CONNTRACK
438                     , NULL
439                     , "Connection Tracker Expectations"
440                     , "expectations/s"
441                     , 3003
442                     , nfstat_root.update_every
443                     , RRDSET_TYPE_LINE
444             );
445             rrdset_flag_set(st_expect, RRDSET_FLAG_DETAIL);
446
447             rd_created = rrddim_add(st_expect, nfstat_root.attr2name_exp[CTA_STATS_EXP_CREATE], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
448             rd_deleted = rrddim_add(st_expect, nfstat_root.attr2name_exp[CTA_STATS_EXP_DELETE], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
449             rd_new = rrddim_add(st_expect, nfstat_root.attr2name_exp[CTA_STATS_EXP_NEW], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
450         }
451         else
452             rrdset_next(st_expect);
453
454         rrddim_set_by_pointer(st_expect, rd_created, (collected_number) nfstat_root.metrics_exp[CTA_STATS_EXP_CREATE]);
455         rrddim_set_by_pointer(st_expect, rd_deleted, (collected_number) nfstat_root.metrics_exp[CTA_STATS_EXP_DELETE]);
456         rrddim_set_by_pointer(st_expect, rd_new, (collected_number) nfstat_root.metrics_exp[CTA_STATS_EXP_NEW]);
457
458         rrdset_done(st_expect);
459     }
460
461 }
462
463 #endif // HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
464
465
466 // ----------------------------------------------------------------------------
467 // DO_NFACCT - collect netfilter accounting statistics via netlink
468
469 #ifdef HAVE_LIBNETFILTER_ACCT
470 #define DO_NFACCT 1
471
472 #include <libnetfilter_acct/libnetfilter_acct.h>
473
474 struct nfacct_data {
475     char *name;
476     uint32_t hash;
477
478     uint64_t pkts;
479     uint64_t bytes;
480
481     RRDDIM *rd_bytes;
482     RRDDIM *rd_packets;
483
484     int updated;
485
486     struct nfacct_data *next;
487 };
488
489 static struct {
490     int update_every;
491     char *buf;
492     size_t buf_size;
493     struct mnl_socket *mnl;
494     struct nlmsghdr *nlh;
495     unsigned int seq;
496     uint32_t portid;
497     struct nfacct *nfacct_buffer;
498     struct nfacct_data *nfacct_metrics;
499 } nfacct_root = {
500         .update_every = 1,
501         .buf = NULL,
502         .buf_size = 0,
503         .mnl = NULL,
504         .nlh = NULL,
505         .seq = 0,
506         .portid = 0,
507         .nfacct_buffer = NULL,
508         .nfacct_metrics = NULL
509 };
510
511 static inline struct nfacct_data *nfacct_data_get(const char *name, uint32_t hash) {
512     struct nfacct_data *d = NULL, *last = NULL;
513     for(d = nfacct_root.nfacct_metrics; d ; last = d, d = d->next) {
514         if(unlikely(d->hash == hash && !strcmp(d->name, name)))
515             return d;
516     }
517
518     d = callocz(1, sizeof(struct nfacct_data));
519     d->name = strdupz(name);
520     d->hash = hash;
521
522     if(!last) {
523         d->next = nfacct_root.nfacct_metrics;
524         nfacct_root.nfacct_metrics = d;
525     }
526     else {
527         d->next = last->next;
528         last->next = d;
529     }
530
531     return d;
532 }
533
534 static int nfacct_init(int update_every) {
535     nfacct_root.update_every = update_every;
536
537     nfacct_root.buf_size = (size_t)MNL_SOCKET_BUFFER_SIZE;
538     nfacct_root.buf = mallocz(nfacct_root.buf_size);
539
540     nfacct_root.nfacct_buffer = nfacct_alloc();
541     if(!nfacct_root.nfacct_buffer) {
542         error("nfacct.plugin: nfacct_alloc() failed.");
543         return 0;
544     }
545
546     nfacct_root.seq = (unsigned int)now_realtime_sec() - 1;
547
548     nfacct_root.mnl  = mnl_socket_open(NETLINK_NETFILTER);
549     if(!nfacct_root.mnl) {
550         error("nfacct.plugin: mnl_socket_open() failed");
551         return 1;
552     }
553
554     if(mnl_socket_bind(nfacct_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
555         error("nfacct.plugin: mnl_socket_bind() failed");
556         return 1;
557     }
558     nfacct_root.portid = mnl_socket_get_portid(nfacct_root.mnl);
559
560     return 0;
561 }
562
563 static void nfacct_cleanup() {
564     if(nfacct_root.mnl) {
565         mnl_socket_close(nfacct_root.mnl);
566         nfacct_root.mnl = NULL;
567     }
568
569     if(nfacct_root.nfacct_buffer) {
570         nfacct_free(nfacct_root.nfacct_buffer);
571         nfacct_root.nfacct_buffer = NULL;
572     }
573
574     freez(nfacct_root.buf);
575     nfacct_root.buf = NULL;
576     nfacct_root.buf_size = 0;
577
578     // FIXME: cleanup the metrics linked list
579 }
580
581 static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
582     (void)data;
583
584     if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) {
585         error("NFACCT: nfacct_nlmsg_parse_payload() failed.");
586         return MNL_CB_OK;
587     }
588
589     const char *name = nfacct_attr_get_str(nfacct_root.nfacct_buffer, NFACCT_ATTR_NAME);
590     uint32_t hash = simple_hash(name);
591
592     struct nfacct_data *d = nfacct_data_get(name, hash);
593
594     d->pkts  = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_PKTS);
595     d->bytes = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_BYTES);
596     d->updated = 1;
597
598     return MNL_CB_OK;
599 }
600
601 static int nfacct_collect() {
602     // mark all old metrics as not-updated
603     struct nfacct_data *d;
604     for(d = nfacct_root.nfacct_metrics; d ; d = d->next)
605         d->updated = 0;
606
607     // prepare the request
608     nfacct_root.seq++;
609     nfacct_root.nlh = nfacct_nlmsg_build_hdr(nfacct_root.buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, (uint32_t)nfacct_root.seq);
610     if(!nfacct_root.nlh) {
611         error("NFACCT: nfacct_nlmsg_build_hdr() failed");
612         return 1;
613     }
614
615     // send the request
616     if(mnl_socket_sendto(nfacct_root.mnl, nfacct_root.nlh, nfacct_root.nlh->nlmsg_len) < 0) {
617         error("NFACCT: mnl_socket_sendto() failed");
618         return 1;
619     }
620
621     // get the reply
622     ssize_t ret;
623     while((ret = mnl_socket_recvfrom(nfacct_root.mnl, nfacct_root.buf, nfacct_root.buf_size)) > 0) {
624         if(mnl_cb_run(
625                 nfacct_root.buf
626                 , (size_t)ret
627                 , nfacct_root.seq
628                 , nfacct_root.portid
629                 , nfacct_callback
630                 , NULL
631         ) <= 0)
632             break;
633     }
634
635     // verify we run without issues
636     if (ret == -1) {
637         error("NFACCT: error communicating with kernel. This plugin can only work when netdata runs as root.");
638         return 1;
639     }
640
641     return 0;
642 }
643
644 static void nfacct_send_metrics() {
645     static RRDSET *st_bytes = NULL, *st_packets = NULL;
646
647     if(!nfacct_root.nfacct_metrics) return;
648     struct nfacct_data *d;
649
650     if(!st_packets) {
651         st_packets = rrdset_create_localhost(
652                 "netfilter"
653                 , "nfacct_packets"
654                 , NULL
655                 , "nfacct"
656                 , NULL
657                 , "Netfilter Accounting Packets"
658                 , "packets/s"
659                 , 3206
660                 , nfacct_root.update_every
661                 , RRDSET_TYPE_STACKED
662         );
663     }
664     else rrdset_next(st_packets);
665
666     for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
667         if(likely(d->updated)) {
668             if(unlikely(!d->rd_packets))
669                 d->rd_packets = rrddim_add(
670                         st_packets
671                         , d->name
672                         , NULL
673                         , 1
674                         , nfacct_root.update_every
675                         , RRD_ALGORITHM_INCREMENTAL
676                 );
677
678             rrddim_set_by_pointer(
679                     st_packets
680                     , d->rd_packets
681                     , (collected_number)d->pkts
682             );
683         }
684     }
685
686     rrdset_done(st_packets);
687
688     // ----------------------------------------------------------------
689
690     st_bytes = rrdset_find_bytype_localhost("netfilter", "nfacct_bytes");
691     if(!st_bytes) {
692         st_bytes = rrdset_create_localhost(
693                 "netfilter"
694                 , "nfacct_bytes"
695                 , NULL
696                 , "nfacct"
697                 , NULL
698                 , "Netfilter Accounting Bandwidth"
699                 , "kilobytes/s"
700                 , 3207
701                 , nfacct_root.update_every
702                 , RRDSET_TYPE_STACKED
703         );
704     }
705     else rrdset_next(st_bytes);
706
707     for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
708         if(likely(d->updated)) {
709             if(unlikely(!d->rd_bytes))
710                 d->rd_bytes = rrddim_add(
711                         st_bytes
712                         , d->name
713                         , NULL
714                         , 1
715                         , 1000 * nfacct_root.update_every
716                         , RRD_ALGORITHM_INCREMENTAL
717                 );
718
719             rrddim_set_by_pointer(
720                     st_bytes
721                     , d->rd_bytes
722                     , (collected_number)d->bytes
723             );
724         }
725     }
726
727     rrdset_done(st_bytes);
728 }
729
730 #endif // HAVE_LIBNETFILTER_ACCT
731 #endif // HAVE_LIBMNL
732
733 // ----------------------------------------------------------------------------
734
735 void *nfacct_main(void *ptr) {
736     struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
737
738     info("NETFILTER thread created with task id %d", gettid());
739
740     if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
741         error("NETFILTER: Cannot set pthread cancel type to DEFERRED.");
742
743     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
744         error("NETFILTER: Cannot set pthread cancel state to ENABLE.");
745
746
747     int update_every = (int)config_get_number("plugin:netfilter", "update every", localhost->rrd_update_every);
748     if(update_every < localhost->rrd_update_every)
749         update_every = localhost->rrd_update_every;
750
751 #ifdef DO_NFACCT
752     int nfacct = !nfacct_init(update_every);
753 #endif
754
755 #ifdef DO_NFSTAT
756     int nfstat = !nfstat_init(update_every);
757 #endif
758
759     // ------------------------------------------------------------------------
760
761     usec_t step = update_every * USEC_PER_SEC;
762     heartbeat_t hb;
763     heartbeat_init(&hb);
764     for(;;) {
765         heartbeat_dt_usec(&hb);
766         heartbeat_next(&hb, step);
767
768         if(unlikely(netdata_exit)) break;
769
770 #ifdef DO_NFACCT
771         if(likely(nfacct)) {
772             nfacct = !nfacct_collect();
773
774             if(likely(nfacct))
775                 nfacct_send_metrics();
776         }
777 #endif
778
779 #ifdef DO_NFSTAT
780         if(likely(nfstat)) {
781             nfstat = !nfstat_collect();
782
783             if(likely(nfstat))
784                 nfstat_send_metrics();
785         }
786 #endif
787     }
788
789     info("NETFILTER thread exiting");
790
791 #ifdef DO_NFACCT
792     nfacct_cleanup();
793 #endif
794
795 #ifdef DO_NFSTAT
796     nfstat_cleanup();
797 #endif
798
799     static_thread->enabled = 0;
800     pthread_exit(NULL);
801     return NULL;
802 }
803
804 #endif // INTERNAL_PLUGIN_NFACCT