3 #ifdef INTERNAL_PLUGIN_NFACCT
6 #include <libmnl/libmnl.h>
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
13 #ifdef HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
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
19 #include <linux/netfilter/nfnetlink_conntrack.h>
25 struct mnl_socket *mnl;
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];
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];
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",
66 [CTA_STATS_EXP_NEW] = "new",
67 [CTA_STATS_EXP_CREATE] = "created",
68 [CTA_STATS_EXP_DELETE] = "deleted",
74 static int nfstat_init(int update_every) {
75 nfstat_root.update_every = update_every;
77 nfstat_root.buf_size = (size_t)MNL_SOCKET_BUFFER_SIZE;
78 nfstat_root.buf = mallocz(nfstat_root.buf_size);
80 nfstat_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
81 if(!nfstat_root.mnl) {
82 error("NFSTAT: mnl_socket_open() failed");
86 nfstat_root.seq = (unsigned int)now_realtime_sec() - 1;
88 if(mnl_socket_bind(nfstat_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
89 error("NFSTAT: mnl_socket_bind() failed");
92 nfstat_root.portid = mnl_socket_get_portid(nfstat_root.mnl);
97 static void nfstat_cleanup() {
99 mnl_socket_close(nfstat_root.mnl);
100 nfstat_root.mnl = NULL;
103 freez(nfstat_root.buf);
104 nfstat_root.buf = NULL;
105 nfstat_root.buf_size = 0;
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;
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;
117 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
118 nfh->nfgen_family = family;
119 nfh->version = NFNETLINK_V0;
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);
129 if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0)
132 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
133 error("NFSTAT: mnl_attr_validate() failed");
141 static int nfstat_callback(const struct nlmsghdr *nlh, void *data) {
144 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
146 mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, nfstat_root.tb);
148 // printf("cpu=%-4u\t", ntohs(nfg->res_id));
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]));
163 static int nfstat_collect_conntrack() {
164 // zero all metrics - we will sum the metrics of all CPUs later
166 for (i = 0; i < CTA_STATS_MAX+1; i++)
167 nfstat_root.metrics[i] = 0;
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);
173 if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
174 error("NFSTAT: mnl_socket_sendto() failed");
180 while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
184 , nfstat_root.nlh->nlmsg_seq
192 // verify we run without issues
194 error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
201 static int nfexp_stats_attr_cb(const struct nlattr *attr, void *data)
203 const struct nlattr **tb = data;
204 int type = mnl_attr_get_type(attr);
206 if (mnl_attr_type_valid(attr, CTA_STATS_EXP_MAX) < 0)
209 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
210 error("NFSTAT EXP: mnl_attr_validate() failed");
218 static int nfstat_callback_exp(const struct nlmsghdr *nlh, void *data) {
221 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
223 mnl_attr_parse(nlh, sizeof(*nfg), nfexp_stats_attr_cb, nfstat_root.tb_exp);
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]));
235 static int nfstat_collect_conntrack_expectations() {
236 // zero all metrics - we will sum the metrics of all CPUs later
238 for (i = 0; i < CTA_STATS_EXP_MAX+1; i++)
239 nfstat_root.metrics_exp[i] = 0;
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);
245 if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
246 error("NFSTAT: mnl_socket_sendto() failed");
252 while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
256 , nfstat_root.nlh->nlmsg_seq
258 , nfstat_callback_exp
264 // verify we run without issues
266 error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
273 static int nfstat_collect() {
276 if(nfstat_collect_conntrack())
279 if(nfstat_collect_conntrack_expectations())
285 static void nfstat_send_metrics() {
288 static RRDSET *st_new = NULL;
289 static RRDDIM *rd_new = NULL, *rd_ignore = NULL, *rd_invalid = NULL;
292 st_new = rrdset_create_localhost(
293 RRD_TYPE_NET_STAT_NETFILTER
294 , RRD_TYPE_NET_STAT_CONNTRACK "_new"
296 , RRD_TYPE_NET_STAT_CONNTRACK
298 , "Connection Tracker New Connections"
301 , nfstat_root.update_every
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);
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]);
319 // ----------------------------------------------------------------
322 static RRDSET *st_changes = NULL;
323 static RRDDIM *rd_inserted = NULL, *rd_deleted = NULL, *rd_delete_list = NULL;
326 st_changes = rrdset_create_localhost(
327 RRD_TYPE_NET_STAT_NETFILTER
328 , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
330 , RRD_TYPE_NET_STAT_CONNTRACK
332 , "Connection Tracker Changes"
335 , nfstat_root.update_every
338 rrdset_flag_set(st_changes, RRDSET_FLAG_DETAIL);
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);
345 rrdset_next(st_changes);
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]);
351 rrdset_done(st_changes);
354 // ----------------------------------------------------------------
357 static RRDSET *st_search = NULL;
358 static RRDDIM *rd_searched = NULL, *rd_restarted = NULL, *rd_found = NULL;
361 st_search = rrdset_create_localhost(
362 RRD_TYPE_NET_STAT_NETFILTER
363 , RRD_TYPE_NET_STAT_CONNTRACK "_search"
365 , RRD_TYPE_NET_STAT_CONNTRACK
367 , "Connection Tracker Searches"
370 , nfstat_root.update_every
373 rrdset_flag_set(st_search, RRDSET_FLAG_DETAIL);
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);
380 rrdset_next(st_search);
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]);
386 rrdset_done(st_search);
389 // ----------------------------------------------------------------
392 static RRDSET *st_errors = NULL;
393 static RRDDIM *rd_error = NULL, *rd_insert_failed = NULL, *rd_drop = NULL, *rd_early_drop = NULL;
396 st_errors = rrdset_create_localhost(
397 RRD_TYPE_NET_STAT_NETFILTER
398 , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
400 , RRD_TYPE_NET_STAT_CONNTRACK
402 , "Connection Tracker Errors"
405 , nfstat_root.update_every
408 rrdset_flag_set(st_errors, RRDSET_FLAG_DETAIL);
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);
416 rrdset_next(st_errors);
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]);
423 rrdset_done(st_errors);
426 // ----------------------------------------------------------------
429 static RRDSET *st_expect = NULL;
430 static RRDDIM *rd_new = NULL, *rd_created = NULL, *rd_deleted = NULL;
433 st_expect = rrdset_create_localhost(
434 RRD_TYPE_NET_STAT_NETFILTER
435 , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
437 , RRD_TYPE_NET_STAT_CONNTRACK
439 , "Connection Tracker Expectations"
442 , nfstat_root.update_every
445 rrdset_flag_set(st_expect, RRDSET_FLAG_DETAIL);
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);
452 rrdset_next(st_expect);
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]);
458 rrdset_done(st_expect);
463 #endif // HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
466 // ----------------------------------------------------------------------------
467 // DO_NFACCT - collect netfilter accounting statistics via netlink
469 #ifdef HAVE_LIBNETFILTER_ACCT
472 #include <libnetfilter_acct/libnetfilter_acct.h>
486 struct nfacct_data *next;
493 struct mnl_socket *mnl;
494 struct nlmsghdr *nlh;
497 struct nfacct *nfacct_buffer;
498 struct nfacct_data *nfacct_metrics;
507 .nfacct_buffer = NULL,
508 .nfacct_metrics = NULL
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)))
518 d = callocz(1, sizeof(struct nfacct_data));
519 d->name = strdupz(name);
523 d->next = nfacct_root.nfacct_metrics;
524 nfacct_root.nfacct_metrics = d;
527 d->next = last->next;
534 static int nfacct_init(int update_every) {
535 nfacct_root.update_every = update_every;
537 nfacct_root.buf_size = (size_t)MNL_SOCKET_BUFFER_SIZE;
538 nfacct_root.buf = mallocz(nfacct_root.buf_size);
540 nfacct_root.nfacct_buffer = nfacct_alloc();
541 if(!nfacct_root.nfacct_buffer) {
542 error("nfacct.plugin: nfacct_alloc() failed.");
546 nfacct_root.seq = (unsigned int)now_realtime_sec() - 1;
548 nfacct_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
549 if(!nfacct_root.mnl) {
550 error("nfacct.plugin: mnl_socket_open() failed");
554 if(mnl_socket_bind(nfacct_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
555 error("nfacct.plugin: mnl_socket_bind() failed");
558 nfacct_root.portid = mnl_socket_get_portid(nfacct_root.mnl);
563 static void nfacct_cleanup() {
564 if(nfacct_root.mnl) {
565 mnl_socket_close(nfacct_root.mnl);
566 nfacct_root.mnl = NULL;
569 if(nfacct_root.nfacct_buffer) {
570 nfacct_free(nfacct_root.nfacct_buffer);
571 nfacct_root.nfacct_buffer = NULL;
574 freez(nfacct_root.buf);
575 nfacct_root.buf = NULL;
576 nfacct_root.buf_size = 0;
578 // FIXME: cleanup the metrics linked list
581 static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
584 if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) {
585 error("NFACCT: nfacct_nlmsg_parse_payload() failed.");
589 const char *name = nfacct_attr_get_str(nfacct_root.nfacct_buffer, NFACCT_ATTR_NAME);
590 uint32_t hash = simple_hash(name);
592 struct nfacct_data *d = nfacct_data_get(name, hash);
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);
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)
607 // prepare the request
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");
616 if(mnl_socket_sendto(nfacct_root.mnl, nfacct_root.nlh, nfacct_root.nlh->nlmsg_len) < 0) {
617 error("NFACCT: mnl_socket_sendto() failed");
623 while((ret = mnl_socket_recvfrom(nfacct_root.mnl, nfacct_root.buf, nfacct_root.buf_size)) > 0) {
635 // verify we run without issues
637 error("NFACCT: error communicating with kernel. This plugin can only work when netdata runs as root.");
644 static void nfacct_send_metrics() {
645 static RRDSET *st_bytes = NULL, *st_packets = NULL;
647 if(!nfacct_root.nfacct_metrics) return;
648 struct nfacct_data *d;
651 st_packets = rrdset_create_localhost(
657 , "Netfilter Accounting Packets"
660 , nfacct_root.update_every
661 , RRDSET_TYPE_STACKED
664 else rrdset_next(st_packets);
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(
674 , nfacct_root.update_every
675 , RRD_ALGORITHM_INCREMENTAL
678 rrddim_set_by_pointer(
681 , (collected_number)d->pkts
686 rrdset_done(st_packets);
688 // ----------------------------------------------------------------
690 st_bytes = rrdset_find_bytype_localhost("netfilter", "nfacct_bytes");
692 st_bytes = rrdset_create_localhost(
698 , "Netfilter Accounting Bandwidth"
701 , nfacct_root.update_every
702 , RRDSET_TYPE_STACKED
705 else rrdset_next(st_bytes);
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(
715 , 1000 * nfacct_root.update_every
716 , RRD_ALGORITHM_INCREMENTAL
719 rrddim_set_by_pointer(
722 , (collected_number)d->bytes
727 rrdset_done(st_bytes);
730 #endif // HAVE_LIBNETFILTER_ACCT
731 #endif // HAVE_LIBMNL
733 // ----------------------------------------------------------------------------
735 void *nfacct_main(void *ptr) {
736 struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
738 info("NETFILTER thread created with task id %d", gettid());
740 if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
741 error("NETFILTER: Cannot set pthread cancel type to DEFERRED.");
743 if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
744 error("NETFILTER: Cannot set pthread cancel state to ENABLE.");
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;
752 int nfacct = !nfacct_init(update_every);
756 int nfstat = !nfstat_init(update_every);
759 // ------------------------------------------------------------------------
761 usec_t step = update_every * USEC_PER_SEC;
765 heartbeat_dt_usec(&hb);
766 heartbeat_next(&hb, step);
768 if(unlikely(netdata_exit)) break;
772 nfacct = !nfacct_collect();
775 nfacct_send_metrics();
781 nfstat = !nfstat_collect();
784 nfstat_send_metrics();
789 info("NETFILTER thread exiting");
799 static_thread->enabled = 0;
804 #endif // INTERNAL_PLUGIN_NFACCT