3 #ifdef INTERNAL_PLUGIN_NFACCT
6 #include <libmnl/libmnl.h>
8 static inline size_t mnl_buffer_size() {
9 long s = MNL_SOCKET_BUFFER_SIZE;
10 if(s <= 0) return 8192;
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
18 #ifdef HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
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
24 #include <linux/netfilter/nfnetlink_conntrack.h>
30 struct mnl_socket *mnl;
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];
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];
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",
71 [CTA_STATS_EXP_NEW] = "new",
72 [CTA_STATS_EXP_CREATE] = "created",
73 [CTA_STATS_EXP_DELETE] = "deleted",
79 static int nfstat_init(int update_every) {
80 nfstat_root.update_every = update_every;
82 nfstat_root.buf_size = mnl_buffer_size();
83 nfstat_root.buf = mallocz(nfstat_root.buf_size);
85 nfstat_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
86 if(!nfstat_root.mnl) {
87 error("NFSTAT: mnl_socket_open() failed");
91 nfstat_root.seq = (unsigned int)now_realtime_sec() - 1;
93 if(mnl_socket_bind(nfstat_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
94 error("NFSTAT: mnl_socket_bind() failed");
97 nfstat_root.portid = mnl_socket_get_portid(nfstat_root.mnl);
102 static void nfstat_cleanup() {
103 if(nfstat_root.mnl) {
104 mnl_socket_close(nfstat_root.mnl);
105 nfstat_root.mnl = NULL;
108 freez(nfstat_root.buf);
109 nfstat_root.buf = NULL;
110 nfstat_root.buf_size = 0;
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;
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;
122 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
123 nfh->nfgen_family = family;
124 nfh->version = NFNETLINK_V0;
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);
134 if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0)
137 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
138 error("NFSTAT: mnl_attr_validate() failed");
146 static int nfstat_callback(const struct nlmsghdr *nlh, void *data) {
149 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
151 mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, nfstat_root.tb);
153 // printf("cpu=%-4u\t", ntohs(nfg->res_id));
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]));
168 static int nfstat_collect_conntrack() {
169 // zero all metrics - we will sum the metrics of all CPUs later
171 for (i = 0; i < CTA_STATS_MAX+1; i++)
172 nfstat_root.metrics[i] = 0;
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);
178 if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
179 error("NFSTAT: mnl_socket_sendto() failed");
185 while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
189 , nfstat_root.nlh->nlmsg_seq
197 // verify we run without issues
199 error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
206 static int nfexp_stats_attr_cb(const struct nlattr *attr, void *data)
208 const struct nlattr **tb = data;
209 int type = mnl_attr_get_type(attr);
211 if (mnl_attr_type_valid(attr, CTA_STATS_EXP_MAX) < 0)
214 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
215 error("NFSTAT EXP: mnl_attr_validate() failed");
223 static int nfstat_callback_exp(const struct nlmsghdr *nlh, void *data) {
226 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
228 mnl_attr_parse(nlh, sizeof(*nfg), nfexp_stats_attr_cb, nfstat_root.tb_exp);
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]));
240 static int nfstat_collect_conntrack_expectations() {
241 // zero all metrics - we will sum the metrics of all CPUs later
243 for (i = 0; i < CTA_STATS_EXP_MAX+1; i++)
244 nfstat_root.metrics_exp[i] = 0;
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);
250 if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
251 error("NFSTAT: mnl_socket_sendto() failed");
257 while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
261 , nfstat_root.nlh->nlmsg_seq
263 , nfstat_callback_exp
269 // verify we run without issues
271 error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
278 static int nfstat_collect() {
281 if(nfstat_collect_conntrack())
284 if(nfstat_collect_conntrack_expectations())
290 static void nfstat_send_metrics() {
293 static RRDSET *st_new = NULL;
294 static RRDDIM *rd_new = NULL, *rd_ignore = NULL, *rd_invalid = NULL;
297 st_new = rrdset_create_localhost(
298 RRD_TYPE_NET_STAT_NETFILTER
299 , RRD_TYPE_NET_STAT_CONNTRACK "_new"
301 , RRD_TYPE_NET_STAT_CONNTRACK
303 , "Connection Tracker New Connections"
306 , nfstat_root.update_every
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);
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]);
324 // ----------------------------------------------------------------
327 static RRDSET *st_changes = NULL;
328 static RRDDIM *rd_inserted = NULL, *rd_deleted = NULL, *rd_delete_list = NULL;
331 st_changes = rrdset_create_localhost(
332 RRD_TYPE_NET_STAT_NETFILTER
333 , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
335 , RRD_TYPE_NET_STAT_CONNTRACK
337 , "Connection Tracker Changes"
340 , nfstat_root.update_every
343 rrdset_flag_set(st_changes, RRDSET_FLAG_DETAIL);
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);
350 rrdset_next(st_changes);
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]);
356 rrdset_done(st_changes);
359 // ----------------------------------------------------------------
362 static RRDSET *st_search = NULL;
363 static RRDDIM *rd_searched = NULL, *rd_restarted = NULL, *rd_found = NULL;
366 st_search = rrdset_create_localhost(
367 RRD_TYPE_NET_STAT_NETFILTER
368 , RRD_TYPE_NET_STAT_CONNTRACK "_search"
370 , RRD_TYPE_NET_STAT_CONNTRACK
372 , "Connection Tracker Searches"
375 , nfstat_root.update_every
378 rrdset_flag_set(st_search, RRDSET_FLAG_DETAIL);
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);
385 rrdset_next(st_search);
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]);
391 rrdset_done(st_search);
394 // ----------------------------------------------------------------
397 static RRDSET *st_errors = NULL;
398 static RRDDIM *rd_error = NULL, *rd_insert_failed = NULL, *rd_drop = NULL, *rd_early_drop = NULL;
401 st_errors = rrdset_create_localhost(
402 RRD_TYPE_NET_STAT_NETFILTER
403 , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
405 , RRD_TYPE_NET_STAT_CONNTRACK
407 , "Connection Tracker Errors"
410 , nfstat_root.update_every
413 rrdset_flag_set(st_errors, RRDSET_FLAG_DETAIL);
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);
421 rrdset_next(st_errors);
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]);
428 rrdset_done(st_errors);
431 // ----------------------------------------------------------------
434 static RRDSET *st_expect = NULL;
435 static RRDDIM *rd_new = NULL, *rd_created = NULL, *rd_deleted = NULL;
438 st_expect = rrdset_create_localhost(
439 RRD_TYPE_NET_STAT_NETFILTER
440 , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
442 , RRD_TYPE_NET_STAT_CONNTRACK
444 , "Connection Tracker Expectations"
447 , nfstat_root.update_every
450 rrdset_flag_set(st_expect, RRDSET_FLAG_DETAIL);
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);
457 rrdset_next(st_expect);
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]);
463 rrdset_done(st_expect);
468 #endif // HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
471 // ----------------------------------------------------------------------------
472 // DO_NFACCT - collect netfilter accounting statistics via netlink
474 #ifdef HAVE_LIBNETFILTER_ACCT
477 #include <libnetfilter_acct/libnetfilter_acct.h>
491 struct nfacct_data *next;
498 struct mnl_socket *mnl;
499 struct nlmsghdr *nlh;
502 struct nfacct *nfacct_buffer;
503 struct nfacct_data *nfacct_metrics;
512 .nfacct_buffer = NULL,
513 .nfacct_metrics = NULL
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)))
523 d = callocz(1, sizeof(struct nfacct_data));
524 d->name = strdupz(name);
528 d->next = nfacct_root.nfacct_metrics;
529 nfacct_root.nfacct_metrics = d;
532 d->next = last->next;
539 static int nfacct_init(int update_every) {
540 nfacct_root.update_every = update_every;
542 nfacct_root.buf_size = mnl_buffer_size();
543 nfacct_root.buf = mallocz(nfacct_root.buf_size);
545 nfacct_root.nfacct_buffer = nfacct_alloc();
546 if(!nfacct_root.nfacct_buffer) {
547 error("nfacct.plugin: nfacct_alloc() failed.");
551 nfacct_root.seq = (unsigned int)now_realtime_sec() - 1;
553 nfacct_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
554 if(!nfacct_root.mnl) {
555 error("nfacct.plugin: mnl_socket_open() failed");
559 if(mnl_socket_bind(nfacct_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
560 error("nfacct.plugin: mnl_socket_bind() failed");
563 nfacct_root.portid = mnl_socket_get_portid(nfacct_root.mnl);
568 static void nfacct_cleanup() {
569 if(nfacct_root.mnl) {
570 mnl_socket_close(nfacct_root.mnl);
571 nfacct_root.mnl = NULL;
574 if(nfacct_root.nfacct_buffer) {
575 nfacct_free(nfacct_root.nfacct_buffer);
576 nfacct_root.nfacct_buffer = NULL;
579 freez(nfacct_root.buf);
580 nfacct_root.buf = NULL;
581 nfacct_root.buf_size = 0;
583 // FIXME: cleanup the metrics linked list
586 static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
589 if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) {
590 error("NFACCT: nfacct_nlmsg_parse_payload() failed.");
594 const char *name = nfacct_attr_get_str(nfacct_root.nfacct_buffer, NFACCT_ATTR_NAME);
595 uint32_t hash = simple_hash(name);
597 struct nfacct_data *d = nfacct_data_get(name, hash);
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);
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)
612 // prepare the request
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");
621 if(mnl_socket_sendto(nfacct_root.mnl, nfacct_root.nlh, nfacct_root.nlh->nlmsg_len) < 0) {
622 error("NFACCT: mnl_socket_sendto() failed");
628 while((ret = mnl_socket_recvfrom(nfacct_root.mnl, nfacct_root.buf, nfacct_root.buf_size)) > 0) {
640 // verify we run without issues
642 error("NFACCT: error communicating with kernel. This plugin can only work when netdata runs as root.");
649 static void nfacct_send_metrics() {
650 static RRDSET *st_bytes = NULL, *st_packets = NULL;
652 if(!nfacct_root.nfacct_metrics) return;
653 struct nfacct_data *d;
656 st_packets = rrdset_create_localhost(
662 , "Netfilter Accounting Packets"
665 , nfacct_root.update_every
666 , RRDSET_TYPE_STACKED
669 else rrdset_next(st_packets);
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(
679 , nfacct_root.update_every
680 , RRD_ALGORITHM_INCREMENTAL
683 rrddim_set_by_pointer(
686 , (collected_number)d->pkts
691 rrdset_done(st_packets);
693 // ----------------------------------------------------------------
695 st_bytes = rrdset_find_bytype_localhost("netfilter", "nfacct_bytes");
697 st_bytes = rrdset_create_localhost(
703 , "Netfilter Accounting Bandwidth"
706 , nfacct_root.update_every
707 , RRDSET_TYPE_STACKED
710 else rrdset_next(st_bytes);
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(
720 , 1000 * nfacct_root.update_every
721 , RRD_ALGORITHM_INCREMENTAL
724 rrddim_set_by_pointer(
727 , (collected_number)d->bytes
732 rrdset_done(st_bytes);
735 #endif // HAVE_LIBNETFILTER_ACCT
736 #endif // HAVE_LIBMNL
738 // ----------------------------------------------------------------------------
740 void *nfacct_main(void *ptr) {
741 struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
743 info("NETFILTER thread created with task id %d", gettid());
745 if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
746 error("NETFILTER: Cannot set pthread cancel type to DEFERRED.");
748 if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
749 error("NETFILTER: Cannot set pthread cancel state to ENABLE.");
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;
757 int nfacct = !nfacct_init(update_every);
761 int nfstat = !nfstat_init(update_every);
764 // ------------------------------------------------------------------------
766 usec_t step = update_every * USEC_PER_SEC;
770 heartbeat_dt_usec(&hb);
771 heartbeat_next(&hb, step);
773 if(unlikely(netdata_exit)) break;
777 nfacct = !nfacct_collect();
780 nfacct_send_metrics();
786 nfstat = !nfstat_collect();
789 nfstat_send_metrics();
794 info("NETFILTER thread exiting");
804 static_thread->enabled = 0;
809 #endif // INTERNAL_PLUGIN_NFACCT