#include <libmnl/libmnl.h>
#include <libnetfilter_acct/libnetfilter_acct.h>
-struct mynfacct {
- const char *name;
+struct nfacct_data {
+ char *name;
+ uint32_t hash;
+
uint64_t pkts;
uint64_t bytes;
- struct nfacct *nfacct;
-};
-struct nfacct_list {
- int size;
- int len;
- struct mynfacct data[];
-} *nfacct_list = NULL;
+ RRDDIM *rd_bytes;
+ RRDDIM *rd_packets;
-static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
- if(data) {};
+ int updated;
- if(!nfacct_list || nfacct_list->len == nfacct_list->size) {
- int size = (nfacct_list) ? nfacct_list->size : 0;
- int len = (nfacct_list) ? nfacct_list->len : 0;
- size++;
+ struct nfacct_data *next;
+};
- info("nfacct.plugin: increasing nfacct_list to size %d", size);
+static struct nfacct_list {
+ struct nfacct *nfacct_buffer;
+ struct nfacct_data *nfacct_metrics;
+} nfacct_root = {
+ .nfacct_buffer = NULL,
+ .nfacct_metrics = NULL
+};
- nfacct_list = reallocz(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size));
+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;
+ }
- nfacct_list->data[len].nfacct = nfacct_alloc();
- if(!nfacct_list->data[size - 1].nfacct) {
- error("nfacct.plugin: nfacct_alloc() failed.");
- free(nfacct_list);
- nfacct_list = NULL;
- return MNL_CB_OK;
- }
+ d = callocz(1, sizeof(struct nfacct_data));
+ d->name = strdupz(name);
+ d->hash = hash;
- nfacct_list->size = size;
- nfacct_list->len = len;
+ if(!last) {
+ d->next = nfacct_root.nfacct_metrics;
+ nfacct_root.nfacct_metrics = d;
+ }
+ else {
+ d->next = last->next;
+ last->next = d;
}
- if(nfacct_nlmsg_parse_payload(nlh, nfacct_list->data[nfacct_list->len].nfacct) < 0) {
+ return d;
+}
+
+static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
+ (void)data;
+
+ if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) {
error("nfacct.plugin: nfacct_nlmsg_parse_payload() failed.");
return MNL_CB_OK;
}
- nfacct_list->data[nfacct_list->len].name = nfacct_attr_get_str(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_NAME);
- nfacct_list->data[nfacct_list->len].pkts = nfacct_attr_get_u64(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_PKTS);
- nfacct_list->data[nfacct_list->len].bytes = nfacct_attr_get_u64(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_BYTES);
+ 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;
- nfacct_list->len++;
return MNL_CB_OK;
}
void *nfacct_main(void *ptr) {
- if(ptr) { ; }
+ struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
info("NFACCT thread created with task id %d", gettid());
if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
error("nfacct.plugin: Cannot set pthread cancel state to ENABLE.");
+ nfacct_root.nfacct_buffer = nfacct_alloc();
+ if(!nfacct_root.nfacct_buffer)
+ fatal("nfacct.plugin: nfacct_alloc() failed.");
+
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = NULL;
struct nlmsghdr *nlh = NULL;
unsigned int seq = 0, portid = 0;
- seq = time(NULL) - 1;
+ seq = (unsigned int)now_realtime_sec() - 1;
nl = mnl_socket_open(NETLINK_NETFILTER);
if(!nl) {
error("nfacct.plugin: mnl_socket_open() failed");
- pthread_exit(NULL);
- return NULL;
+ goto cleanup;
}
if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- mnl_socket_close(nl);
error("nfacct.plugin: mnl_socket_bind() failed");
- pthread_exit(NULL);
- return NULL;
+ goto cleanup;
}
portid = mnl_socket_get_portid(nl);
// ------------------------------------------------------------------------
- struct timeval last, now;
- unsigned long long usec = 0, susec = 0;
- RRDSET *st = NULL;
-
- gettimeofday(&last, NULL);
+ RRDSET *st_bytes = NULL, *st_packets = NULL;
// ------------------------------------------------------------------------
- while(1) {
+ int update_every = (int)config_get_number("plugin:nfacct", "update every", localhost->rrd_update_every);
+ if(update_every < localhost->rrd_update_every)
+ update_every = localhost->rrd_update_every;
+
+ usec_t step = update_every * USEC_PER_SEC;
+ heartbeat_t hb;
+ heartbeat_init(&hb);
+ for(;;) {
+ heartbeat_dt_usec(&hb);
+ heartbeat_next(&hb, step);
+
if(unlikely(netdata_exit)) break;
seq++;
- nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq);
+ nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, (uint32_t)seq);
if(!nlh) {
- mnl_socket_close(nl);
error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed");
- pthread_exit(NULL);
- return NULL;
+ goto cleanup;
}
if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
error("nfacct.plugin: mnl_socket_send");
- pthread_exit(NULL);
- return NULL;
+ goto cleanup;
}
- if(nfacct_list) nfacct_list->len = 0;
-
- int ret;
+ ssize_t ret;
while((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
- if((ret = mnl_cb_run(buf, ret, seq, portid, nfacct_callback, NULL)) <= 0) break;
+ if((ret = mnl_cb_run(buf, (size_t)ret, seq, portid, nfacct_callback, NULL)) <= 0) break;
}
if (ret == -1) {
- error("nfacct.plugin: error communicating with kernel.");
- pthread_exit(NULL);
- return NULL;
+ error("nfacct.plugin: error communicating with kernel. NFACCT plugin can only work when netdata runs as root.");
+ goto cleanup;
}
// --------------------------------------------------------------------
- gettimeofday(&now, NULL);
- usec = usec_dt(&now, &last) - susec;
- debug(D_NFACCT_LOOP, "nfacct.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec);
-
- if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
- else susec = rrd_update_every * 1000000ULL / 2ULL;
-
-
- // --------------------------------------------------------------------
-
- if(nfacct_list && nfacct_list->len) {
- int i;
-
- st = rrdset_find_bytype("netfilter", "nfacct_packets");
- if(!st) {
- st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 3206, rrd_update_every, RRDSET_TYPE_STACKED);
-
- for(i = 0; i < nfacct_list->len ; i++)
- rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
+ if(nfacct_root.nfacct_metrics) {
+ struct nfacct_data *d;
+
+ if(!st_packets) {
+ st_packets = rrdset_create_localhost(
+ "netfilter"
+ , "nfacct_packets"
+ , NULL
+ , "nfacct"
+ , NULL
+ , "Netfilter Accounting Packets"
+ , "packets/s"
+ , 3206
+ , update_every
+ , RRDSET_TYPE_STACKED
+ );
}
- else rrdset_next(st);
-
- for(i = 0; i < nfacct_list->len ; i++) {
- RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
-
- if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
- if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].pkts);
+ else rrdset_next(st_packets);
+
+ for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
+ if(likely(d->updated)) {
+ if(unlikely(!d->rd_packets))
+ d->rd_packets = rrddim_add(
+ st_packets
+ , d->name
+ , NULL
+ , 1
+ , update_every
+ , RRD_ALGORITHM_INCREMENTAL
+ );
+
+ rrddim_set_by_pointer(
+ st_packets
+ , d->rd_packets
+ , (collected_number)d->pkts
+ );
+ }
}
- rrdset_done(st);
+ rrdset_done(st_packets);
// ----------------------------------------------------------------
- st = rrdset_find_bytype("netfilter", "nfacct_bytes");
- if(!st) {
- st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 3207, rrd_update_every, RRDSET_TYPE_STACKED);
-
- for(i = 0; i < nfacct_list->len ; i++)
- rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
+ st_bytes = rrdset_find_bytype_localhost("netfilter", "nfacct_bytes");
+ if(!st_bytes) {
+ st_bytes = rrdset_create_localhost(
+ "netfilter"
+ , "nfacct_bytes"
+ , NULL
+ , "nfacct"
+ , NULL
+ , "Netfilter Accounting Bandwidth"
+ , "kilobytes/s"
+ , 3207
+ , update_every
+ , RRDSET_TYPE_STACKED
+ );
}
- else rrdset_next(st);
-
- for(i = 0; i < nfacct_list->len ; i++) {
- RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
-
- if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
- if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].bytes);
+ else rrdset_next(st_bytes);
+
+ for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
+ if(likely(d->updated)) {
+ if(unlikely(!d->rd_bytes))
+ d->rd_bytes = rrddim_add(
+ st_bytes
+ , d->name
+ , NULL
+ , 1
+ , 1000 * update_every
+ , RRD_ALGORITHM_INCREMENTAL
+ );
+
+ rrddim_set_by_pointer(
+ st_bytes
+ , d->rd_bytes
+ , (collected_number)d->bytes
+ );
+ }
}
- rrdset_done(st);
- }
+ rrdset_done(st_bytes);
- // --------------------------------------------------------------------
- usleep(susec);
+ // ----------------------------------------------------------------
+ // prepare for the next loop
- // copy current to last
- bcopy(&now, &last, sizeof(struct timeval));
+ for(d = nfacct_root.nfacct_metrics; d ; d = d->next)
+ d->updated = 0;
+ }
}
- mnl_socket_close(nl);
+cleanup:
+ info("NFACCT thread exiting");
+
+ if(nl) mnl_socket_close(nl);
+
+ static_thread->enabled = 0;
pthread_exit(NULL);
return NULL;
}