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