master
c 876 lines 28 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4
5 #include <linux/netfilter/nfnetlink_conntrack.h>
6 #include <libmnl/libmnl.h>
7 #include <libnetfilter_acct/libnetfilter_acct.h>
8
9 #define PLUGIN_NFACCT_NAME "nfacct.plugin"
10
11 #define NETDATA_CHART_PRIO_NETFILTER_NEW 8701
12 #define NETDATA_CHART_PRIO_NETFILTER_CHANGES 8702
13 #define NETDATA_CHART_PRIO_NETFILTER_EXPECT 8703
14 #define NETDATA_CHART_PRIO_NETFILTER_ERRORS 8705
15 #define NETDATA_CHART_PRIO_NETFILTER_SEARCH 8710
16
17 #define NETDATA_CHART_PRIO_NETFILTER_PACKETS 8906
18 #define NETDATA_CHART_PRIO_NETFILTER_BYTES 8907
19
20 #define NFACCT_RESTART_EVERY_SECONDS 86400 // restart the plugin every this many seconds
21
22 static inline size_t mnl_buffer_size() {
23 long s = MNL_SOCKET_BUFFER_SIZE;
24 if(s <= 0) return 8192;
25 return (size_t)s;
26 }
27
28 // variables
29 static int debug = 0;
30 static int netdata_update_every = 1;
31
32 #define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
33 #define RRD_TYPE_NET_STAT_CONNTRACK "netlink"
34
35 static struct {
36 int update_every;
37 char *buf;
38 size_t buf_size;
39 struct mnl_socket *mnl;
40 struct nlmsghdr *nlh;
41 struct nfgenmsg *nfh;
42 unsigned int seq;
43 uint32_t portid;
44
45 struct nlattr *tb[CTA_STATS_MAX+1];
46 const char *attr2name[CTA_STATS_MAX+1];
47 kernel_uint_t metrics[CTA_STATS_MAX+1];
48
49 struct nlattr *tb_exp[CTA_STATS_EXP_MAX+1];
50 const char *attr2name_exp[CTA_STATS_EXP_MAX+1];
51 kernel_uint_t metrics_exp[CTA_STATS_EXP_MAX+1];
52 } nfstat_root = {
53 .update_every = 1,
54 .buf = NULL,
55 .buf_size = 0,
56 .mnl = NULL,
57 .nlh = NULL,
58 .nfh = NULL,
59 .seq = 0,
60 .portid = 0,
61 .tb = {},
62 .attr2name = {
63 [CTA_STATS_SEARCHED] = "searched",
64 [CTA_STATS_FOUND] = "found",
65 [CTA_STATS_NEW] = "new",
66 [CTA_STATS_INVALID] = "invalid",
67 [CTA_STATS_IGNORE] = "ignore",
68 [CTA_STATS_DELETE] = "delete",
69 [CTA_STATS_DELETE_LIST] = "delete_list",
70 [CTA_STATS_INSERT] = "insert",
71 [CTA_STATS_INSERT_FAILED] = "insert_failed",
72 [CTA_STATS_DROP] = "drop",
73 [CTA_STATS_EARLY_DROP] = "early_drop",
74 [CTA_STATS_ERROR] = "icmp_error",
75 [CTA_STATS_SEARCH_RESTART] = "search_restart",
76 },
77 .metrics = {},
78 .tb_exp = {},
79 .attr2name_exp = {
80 [CTA_STATS_EXP_NEW] = "new",
81 [CTA_STATS_EXP_CREATE] = "created",
82 [CTA_STATS_EXP_DELETE] = "deleted",
83 },
84 .metrics_exp = {}
85 };
86
87
88 static int nfstat_init(int update_every) {
89 nfstat_root.update_every = update_every;
90
91 nfstat_root.buf_size = mnl_buffer_size();
92 nfstat_root.buf = mallocz(nfstat_root.buf_size);
93
94 nfstat_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
95 if(!nfstat_root.mnl) {
96 collector_error("NFSTAT: mnl_socket_open() failed");
97 return 1;
98 }
99
100 nfstat_root.seq = (unsigned int)now_realtime_sec() - 1;
101
102 if(mnl_socket_bind(nfstat_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
103 collector_error("NFSTAT: mnl_socket_bind() failed");
104 return 1;
105 }
106 nfstat_root.portid = mnl_socket_get_portid(nfstat_root.mnl);
107
108 return 0;
109 }
110
111 static struct nlmsghdr * nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type, uint8_t family, uint32_t seq) {
112 struct nlmsghdr *nlh;
113 struct nfgenmsg *nfh;
114
115 nlh = mnl_nlmsg_put_header(buf);
116 nlh->nlmsg_type = (subsys << 8) | type;
117 nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
118 nlh->nlmsg_seq = seq;
119
120 nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
121 nfh->nfgen_family = family;
122 nfh->version = NFNETLINK_V0;
123 nfh->res_id = 0;
124
125 return nlh;
126 }
127
128 static int nfct_stats_attr_cb(const struct nlattr *attr, void *data) {
129 const struct nlattr **tb = data;
130 int type = mnl_attr_get_type(attr);
131
132 if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0)
133 return MNL_CB_OK;
134
135 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
136 collector_error("NFSTAT: mnl_attr_validate() failed");
137 return MNL_CB_ERROR;
138 }
139
140 tb[type] = attr;
141 return MNL_CB_OK;
142 }
143
144 static int nfstat_callback(const struct nlmsghdr *nlh, void *data) {
145 (void)data;
146
147 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
148
149 mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, nfstat_root.tb);
150
151 // printf("cpu=%-4u\t", ntohs(nfg->res_id));
152
153 int i;
154 // add the metrics of this CPU into the metrics
155 for (i = 0; i < CTA_STATS_MAX+1; i++) {
156 if (nfstat_root.tb[i]) {
157 // printf("%s=%u ", nfstat_root.attr2name[i], ntohl(mnl_attr_get_u32(nfstat_root.tb[i])));
158 nfstat_root.metrics[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb[i]));
159 }
160 }
161 // printf("\n");
162
163 return MNL_CB_OK;
164 }
165
166 static int nfstat_collect_conntrack() {
167 // zero all metrics - we will sum the metrics of all CPUs later
168 int i;
169 for (i = 0; i < CTA_STATS_MAX+1; i++)
170 nfstat_root.metrics[i] = 0;
171
172 // prepare the request
173 nfstat_root.nlh = nfct_mnl_nlmsghdr_put(nfstat_root.buf, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS_CPU, AF_UNSPEC, nfstat_root.seq);
174
175 // send the request
176 if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
177 collector_error("NFSTAT: mnl_socket_sendto() failed");
178 return 1;
179 }
180
181 // get the reply
182 ssize_t ret;
183 while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
184 if(mnl_cb_run(
185 nfstat_root.buf
186 , (size_t)ret
187 , nfstat_root.nlh->nlmsg_seq
188 , nfstat_root.portid
189 , nfstat_callback
190 , NULL
191 ) <= MNL_CB_STOP)
192 break;
193 }
194
195 // verify we run without issues
196 if (ret == -1) {
197 collector_error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
198 return 1;
199 }
200
201 return 0;
202 }
203
204 static int nfexp_stats_attr_cb(const struct nlattr *attr, void *data)
205 {
206 const struct nlattr **tb = data;
207 int type = mnl_attr_get_type(attr);
208
209 if (mnl_attr_type_valid(attr, CTA_STATS_EXP_MAX) < 0)
210 return MNL_CB_OK;
211
212 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
213 collector_error("NFSTAT EXP: mnl_attr_validate() failed");
214 return MNL_CB_ERROR;
215 }
216
217 tb[type] = attr;
218 return MNL_CB_OK;
219 }
220
221 static int nfstat_callback_exp(const struct nlmsghdr *nlh, void *data) {
222 (void)data;
223
224 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
225
226 mnl_attr_parse(nlh, sizeof(*nfg), nfexp_stats_attr_cb, nfstat_root.tb_exp);
227
228 int i;
229 for (i = 0; i < CTA_STATS_EXP_MAX+1; i++) {
230 if (nfstat_root.tb_exp[i]) {
231 nfstat_root.metrics_exp[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb_exp[i]));
232 }
233 }
234
235 return MNL_CB_OK;
236 }
237
238 static int nfstat_collect_conntrack_expectations() {
239 // zero all metrics - we will sum the metrics of all CPUs later
240 int i;
241 for (i = 0; i < CTA_STATS_EXP_MAX+1; i++)
242 nfstat_root.metrics_exp[i] = 0;
243
244 // prepare the request
245 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);
246
247 // send the request
248 if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
249 collector_error("NFSTAT: mnl_socket_sendto() failed");
250 return 1;
251 }
252
253 // get the reply
254 ssize_t ret;
255 while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
256 if(mnl_cb_run(
257 nfstat_root.buf
258 , (size_t)ret
259 , nfstat_root.nlh->nlmsg_seq
260 , nfstat_root.portid
261 , nfstat_callback_exp
262 , NULL
263 ) <= MNL_CB_STOP)
264 break;
265 }
266
267 // verify we run without issues
268 if (ret == -1) {
269 collector_error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
270 return 1;
271 }
272
273 return 0;
274 }
275
276 static int nfstat_collect() {
277 nfstat_root.seq++;
278
279 if(nfstat_collect_conntrack())
280 return 1;
281
282 if(nfstat_collect_conntrack_expectations())
283 return 1;
284
285 return 0;
286 }
287
288 static void nfstat_send_metrics() {
289 static int new_chart_generated = 0, changes_chart_generated = 0, search_chart_generated = 0, errors_chart_generated = 0, expect_chart_generated = 0;
290
291 if(!new_chart_generated) {
292 new_chart_generated = 1;
293
294 printf("CHART %s.%s '' 'Connection Tracker New Connections' 'connections/s' %s '' line %d %d %s\n"
295 , RRD_TYPE_NET_STAT_NETFILTER
296 , RRD_TYPE_NET_STAT_CONNTRACK "_new"
297 , RRD_TYPE_NET_STAT_CONNTRACK
298 , NETDATA_CHART_PRIO_NETFILTER_NEW
299 , nfstat_root.update_every
300 , PLUGIN_NFACCT_NAME
301 );
302 printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_NEW]);
303 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_IGNORE]);
304 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_INVALID]);
305 }
306
307 printf(
308 "BEGIN %s.%s\n"
309 , RRD_TYPE_NET_STAT_NETFILTER
310 , RRD_TYPE_NET_STAT_CONNTRACK "_new"
311 );
312 printf(
313 "SET %s = %lld\n"
314 , nfstat_root.attr2name[CTA_STATS_NEW]
315 , (collected_number) nfstat_root.metrics[CTA_STATS_NEW]
316 );
317 printf(
318 "SET %s = %lld\n"
319 , nfstat_root.attr2name[CTA_STATS_IGNORE]
320 , (collected_number) nfstat_root.metrics[CTA_STATS_IGNORE]
321 );
322 printf(
323 "SET %s = %lld\n"
324 , nfstat_root.attr2name[CTA_STATS_INVALID]
325 , (collected_number) nfstat_root.metrics[CTA_STATS_INVALID]
326 );
327 printf("END\n");
328
329 // ----------------------------------------------------------------
330
331 if(!changes_chart_generated) {
332 changes_chart_generated = 1;
333
334 printf("CHART %s.%s '' 'Connection Tracker Changes' 'changes/s' %s '' line %d %d detail %s\n"
335 , RRD_TYPE_NET_STAT_NETFILTER
336 , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
337 , RRD_TYPE_NET_STAT_CONNTRACK
338 , NETDATA_CHART_PRIO_NETFILTER_CHANGES
339 , nfstat_root.update_every
340 , PLUGIN_NFACCT_NAME
341 );
342 printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_INSERT]);
343 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DELETE]);
344 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DELETE_LIST]);
345 }
346
347 printf(
348 "BEGIN %s.%s\n"
349 , RRD_TYPE_NET_STAT_NETFILTER
350 , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
351 );
352 printf(
353 "SET %s = %lld\n"
354 , nfstat_root.attr2name[CTA_STATS_INSERT]
355 , (collected_number) nfstat_root.metrics[CTA_STATS_INSERT]
356 );
357 printf(
358 "SET %s = %lld\n"
359 , nfstat_root.attr2name[CTA_STATS_DELETE]
360 , (collected_number) nfstat_root.metrics[CTA_STATS_DELETE]
361 );
362 printf(
363 "SET %s = %lld\n"
364 , nfstat_root.attr2name[CTA_STATS_DELETE_LIST]
365 , (collected_number) nfstat_root.metrics[CTA_STATS_DELETE_LIST]
366 );
367 printf("END\n");
368
369 // ----------------------------------------------------------------
370
371 if(!search_chart_generated) {
372 search_chart_generated = 1;
373
374 printf("CHART %s.%s '' 'Connection Tracker Searches' 'searches/s' %s '' line %d %d detail %s\n"
375 , RRD_TYPE_NET_STAT_NETFILTER
376 , RRD_TYPE_NET_STAT_CONNTRACK "_search"
377 , RRD_TYPE_NET_STAT_CONNTRACK
378 , NETDATA_CHART_PRIO_NETFILTER_SEARCH
379 , nfstat_root.update_every
380 , PLUGIN_NFACCT_NAME
381 );
382 printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_SEARCHED]);
383 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_SEARCH_RESTART]);
384 printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_FOUND]);
385 }
386
387 printf(
388 "BEGIN %s.%s\n"
389 , RRD_TYPE_NET_STAT_NETFILTER
390 , RRD_TYPE_NET_STAT_CONNTRACK "_search"
391 );
392 printf(
393 "SET %s = %lld\n"
394 , nfstat_root.attr2name[CTA_STATS_SEARCHED]
395 , (collected_number) nfstat_root.metrics[CTA_STATS_SEARCHED]
396 );
397 printf(
398 "SET %s = %lld\n"
399 , nfstat_root.attr2name[CTA_STATS_SEARCH_RESTART]
400 , (collected_number) nfstat_root.metrics[CTA_STATS_SEARCH_RESTART]
401 );
402 printf(
403 "SET %s = %lld\n"
404 , nfstat_root.attr2name[CTA_STATS_FOUND]
405 , (collected_number) nfstat_root.metrics[CTA_STATS_FOUND]
406 );
407 printf("END\n");
408
409 // ----------------------------------------------------------------
410
411 if(!errors_chart_generated) {
412 errors_chart_generated = 1;
413
414 printf("CHART %s.%s '' 'Connection Tracker Errors' 'events/s' %s '' line %d %d detail %s\n"
415 , RRD_TYPE_NET_STAT_NETFILTER
416 , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
417 , RRD_TYPE_NET_STAT_CONNTRACK
418 , NETDATA_CHART_PRIO_NETFILTER_ERRORS
419 , nfstat_root.update_every
420 , PLUGIN_NFACCT_NAME
421 );
422 printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_ERROR]);
423 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_INSERT_FAILED]);
424 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DROP]);
425 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_EARLY_DROP]);
426 }
427
428 printf(
429 "BEGIN %s.%s\n"
430 , RRD_TYPE_NET_STAT_NETFILTER
431 , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
432 );
433 printf(
434 "SET %s = %lld\n"
435 , nfstat_root.attr2name[CTA_STATS_ERROR]
436 , (collected_number) nfstat_root.metrics[CTA_STATS_ERROR]
437 );
438 printf(
439 "SET %s = %lld\n"
440 , nfstat_root.attr2name[CTA_STATS_INSERT_FAILED]
441 , (collected_number) nfstat_root.metrics[CTA_STATS_INSERT_FAILED]
442 );
443 printf(
444 "SET %s = %lld\n"
445 , nfstat_root.attr2name[CTA_STATS_DROP]
446 , (collected_number) nfstat_root.metrics[CTA_STATS_DROP]
447 );
448 printf(
449 "SET %s = %lld\n"
450 , nfstat_root.attr2name[CTA_STATS_EARLY_DROP]
451 , (collected_number) nfstat_root.metrics[CTA_STATS_EARLY_DROP]
452 );
453 printf("END\n");
454
455 // ----------------------------------------------------------------
456
457 if(!expect_chart_generated) {
458 expect_chart_generated = 1;
459
460 printf("CHART %s.%s '' 'Connection Tracker Expectations' 'expectations/s' %s '' line %d %d detail %s\n"
461 , RRD_TYPE_NET_STAT_NETFILTER
462 , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
463 , RRD_TYPE_NET_STAT_CONNTRACK
464 , NETDATA_CHART_PRIO_NETFILTER_EXPECT
465 , nfstat_root.update_every
466 , PLUGIN_NFACCT_NAME
467 );
468 printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_CREATE]);
469 printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_DELETE]);
470 printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_NEW]);
471 }
472
473 printf(
474 "BEGIN %s.%s\n"
475 , RRD_TYPE_NET_STAT_NETFILTER
476 , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
477 );
478 printf(
479 "SET %s = %lld\n"
480 , nfstat_root.attr2name[CTA_STATS_EXP_CREATE]
481 , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_CREATE]
482 );
483 printf(
484 "SET %s = %lld\n"
485 , nfstat_root.attr2name[CTA_STATS_EXP_DELETE]
486 , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_DELETE]
487 );
488 printf(
489 "SET %s = %lld\n"
490 , nfstat_root.attr2name[CTA_STATS_EXP_NEW]
491 , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_NEW]
492 );
493 printf("END\n");
494 }
495
496
497 struct nfacct_data {
498 char *name;
499 uint32_t hash;
500
501 uint64_t pkts;
502 uint64_t bytes;
503
504 int packets_dimension_added;
505 int bytes_dimension_added;
506
507 int updated;
508
509 struct nfacct_data *next;
510 };
511
512 static struct {
513 int update_every;
514 char *buf;
515 size_t buf_size;
516 struct mnl_socket *mnl;
517 struct nlmsghdr *nlh;
518 unsigned int seq;
519 uint32_t portid;
520 struct nfacct *nfacct_buffer;
521 struct nfacct_data *nfacct_metrics;
522 } nfacct_root = {
523 .update_every = 1,
524 .buf = NULL,
525 .buf_size = 0,
526 .mnl = NULL,
527 .nlh = NULL,
528 .seq = 0,
529 .portid = 0,
530 .nfacct_buffer = NULL,
531 .nfacct_metrics = NULL
532 };
533
534 static inline struct nfacct_data *nfacct_data_get(const char *name, uint32_t hash) {
535 struct nfacct_data *d = NULL, *last = NULL;
536 for(d = nfacct_root.nfacct_metrics; d ; last = d, d = d->next) {
537 if(unlikely(d->hash == hash && !strcmp(d->name, name)))
538 return d;
539 }
540
541 d = callocz(1, sizeof(struct nfacct_data));
542 d->name = strdupz(name);
543 d->hash = hash;
544
545 if(!last) {
546 d->next = nfacct_root.nfacct_metrics;
547 nfacct_root.nfacct_metrics = d;
548 }
549 else {
550 d->next = last->next;
551 last->next = d;
552 }
553
554 return d;
555 }
556
557 static int nfacct_init(int update_every) {
558 nfacct_root.update_every = update_every;
559
560 nfacct_root.buf_size = mnl_buffer_size();
561 nfacct_root.buf = mallocz(nfacct_root.buf_size);
562
563 nfacct_root.nfacct_buffer = nfacct_alloc();
564 if(!nfacct_root.nfacct_buffer) {
565 collector_error("nfacct.plugin: nfacct_alloc() failed.");
566 return 0;
567 }
568
569 nfacct_root.seq = (unsigned int)now_realtime_sec() - 1;
570
571 nfacct_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
572 if(!nfacct_root.mnl) {
573 collector_error("nfacct.plugin: mnl_socket_open() failed");
574 return 1;
575 }
576
577 if(mnl_socket_bind(nfacct_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
578 collector_error("nfacct.plugin: mnl_socket_bind() failed");
579 return 1;
580 }
581 nfacct_root.portid = mnl_socket_get_portid(nfacct_root.mnl);
582
583 return 0;
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 collector_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 collector_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 collector_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 collector_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 int bytes_chart_generated = 0, packets_chart_generated = 0;
651
652 if(!nfacct_root.nfacct_metrics) return;
653 struct nfacct_data *d;
654
655 if(!packets_chart_generated) {
656 packets_chart_generated = 1;
657 printf("CHART netfilter.nfacct_packets '' 'Netfilter Accounting Packets' 'packets/s' 'nfacct' '' stacked %d %d %s\n"
658 , NETDATA_CHART_PRIO_NETFILTER_PACKETS
659 , nfacct_root.update_every
660 , PLUGIN_NFACCT_NAME
661 );
662 }
663
664 for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
665 if(likely(d->updated)) {
666 if(unlikely(!d->packets_dimension_added)) {
667 d->packets_dimension_added = 1;
668 printf(
669 "CHART netfilter.nfacct_packets '' 'Netfilter Accounting Packets' 'packets/s' 'nfacct' '' stacked %d %d %s\n",
670 NETDATA_CHART_PRIO_NETFILTER_PACKETS,
671 nfacct_root.update_every,
672 PLUGIN_NFACCT_NAME);
673 printf("DIMENSION %s '' incremental 1 %d\n", d->name, nfacct_root.update_every);
674 }
675 }
676 }
677 printf("BEGIN netfilter.nfacct_packets\n");
678 for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
679 if(likely(d->updated)) {
680 printf("SET %s = %lld\n"
681 , d->name
682 , (collected_number)d->pkts
683 );
684 }
685 }
686 printf("END\n");
687
688 // ----------------------------------------------------------------
689
690 if(!bytes_chart_generated) {
691 bytes_chart_generated = 1;
692 printf("CHART netfilter.nfacct_bytes '' 'Netfilter Accounting Bandwidth' 'kilobytes/s' 'nfacct' '' stacked %d %d %s\n"
693 , NETDATA_CHART_PRIO_NETFILTER_BYTES
694 , nfacct_root.update_every
695 , PLUGIN_NFACCT_NAME
696 );
697 }
698
699 for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
700 if(likely(d->updated)) {
701 if(unlikely(!d->bytes_dimension_added)) {
702 d->bytes_dimension_added = 1;
703 printf(
704 "CHART netfilter.nfacct_bytes '' 'Netfilter Accounting Bandwidth' 'kilobytes/s' 'nfacct' '' stacked %d %d %s\n",
705 NETDATA_CHART_PRIO_NETFILTER_BYTES,
706 nfacct_root.update_every,
707 PLUGIN_NFACCT_NAME);
708 printf("DIMENSION %s '' incremental 1 %d\n", d->name, 1000 * nfacct_root.update_every);
709 }
710 }
711 }
712 printf("BEGIN netfilter.nfacct_bytes\n");
713 for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
714 if(likely(d->updated)) {
715 printf("SET %s = %lld\n"
716 , d->name
717 , (collected_number)d->bytes
718 );
719 }
720 }
721 printf("END\n");
722 }
723
724 static void nfacct_signal_handler(int signo)
725 {
726 exit((signo == SIGPIPE)?1:0);
727 }
728
729 // When Netdata crashes this plugin was becoming zombie,
730 // this function was added to remove it when sigpipe and other signals are received.
731 void nfacct_signals()
732 {
733 int signals[] = { SIGPIPE, SIGINT, SIGTERM, 0};
734 int i;
735 struct sigaction sa;
736 sa.sa_flags = 0;
737 sa.sa_handler = nfacct_signal_handler;
738
739 // ignore all signals while we run in a signal handler
740 sigfillset(&sa.sa_mask);
741
742 for (i = 0; signals[i]; i++) {
743 if(sigaction(signals[i], &sa, NULL) == -1)
744 collector_error("Cannot add the handler to signal %d", signals[i]);
745 }
746 }
747
748 int main(int argc, char **argv) {
749 nd_log_initialize_for_external_plugins("nfacct.plugin");
750 netdata_threads_init_for_external_plugins(0);
751
752 // ------------------------------------------------------------------------
753 // parse command line parameters
754
755 int i, freq = 0;
756 for(i = 1; i < argc ; i++) {
757 if(isdigit(*argv[i]) && !freq) {
758 int n = str2i(argv[i]);
759 if(n > 0 && n < 86400) {
760 freq = n;
761 continue;
762 }
763 }
764 else if(strcmp("version", argv[i]) == 0 || strcmp("-version", argv[i]) == 0 || strcmp("--version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
765 printf("nfacct.plugin %s\n", NETDATA_VERSION);
766 exit(0);
767 }
768 else if(strcmp("debug", argv[i]) == 0) {
769 debug = 1;
770 continue;
771 }
772 else if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
773 fprintf(stderr,
774 "\n"
775 " netdata nfacct.plugin %s\n"
776 " Copyright 2018-2025 Netdata Inc.\n"
777 " Released under GNU General Public License v3 or later.\n"
778 "\n"
779 " This program is a data collector plugin for netdata.\n"
780 "\n"
781 " Available command line options:\n"
782 "\n"
783 " COLLECTION_FREQUENCY data collection frequency in seconds\n"
784 " minimum: %d\n"
785 "\n"
786 " debug enable verbose output\n"
787 " default: disabled\n"
788 "\n"
789 " -v\n"
790 " -V\n"
791 " --version print version and exit\n"
792 "\n"
793 " -h\n"
794 " --help print this message and exit\n"
795 "\n"
796 " For more information:\n"
797 " https://github.com/netdata/netdata/tree/master/src/collectors/nfacct.plugin\n"
798 "\n"
799 , NETDATA_VERSION
800 , netdata_update_every
801 );
802 exit(1);
803 }
804
805 collector_error("nfacct.plugin: ignoring parameter '%s'", argv[i]);
806 }
807
808 nfacct_signals();
809
810 errno_clear();
811
812 if(freq >= netdata_update_every)
813 netdata_update_every = freq;
814 else if(freq)
815 collector_error("update frequency %d seconds is too small for NFACCT. Using %d.", freq, netdata_update_every);
816
817 if (debug)
818 fprintf(stderr, "nfacct.plugin: calling nfacct_init()\n");
819 int nfacct = !nfacct_init(netdata_update_every);
820
821 if (debug)
822 fprintf(stderr, "nfacct.plugin: calling nfstat_init()\n");
823 int nfstat = !nfstat_init(netdata_update_every);
824
825 // ------------------------------------------------------------------------
826 // the main loop
827
828 if(debug) fprintf(stderr, "nfacct.plugin: starting data collection\n");
829
830 time_t started_t = now_monotonic_sec();
831
832 size_t iteration;
833
834 heartbeat_t hb;
835 heartbeat_init(&hb, netdata_update_every * USEC_PER_SEC);
836 for(iteration = 0; 1; iteration++) {
837 usec_t dt = heartbeat_next(&hb);
838
839 if(unlikely(exit_initiated_get())) break;
840
841 if(debug && iteration)
842 fprintf(stderr, "nfacct.plugin: iteration %zu, dt %"PRIu64" usec\n"
843 , iteration
844 , dt
845 );
846
847 if(likely(nfacct)) {
848 if(debug) fprintf(stderr, "nfacct.plugin: calling nfacct_collect()\n");
849 nfacct = !nfacct_collect();
850
851 if(likely(nfacct)) {
852 if(debug) fprintf(stderr, "nfacct.plugin: calling nfacct_send_metrics()\n");
853 nfacct_send_metrics();
854 }
855 }
856
857 if(likely(nfstat)) {
858 if(debug) fprintf(stderr, "nfacct.plugin: calling nfstat_collect()\n");
859 nfstat = !nfstat_collect();
860
861 if(likely(nfstat)) {
862 if(debug) fprintf(stderr, "nfacct.plugin: calling nfstat_send_metrics()\n");
863 nfstat_send_metrics();
864 }
865 }
866
867 fflush(stdout);
868
869 if (now_monotonic_sec() - started_t > NFACCT_RESTART_EVERY_SECONDS) {
870 collector_info("NFACCT reached my lifetime expectancy. Exiting to restart.");
871 fprintf(stdout, "EXIT\n");
872 fflush(stdout);
873 exit(0);
874 }
875 }
876 }