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