summaryrefslogtreecommitdiffstats
path: root/collectors/nfacct.plugin
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--collectors/nfacct.plugin/Makefile.am8
l---------collectors/nfacct.plugin/README.md1
-rw-r--r--collectors/nfacct.plugin/integrations/netfilter.md132
-rw-r--r--collectors/nfacct.plugin/metadata.yaml133
-rw-r--r--collectors/nfacct.plugin/plugin_nfacct.c879
5 files changed, 1153 insertions, 0 deletions
diff --git a/collectors/nfacct.plugin/Makefile.am b/collectors/nfacct.plugin/Makefile.am
new file mode 100644
index 00000000..161784b8
--- /dev/null
+++ b/collectors/nfacct.plugin/Makefile.am
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+ README.md \
+ $(NULL)
diff --git a/collectors/nfacct.plugin/README.md b/collectors/nfacct.plugin/README.md
new file mode 120000
index 00000000..ea320d13
--- /dev/null
+++ b/collectors/nfacct.plugin/README.md
@@ -0,0 +1 @@
+integrations/netfilter.md \ No newline at end of file
diff --git a/collectors/nfacct.plugin/integrations/netfilter.md b/collectors/nfacct.plugin/integrations/netfilter.md
new file mode 100644
index 00000000..831b6fb5
--- /dev/null
+++ b/collectors/nfacct.plugin/integrations/netfilter.md
@@ -0,0 +1,132 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/collectors/nfacct.plugin/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/collectors/nfacct.plugin/metadata.yaml"
+sidebar_label: "Netfilter"
+learn_status: "Published"
+learn_rel_path: "Data Collection/Linux Systems/Firewall"
+most_popular: False
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# Netfilter
+
+
+<img src="https://netdata.cloud/img/netfilter.png" width="150"/>
+
+
+Plugin: nfacct.plugin
+Module: nfacct.plugin
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+Monitor Netfilter metrics for optimal packet filtering and manipulation. Keep tabs on packet counts, dropped packets, and error rates to secure network operations.
+
+Netdata uses libmnl (https://www.netfilter.org/projects/libmnl/index.html) to collect information.
+
+This collector is supported on all platforms.
+
+This collector supports collecting metrics from multiple instances of this integration, including remote instances.
+
+This plugin needs setuid.
+
+### Default Behavior
+
+#### Auto-Detection
+
+This plugin uses socket to connect with netfilter to collect data
+
+#### Limits
+
+The default configuration for this integration does not impose any limits on data collection.
+
+#### Performance Impact
+
+The default configuration for this integration is not expected to impose a significant performance impact on the system.
+
+
+## Metrics
+
+Metrics grouped by *scope*.
+
+The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.
+
+
+
+### Per Netfilter instance
+
+
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| netfilter.netlink_new | new, ignore, invalid | connections/s |
+| netfilter.netlink_changes | insert, delete, delete_list | changes/s |
+| netfilter.netlink_search | searched, search_restart, found | searches/s |
+| netfilter.netlink_errors | icmp_error, insert_failed, drop, early_drop | events/s |
+| netfilter.netlink_expect | created, deleted, new | expectations/s |
+| netfilter.nfacct_packets | a dimension per nfacct object | packets/s |
+| netfilter.nfacct_bytes | a dimension per nfacct object | kilobytes/s |
+
+
+
+## Alerts
+
+There are no alerts configured by default for this integration.
+
+
+## Setup
+
+### Prerequisites
+
+#### Install required packages
+
+Install `libmnl-dev` and `libnetfilter-acct-dev` using the package manager of your system.
+
+
+
+### Configuration
+
+#### File
+
+The configuration file name for this integration is `netdata.conf`.
+Configuration for this specific integration is located in the `[plugin:nfacct]` section within that file.
+
+The file format is a modified INI syntax. The general structure is:
+
+```ini
+[section1]
+ option1 = some value
+ option2 = some other value
+
+[section2]
+ option3 = some third value
+```
+You can edit the configuration file using the `edit-config` script from the
+Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/configure/nodes.md#the-netdata-config-directory).
+
+```bash
+cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
+sudo ./edit-config netdata.conf
+```
+#### Options
+
+
+
+<details><summary>Config options</summary>
+
+| Name | Description | Default | Required |
+|:----|:-----------|:-------|:--------:|
+| update every | Data collection frequency. | 1 | no |
+| command options | Additinal parameters for collector | | no |
+
+</details>
+
+#### Examples
+There are no configuration examples.
+
+
diff --git a/collectors/nfacct.plugin/metadata.yaml b/collectors/nfacct.plugin/metadata.yaml
new file mode 100644
index 00000000..943471a3
--- /dev/null
+++ b/collectors/nfacct.plugin/metadata.yaml
@@ -0,0 +1,133 @@
+plugin_name: nfacct.plugin
+modules:
+ - meta:
+ plugin_name: nfacct.plugin
+ module_name: nfacct.plugin
+ monitored_instance:
+ name: Netfilter
+ link: 'https://www.netfilter.org/'
+ categories:
+ - data-collection.linux-systems.firewall-metrics
+ icon_filename: 'netfilter.png'
+ related_resources:
+ integrations:
+ list: []
+ info_provided_to_referring_integrations:
+ description: ''
+ keywords: []
+ most_popular: false
+ overview:
+ data_collection:
+ metrics_description: 'Monitor Netfilter metrics for optimal packet filtering and manipulation. Keep tabs on packet counts, dropped packets, and error rates to secure network operations.'
+ method_description: 'Netdata uses libmnl (https://www.netfilter.org/projects/libmnl/index.html) to collect information.'
+ supported_platforms:
+ include: []
+ exclude: []
+ multi_instance: true
+ additional_permissions:
+ description: 'This plugin needs setuid.'
+ default_behavior:
+ auto_detection:
+ description: 'This plugin uses socket to connect with netfilter to collect data'
+ limits:
+ description: ''
+ performance_impact:
+ description: ''
+ setup:
+ prerequisites:
+ list:
+ - title: Install required packages
+ description: |
+ Install `libmnl-dev` and `libnetfilter-acct-dev` using the package manager of your system.
+ configuration:
+ file:
+ name: 'netdata.conf'
+ section_name: '[plugin:nfacct]'
+ description: 'This is netdata main configuration file'
+ options:
+ description: ''
+ folding:
+ title: 'Config options'
+ enabled: true
+ list:
+ - name: update every
+ description: Data collection frequency.
+ default_value: 1
+ required: false
+ - name: command options
+ description: Additinal parameters for collector
+ default_value: ""
+ required: false
+ examples:
+ folding:
+ enabled: true
+ title: ''
+ list: []
+ troubleshooting:
+ problems:
+ list: []
+ alerts: []
+ metrics:
+ folding:
+ title: Metrics
+ enabled: false
+ description: ""
+ availability: []
+ scopes:
+ - name: global
+ description: ""
+ labels: []
+ metrics:
+ - name: netfilter.netlink_new
+ description: Connection Tracker New Connections
+ unit: "connections/s"
+ chart_type: line
+ dimensions:
+ - name: new
+ - name: ignore
+ - name: invalid
+ - name: netfilter.netlink_changes
+ description: Connection Tracker Changes
+ unit: "changes/s"
+ chart_type: line
+ dimensions:
+ - name: insert
+ - name: delete
+ - name: delete_list
+ - name: netfilter.netlink_search
+ description: Connection Tracker Searches
+ unit: "searches/s"
+ chart_type: line
+ dimensions:
+ - name: searched
+ - name: search_restart
+ - name: found
+ - name: netfilter.netlink_errors
+ description: Connection Tracker Errors
+ unit: "events/s"
+ chart_type: line
+ dimensions:
+ - name: icmp_error
+ - name: insert_failed
+ - name: drop
+ - name: early_drop
+ - name: netfilter.netlink_expect
+ description: Connection Tracker Expectations
+ unit: "expectations/s"
+ chart_type: line
+ dimensions:
+ - name: created
+ - name: deleted
+ - name: new
+ - name: netfilter.nfacct_packets
+ description: Netfilter Accounting Packets
+ unit: "packets/s"
+ chart_type: line
+ dimensions:
+ - name: a dimension per nfacct object
+ - name: netfilter.nfacct_bytes
+ description: Netfilter Accounting Bandwidth
+ unit: "kilobytes/s"
+ chart_type: line
+ dimensions:
+ - name: a dimension per nfacct object
diff --git a/collectors/nfacct.plugin/plugin_nfacct.c b/collectors/nfacct.plugin/plugin_nfacct.c
new file mode 100644
index 00000000..2863cd7e
--- /dev/null
+++ b/collectors/nfacct.plugin/plugin_nfacct.c
@@ -0,0 +1,879 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "libnetdata/libnetdata.h"
+#include "libnetdata/required_dummies.h"
+
+#include <linux/netfilter/nfnetlink_conntrack.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_acct/libnetfilter_acct.h>
+
+#define PLUGIN_NFACCT_NAME "nfacct.plugin"
+
+#define NETDATA_CHART_PRIO_NETFILTER_NEW 8701
+#define NETDATA_CHART_PRIO_NETFILTER_CHANGES 8702
+#define NETDATA_CHART_PRIO_NETFILTER_EXPECT 8703
+#define NETDATA_CHART_PRIO_NETFILTER_ERRORS 8705
+#define NETDATA_CHART_PRIO_NETFILTER_SEARCH 8710
+
+#define NETDATA_CHART_PRIO_NETFILTER_PACKETS 8906
+#define NETDATA_CHART_PRIO_NETFILTER_BYTES 8907
+
+#define NFACCT_RESTART_EVERY_SECONDS 86400 // restart the plugin every this many seconds
+
+static inline size_t mnl_buffer_size() {
+ long s = MNL_SOCKET_BUFFER_SIZE;
+ if(s <= 0) return 8192;
+ return (size_t)s;
+}
+
+// variables
+static int debug = 0;
+static int netdata_update_every = 1;
+
+#define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
+#define RRD_TYPE_NET_STAT_CONNTRACK "netlink"
+
+static struct {
+ int update_every;
+ char *buf;
+ size_t buf_size;
+ struct mnl_socket *mnl;
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+ unsigned int seq;
+ uint32_t portid;
+
+ struct nlattr *tb[CTA_STATS_MAX+1];
+ const char *attr2name[CTA_STATS_MAX+1];
+ kernel_uint_t metrics[CTA_STATS_MAX+1];
+
+ struct nlattr *tb_exp[CTA_STATS_EXP_MAX+1];
+ const char *attr2name_exp[CTA_STATS_EXP_MAX+1];
+ kernel_uint_t metrics_exp[CTA_STATS_EXP_MAX+1];
+} nfstat_root = {
+ .update_every = 1,
+ .buf = NULL,
+ .buf_size = 0,
+ .mnl = NULL,
+ .nlh = NULL,
+ .nfh = NULL,
+ .seq = 0,
+ .portid = 0,
+ .tb = {},
+ .attr2name = {
+ [CTA_STATS_SEARCHED] = "searched",
+ [CTA_STATS_FOUND] = "found",
+ [CTA_STATS_NEW] = "new",
+ [CTA_STATS_INVALID] = "invalid",
+ [CTA_STATS_IGNORE] = "ignore",
+ [CTA_STATS_DELETE] = "delete",
+ [CTA_STATS_DELETE_LIST] = "delete_list",
+ [CTA_STATS_INSERT] = "insert",
+ [CTA_STATS_INSERT_FAILED] = "insert_failed",
+ [CTA_STATS_DROP] = "drop",
+ [CTA_STATS_EARLY_DROP] = "early_drop",
+ [CTA_STATS_ERROR] = "icmp_error",
+ [CTA_STATS_SEARCH_RESTART] = "search_restart",
+ },
+ .metrics = {},
+ .tb_exp = {},
+ .attr2name_exp = {
+ [CTA_STATS_EXP_NEW] = "new",
+ [CTA_STATS_EXP_CREATE] = "created",
+ [CTA_STATS_EXP_DELETE] = "deleted",
+ },
+ .metrics_exp = {}
+};
+
+
+static int nfstat_init(int update_every) {
+ nfstat_root.update_every = update_every;
+
+ nfstat_root.buf_size = mnl_buffer_size();
+ nfstat_root.buf = mallocz(nfstat_root.buf_size);
+
+ nfstat_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
+ if(!nfstat_root.mnl) {
+ collector_error("NFSTAT: mnl_socket_open() failed");
+ return 1;
+ }
+
+ nfstat_root.seq = (unsigned int)now_realtime_sec() - 1;
+
+ if(mnl_socket_bind(nfstat_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ collector_error("NFSTAT: mnl_socket_bind() failed");
+ return 1;
+ }
+ nfstat_root.portid = mnl_socket_get_portid(nfstat_root.mnl);
+
+ return 0;
+}
+
+static struct nlmsghdr * nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type, uint8_t family, uint32_t seq) {
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (subsys << 8) | type;
+ nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+ nlh->nlmsg_seq = seq;
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = family;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = 0;
+
+ return nlh;
+}
+
+static int nfct_stats_attr_cb(const struct nlattr *attr, void *data) {
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0)
+ return MNL_CB_OK;
+
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ collector_error("NFSTAT: mnl_attr_validate() failed");
+ return MNL_CB_ERROR;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nfstat_callback(const struct nlmsghdr *nlh, void *data) {
+ (void)data;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+
+ mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, nfstat_root.tb);
+
+ // printf("cpu=%-4u\t", ntohs(nfg->res_id));
+
+ int i;
+ // add the metrics of this CPU into the metrics
+ for (i = 0; i < CTA_STATS_MAX+1; i++) {
+ if (nfstat_root.tb[i]) {
+ // printf("%s=%u ", nfstat_root.attr2name[i], ntohl(mnl_attr_get_u32(nfstat_root.tb[i])));
+ nfstat_root.metrics[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb[i]));
+ }
+ }
+ // printf("\n");
+
+ return MNL_CB_OK;
+}
+
+static int nfstat_collect_conntrack() {
+ // zero all metrics - we will sum the metrics of all CPUs later
+ int i;
+ for (i = 0; i < CTA_STATS_MAX+1; i++)
+ nfstat_root.metrics[i] = 0;
+
+ // prepare the request
+ nfstat_root.nlh = nfct_mnl_nlmsghdr_put(nfstat_root.buf, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS_CPU, AF_UNSPEC, nfstat_root.seq);
+
+ // send the request
+ if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
+ collector_error("NFSTAT: mnl_socket_sendto() failed");
+ return 1;
+ }
+
+ // get the reply
+ ssize_t ret;
+ while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
+ if(mnl_cb_run(
+ nfstat_root.buf
+ , (size_t)ret
+ , nfstat_root.nlh->nlmsg_seq
+ , nfstat_root.portid
+ , nfstat_callback
+ , NULL
+ ) <= MNL_CB_STOP)
+ break;
+ }
+
+ // verify we run without issues
+ if (ret == -1) {
+ collector_error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int nfexp_stats_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTA_STATS_EXP_MAX) < 0)
+ return MNL_CB_OK;
+
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ collector_error("NFSTAT EXP: mnl_attr_validate() failed");
+ return MNL_CB_ERROR;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nfstat_callback_exp(const struct nlmsghdr *nlh, void *data) {
+ (void)data;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+
+ mnl_attr_parse(nlh, sizeof(*nfg), nfexp_stats_attr_cb, nfstat_root.tb_exp);
+
+ int i;
+ for (i = 0; i < CTA_STATS_EXP_MAX+1; i++) {
+ if (nfstat_root.tb_exp[i]) {
+ nfstat_root.metrics_exp[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb_exp[i]));
+ }
+ }
+
+ return MNL_CB_OK;
+}
+
+static int nfstat_collect_conntrack_expectations() {
+ // zero all metrics - we will sum the metrics of all CPUs later
+ int i;
+ for (i = 0; i < CTA_STATS_EXP_MAX+1; i++)
+ nfstat_root.metrics_exp[i] = 0;
+
+ // prepare the request
+ 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);
+
+ // send the request
+ if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
+ collector_error("NFSTAT: mnl_socket_sendto() failed");
+ return 1;
+ }
+
+ // get the reply
+ ssize_t ret;
+ while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
+ if(mnl_cb_run(
+ nfstat_root.buf
+ , (size_t)ret
+ , nfstat_root.nlh->nlmsg_seq
+ , nfstat_root.portid
+ , nfstat_callback_exp
+ , NULL
+ ) <= MNL_CB_STOP)
+ break;
+ }
+
+ // verify we run without issues
+ if (ret == -1) {
+ collector_error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int nfstat_collect() {
+ nfstat_root.seq++;
+
+ if(nfstat_collect_conntrack())
+ return 1;
+
+ if(nfstat_collect_conntrack_expectations())
+ return 1;
+
+ return 0;
+}
+
+static void nfstat_send_metrics() {
+ static int new_chart_generated = 0, changes_chart_generated = 0, search_chart_generated = 0, errors_chart_generated = 0, expect_chart_generated = 0;
+
+ if(!new_chart_generated) {
+ new_chart_generated = 1;
+
+ printf("CHART %s.%s '' 'Connection Tracker New Connections' 'connections/s' %s '' line %d %d %s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_new"
+ , RRD_TYPE_NET_STAT_CONNTRACK
+ , NETDATA_CHART_PRIO_NETFILTER_NEW
+ , nfstat_root.update_every
+ , PLUGIN_NFACCT_NAME
+ );
+ printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_NEW]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_IGNORE]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_INVALID]);
+ }
+
+ printf(
+ "BEGIN %s.%s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_new"
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_NEW]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_NEW]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_IGNORE]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_IGNORE]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_INVALID]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_INVALID]
+ );
+ printf("END\n");
+
+ // ----------------------------------------------------------------
+
+ if(!changes_chart_generated) {
+ changes_chart_generated = 1;
+
+ printf("CHART %s.%s '' 'Connection Tracker Changes' 'changes/s' %s '' line %d %d detail %s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
+ , RRD_TYPE_NET_STAT_CONNTRACK
+ , NETDATA_CHART_PRIO_NETFILTER_CHANGES
+ , nfstat_root.update_every
+ , PLUGIN_NFACCT_NAME
+ );
+ printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_INSERT]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DELETE]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DELETE_LIST]);
+ }
+
+ printf(
+ "BEGIN %s.%s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_INSERT]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_INSERT]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_DELETE]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_DELETE]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_DELETE_LIST]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_DELETE_LIST]
+ );
+ printf("END\n");
+
+ // ----------------------------------------------------------------
+
+ if(!search_chart_generated) {
+ search_chart_generated = 1;
+
+ printf("CHART %s.%s '' 'Connection Tracker Searches' 'searches/s' %s '' line %d %d detail %s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_search"
+ , RRD_TYPE_NET_STAT_CONNTRACK
+ , NETDATA_CHART_PRIO_NETFILTER_SEARCH
+ , nfstat_root.update_every
+ , PLUGIN_NFACCT_NAME
+ );
+ printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_SEARCHED]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_SEARCH_RESTART]);
+ printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_FOUND]);
+ }
+
+ printf(
+ "BEGIN %s.%s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_search"
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_SEARCHED]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_SEARCHED]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_SEARCH_RESTART]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_SEARCH_RESTART]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_FOUND]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_FOUND]
+ );
+ printf("END\n");
+
+ // ----------------------------------------------------------------
+
+ if(!errors_chart_generated) {
+ errors_chart_generated = 1;
+
+ printf("CHART %s.%s '' 'Connection Tracker Errors' 'events/s' %s '' line %d %d detail %s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
+ , RRD_TYPE_NET_STAT_CONNTRACK
+ , NETDATA_CHART_PRIO_NETFILTER_ERRORS
+ , nfstat_root.update_every
+ , PLUGIN_NFACCT_NAME
+ );
+ printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_ERROR]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_INSERT_FAILED]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DROP]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_EARLY_DROP]);
+ }
+
+ printf(
+ "BEGIN %s.%s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_ERROR]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_ERROR]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_INSERT_FAILED]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_INSERT_FAILED]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_DROP]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_DROP]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_EARLY_DROP]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_EARLY_DROP]
+ );
+ printf("END\n");
+
+ // ----------------------------------------------------------------
+
+ if(!expect_chart_generated) {
+ expect_chart_generated = 1;
+
+ printf("CHART %s.%s '' 'Connection Tracker Expectations' 'expectations/s' %s '' line %d %d detail %s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
+ , RRD_TYPE_NET_STAT_CONNTRACK
+ , NETDATA_CHART_PRIO_NETFILTER_EXPECT
+ , nfstat_root.update_every
+ , PLUGIN_NFACCT_NAME
+ );
+ printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_CREATE]);
+ printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_DELETE]);
+ printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_NEW]);
+ }
+
+ printf(
+ "BEGIN %s.%s\n"
+ , RRD_TYPE_NET_STAT_NETFILTER
+ , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_EXP_CREATE]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_CREATE]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_EXP_DELETE]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_DELETE]
+ );
+ printf(
+ "SET %s = %lld\n"
+ , nfstat_root.attr2name[CTA_STATS_EXP_NEW]
+ , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_NEW]
+ );
+ printf("END\n");
+}
+
+
+struct nfacct_data {
+ char *name;
+ uint32_t hash;
+
+ uint64_t pkts;
+ uint64_t bytes;
+
+ int packets_dimension_added;
+ int bytes_dimension_added;
+
+ int updated;
+
+ struct nfacct_data *next;
+};
+
+static struct {
+ int update_every;
+ char *buf;
+ size_t buf_size;
+ struct mnl_socket *mnl;
+ struct nlmsghdr *nlh;
+ unsigned int seq;
+ uint32_t portid;
+ struct nfacct *nfacct_buffer;
+ struct nfacct_data *nfacct_metrics;
+} nfacct_root = {
+ .update_every = 1,
+ .buf = NULL,
+ .buf_size = 0,
+ .mnl = NULL,
+ .nlh = NULL,
+ .seq = 0,
+ .portid = 0,
+ .nfacct_buffer = NULL,
+ .nfacct_metrics = NULL
+};
+
+static inline struct nfacct_data *nfacct_data_get(const char *name, uint32_t hash) {
+ struct nfacct_data *d = NULL, *last = NULL;
+ for(d = nfacct_root.nfacct_metrics; d ; last = d, d = d->next) {
+ if(unlikely(d->hash == hash && !strcmp(d->name, name)))
+ return d;
+ }
+
+ d = callocz(1, sizeof(struct nfacct_data));
+ d->name = strdupz(name);
+ d->hash = hash;
+
+ if(!last) {
+ d->next = nfacct_root.nfacct_metrics;
+ nfacct_root.nfacct_metrics = d;
+ }
+ else {
+ d->next = last->next;
+ last->next = d;
+ }
+
+ return d;
+}
+
+static int nfacct_init(int update_every) {
+ nfacct_root.update_every = update_every;
+
+ nfacct_root.buf_size = mnl_buffer_size();
+ nfacct_root.buf = mallocz(nfacct_root.buf_size);
+
+ nfacct_root.nfacct_buffer = nfacct_alloc();
+ if(!nfacct_root.nfacct_buffer) {
+ collector_error("nfacct.plugin: nfacct_alloc() failed.");
+ return 0;
+ }
+
+ nfacct_root.seq = (unsigned int)now_realtime_sec() - 1;
+
+ nfacct_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
+ if(!nfacct_root.mnl) {
+ collector_error("nfacct.plugin: mnl_socket_open() failed");
+ return 1;
+ }
+
+ if(mnl_socket_bind(nfacct_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ collector_error("nfacct.plugin: mnl_socket_bind() failed");
+ return 1;
+ }
+ nfacct_root.portid = mnl_socket_get_portid(nfacct_root.mnl);
+
+ return 0;
+}
+
+static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
+ (void)data;
+
+ if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) {
+ collector_error("NFACCT: nfacct_nlmsg_parse_payload() failed.");
+ return MNL_CB_OK;
+ }
+
+ const char *name = nfacct_attr_get_str(nfacct_root.nfacct_buffer, NFACCT_ATTR_NAME);
+ uint32_t hash = simple_hash(name);
+
+ struct nfacct_data *d = nfacct_data_get(name, hash);
+
+ d->pkts = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_PKTS);
+ d->bytes = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_BYTES);
+ d->updated = 1;
+
+ return MNL_CB_OK;
+}
+
+static int nfacct_collect() {
+ // mark all old metrics as not-updated
+ struct nfacct_data *d;
+ for(d = nfacct_root.nfacct_metrics; d ; d = d->next)
+ d->updated = 0;
+
+ // prepare the request
+ nfacct_root.seq++;
+ nfacct_root.nlh = nfacct_nlmsg_build_hdr(nfacct_root.buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, (uint32_t)nfacct_root.seq);
+ if(!nfacct_root.nlh) {
+ collector_error("NFACCT: nfacct_nlmsg_build_hdr() failed");
+ return 1;
+ }
+
+ // send the request
+ if(mnl_socket_sendto(nfacct_root.mnl, nfacct_root.nlh, nfacct_root.nlh->nlmsg_len) < 0) {
+ collector_error("NFACCT: mnl_socket_sendto() failed");
+ return 1;
+ }
+
+ // get the reply
+ ssize_t ret;
+ while((ret = mnl_socket_recvfrom(nfacct_root.mnl, nfacct_root.buf, nfacct_root.buf_size)) > 0) {
+ if(mnl_cb_run(
+ nfacct_root.buf
+ , (size_t)ret
+ , nfacct_root.seq
+ , nfacct_root.portid
+ , nfacct_callback
+ , NULL
+ ) <= 0)
+ break;
+ }
+
+ // verify we run without issues
+ if (ret == -1) {
+ collector_error("NFACCT: error communicating with kernel. This plugin can only work when netdata runs as root.");
+ return 1;
+ }
+
+ return 0;
+}
+
+static void nfacct_send_metrics() {
+ static int bytes_chart_generated = 0, packets_chart_generated = 0;
+
+ if(!nfacct_root.nfacct_metrics) return;
+ struct nfacct_data *d;
+
+ if(!packets_chart_generated) {
+ packets_chart_generated = 1;
+ printf("CHART netfilter.nfacct_packets '' 'Netfilter Accounting Packets' 'packets/s' 'nfacct' '' stacked %d %d %s\n"
+ , NETDATA_CHART_PRIO_NETFILTER_PACKETS
+ , nfacct_root.update_every
+ , PLUGIN_NFACCT_NAME
+ );
+ }
+
+ for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
+ if(likely(d->updated)) {
+ if(unlikely(!d->packets_dimension_added)) {
+ d->packets_dimension_added = 1;
+ printf(
+ "CHART netfilter.nfacct_packets '' 'Netfilter Accounting Packets' 'packets/s' 'nfacct' '' stacked %d %d %s\n",
+ NETDATA_CHART_PRIO_NETFILTER_PACKETS,
+ nfacct_root.update_every,
+ PLUGIN_NFACCT_NAME);
+ printf("DIMENSION %s '' incremental 1 %d\n", d->name, nfacct_root.update_every);
+ }
+ }
+ }
+ printf("BEGIN netfilter.nfacct_packets\n");
+ for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
+ if(likely(d->updated)) {
+ printf("SET %s = %lld\n"
+ , d->name
+ , (collected_number)d->pkts
+ );
+ }
+ }
+ printf("END\n");
+
+ // ----------------------------------------------------------------
+
+ if(!bytes_chart_generated) {
+ bytes_chart_generated = 1;
+ printf("CHART netfilter.nfacct_bytes '' 'Netfilter Accounting Bandwidth' 'kilobytes/s' 'nfacct' '' stacked %d %d %s\n"
+ , NETDATA_CHART_PRIO_NETFILTER_BYTES
+ , nfacct_root.update_every
+ , PLUGIN_NFACCT_NAME
+ );
+ }
+
+ for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
+ if(likely(d->updated)) {
+ if(unlikely(!d->bytes_dimension_added)) {
+ d->bytes_dimension_added = 1;
+ printf(
+ "CHART netfilter.nfacct_bytes '' 'Netfilter Accounting Bandwidth' 'kilobytes/s' 'nfacct' '' stacked %d %d %s\n",
+ NETDATA_CHART_PRIO_NETFILTER_BYTES,
+ nfacct_root.update_every,
+ PLUGIN_NFACCT_NAME);
+ printf("DIMENSION %s '' incremental 1 %d\n", d->name, 1000 * nfacct_root.update_every);
+ }
+ }
+ }
+ printf("BEGIN netfilter.nfacct_bytes\n");
+ for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
+ if(likely(d->updated)) {
+ printf("SET %s = %lld\n"
+ , d->name
+ , (collected_number)d->bytes
+ );
+ }
+ }
+ printf("END\n");
+}
+
+static void nfacct_signal_handler(int signo)
+{
+ exit((signo == SIGPIPE)?1:0);
+}
+
+// When Netdata crashes this plugin was becoming zombie,
+// this function was added to remove it when sigpipe and other signals are received.
+void nfacct_signals()
+{
+ int signals[] = { SIGPIPE, SIGINT, SIGTERM, 0};
+ int i;
+ struct sigaction sa;
+ sa.sa_flags = 0;
+ sa.sa_handler = nfacct_signal_handler;
+
+ // ignore all signals while we run in a signal handler
+ sigfillset(&sa.sa_mask);
+
+ for (i = 0; signals[i]; i++) {
+ if(sigaction(signals[i], &sa, NULL) == -1)
+ collector_error("Cannot add the handler to signal %d", signals[i]);
+ }
+}
+
+int main(int argc, char **argv) {
+ clocks_init();
+ nd_log_initialize_for_external_plugins("nfacct.plugin");
+
+ // ------------------------------------------------------------------------
+ // parse command line parameters
+
+ int i, freq = 0;
+ for(i = 1; i < argc ; i++) {
+ if(isdigit(*argv[i]) && !freq) {
+ int n = str2i(argv[i]);
+ if(n > 0 && n < 86400) {
+ freq = n;
+ continue;
+ }
+ }
+ 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) {
+ printf("nfacct.plugin %s\n", VERSION);
+ exit(0);
+ }
+ else if(strcmp("debug", argv[i]) == 0) {
+ debug = 1;
+ continue;
+ }
+ else if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
+ fprintf(stderr,
+ "\n"
+ " netdata nfacct.plugin %s\n"
+ " Copyright (C) 2015-2017 Costa Tsaousis <costa@tsaousis.gr>\n"
+ " Released under GNU General Public License v3 or later.\n"
+ " All rights reserved.\n"
+ "\n"
+ " This program is a data collector plugin for netdata.\n"
+ "\n"
+ " Available command line options:\n"
+ "\n"
+ " COLLECTION_FREQUENCY data collection frequency in seconds\n"
+ " minimum: %d\n"
+ "\n"
+ " debug enable verbose output\n"
+ " default: disabled\n"
+ "\n"
+ " -v\n"
+ " -V\n"
+ " --version print version and exit\n"
+ "\n"
+ " -h\n"
+ " --help print this message and exit\n"
+ "\n"
+ " For more information:\n"
+ " https://github.com/netdata/netdata/tree/master/collectors/nfacct.plugin\n"
+ "\n"
+ , VERSION
+ , netdata_update_every
+ );
+ exit(1);
+ }
+
+ collector_error("nfacct.plugin: ignoring parameter '%s'", argv[i]);
+ }
+
+ nfacct_signals();
+
+ errno = 0;
+
+ if(freq >= netdata_update_every)
+ netdata_update_every = freq;
+ else if(freq)
+ collector_error("update frequency %d seconds is too small for NFACCT. Using %d.", freq, netdata_update_every);
+
+ if (debug)
+ fprintf(stderr, "nfacct.plugin: calling nfacct_init()\n");
+ int nfacct = !nfacct_init(netdata_update_every);
+
+ if (debug)
+ fprintf(stderr, "nfacct.plugin: calling nfstat_init()\n");
+ int nfstat = !nfstat_init(netdata_update_every);
+
+ // ------------------------------------------------------------------------
+ // the main loop
+
+ if(debug) fprintf(stderr, "nfacct.plugin: starting data collection\n");
+
+ time_t started_t = now_monotonic_sec();
+
+ size_t iteration;
+ usec_t step = netdata_update_every * USEC_PER_SEC;
+
+ heartbeat_t hb;
+ heartbeat_init(&hb);
+ for(iteration = 0; 1; iteration++) {
+ usec_t dt = heartbeat_next(&hb, step);
+
+ if(unlikely(netdata_exit)) break;
+
+ if(debug && iteration)
+ fprintf(stderr, "nfacct.plugin: iteration %zu, dt %"PRIu64" usec\n"
+ , iteration
+ , dt
+ );
+
+ if(likely(nfacct)) {
+ if(debug) fprintf(stderr, "nfacct.plugin: calling nfacct_collect()\n");
+ nfacct = !nfacct_collect();
+
+ if(likely(nfacct)) {
+ if(debug) fprintf(stderr, "nfacct.plugin: calling nfacct_send_metrics()\n");
+ nfacct_send_metrics();
+ }
+ }
+
+ if(likely(nfstat)) {
+ if(debug) fprintf(stderr, "nfacct.plugin: calling nfstat_collect()\n");
+ nfstat = !nfstat_collect();
+
+ if(likely(nfstat)) {
+ if(debug) fprintf(stderr, "nfacct.plugin: calling nfstat_send_metrics()\n");
+ nfstat_send_metrics();
+ }
+ }
+
+ fflush(stdout);
+
+ if (now_monotonic_sec() - started_t > NFACCT_RESTART_EVERY_SECONDS) {
+ collector_info("NFACCT reached my lifetime expectancy. Exiting to restart.");
+ fprintf(stdout, "EXIT\n");
+ fflush(stdout);
+ exit(0);
+ }
+ }
+}