]> arthur.barton.de Git - netdata.git/blob - src/plugin_nfacct.c
convert nfacct to linked-list instead of array
[netdata.git] / src / plugin_nfacct.c
1 #include "common.h"
2
3 #ifdef INTERNAL_PLUGIN_NFACCT
4 #include <libmnl/libmnl.h>
5 #include <libnetfilter_acct/libnetfilter_acct.h>
6
7 struct nfacct_data {
8     char *name;
9     uint32_t hash;
10
11     uint64_t pkts;
12     uint64_t bytes;
13
14     RRDDIM *rd_bytes;
15     RRDDIM *rd_packets;
16
17     int updated;
18
19     struct nfacct_data *next;
20 };
21
22 static struct nfacct_list {
23     struct nfacct *nfacct_buffer;
24     struct nfacct_data *nfacct_metrics;
25 } nfacct_root = {
26         .nfacct_buffer = NULL,
27         .nfacct_metrics = NULL
28 };
29
30 static inline struct nfacct_data *nfacct_data_get(const char *name, uint32_t hash) {
31     struct nfacct_data *d = NULL, *last = NULL;
32     for(d = nfacct_root.nfacct_metrics; d ; last = d, d = d->next) {
33         if(unlikely(d->hash == hash && !strcmp(d->name, name)))
34             return d;
35     }
36
37     d = callocz(1, sizeof(struct nfacct_data));
38     d->name = strdupz(name);
39     d->hash = hash;
40
41     if(!last) {
42         d->next = nfacct_root.nfacct_metrics;
43         nfacct_root.nfacct_metrics = d;
44     }
45     else {
46         d->next = last->next;
47         last->next = d;
48     }
49
50     return d;
51 }
52
53 static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
54     (void)data;
55
56     if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) {
57         error("nfacct.plugin: nfacct_nlmsg_parse_payload() failed.");
58         return MNL_CB_OK;
59     }
60
61     const char *name = nfacct_attr_get_str(nfacct_root.nfacct_buffer, NFACCT_ATTR_NAME);
62     uint32_t hash = simple_hash(name);
63
64     struct nfacct_data *d = nfacct_data_get(name, hash);
65
66     d->pkts  = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_PKTS);
67     d->bytes = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_BYTES);
68     d->updated = 1;
69
70     return MNL_CB_OK;
71 }
72
73 void *nfacct_main(void *ptr) {
74     struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
75
76     info("NFACCT thread created with task id %d", gettid());
77
78     if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
79         error("nfacct.plugin: Cannot set pthread cancel type to DEFERRED.");
80
81     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
82         error("nfacct.plugin: Cannot set pthread cancel state to ENABLE.");
83
84     nfacct_root.nfacct_buffer = nfacct_alloc();
85     if(!nfacct_root.nfacct_buffer)
86         fatal("nfacct.plugin: nfacct_alloc() failed.");
87
88     char buf[MNL_SOCKET_BUFFER_SIZE];
89     struct mnl_socket *nl = NULL;
90     struct nlmsghdr *nlh = NULL;
91     unsigned int seq = 0, portid = 0;
92
93     seq = (unsigned int)now_realtime_sec() - 1;
94
95     nl  = mnl_socket_open(NETLINK_NETFILTER);
96     if(!nl) {
97         error("nfacct.plugin: mnl_socket_open() failed");
98         goto cleanup;
99     }
100
101     if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
102         error("nfacct.plugin: mnl_socket_bind() failed");
103         goto cleanup;
104     }
105     portid = mnl_socket_get_portid(nl);
106
107     // ------------------------------------------------------------------------
108
109     RRDSET *st_bytes = NULL, *st_packets = NULL;
110
111     // ------------------------------------------------------------------------
112
113     int update_every = (int)config_get_number("plugin:nfacct", "update every", localhost->rrd_update_every);
114     if(update_every < localhost->rrd_update_every)
115         update_every = localhost->rrd_update_every;
116
117     usec_t step = update_every * USEC_PER_SEC;
118     heartbeat_t hb;
119     heartbeat_init(&hb);
120     for(;;) {
121         heartbeat_dt_usec(&hb);
122         heartbeat_next(&hb, step);
123
124         if(unlikely(netdata_exit)) break;
125
126         seq++;
127
128         nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, (uint32_t)seq);
129         if(!nlh) {
130             error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed");
131             goto cleanup;
132         }
133
134         if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
135             error("nfacct.plugin: mnl_socket_send");
136             goto cleanup;
137         }
138
139         ssize_t ret;
140         while((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
141             if((ret = mnl_cb_run(buf, (size_t)ret, seq, portid, nfacct_callback, NULL)) <= 0) break;
142         }
143
144         if (ret == -1) {
145             error("nfacct.plugin: error communicating with kernel. NFACCT plugin can only work when netdata runs as root.");
146             goto cleanup;
147         }
148
149         // --------------------------------------------------------------------
150
151         if(nfacct_root.nfacct_metrics) {
152             struct nfacct_data *d;
153
154             if(!st_packets) {
155                 st_packets = rrdset_create_localhost(
156                         "netfilter"
157                         , "nfacct_packets"
158                         , NULL
159                         , "nfacct"
160                         , NULL
161                         , "Netfilter Accounting Packets"
162                         , "packets/s"
163                         , 3206
164                         , update_every
165                         , RRDSET_TYPE_STACKED
166                 );
167             }
168             else rrdset_next(st_packets);
169
170             for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
171                 if(likely(d->updated)) {
172                     if(unlikely(!d->rd_packets))
173                         d->rd_packets = rrddim_add(
174                                 st_packets
175                                 , d->name
176                                 , NULL
177                                 , 1
178                                 , update_every
179                                 , RRD_ALGORITHM_INCREMENTAL
180                         );
181
182                     rrddim_set_by_pointer(
183                             st_packets
184                             , d->rd_packets
185                             , (collected_number)d->pkts
186                     );
187                 }
188             }
189
190             rrdset_done(st_packets);
191
192             // ----------------------------------------------------------------
193
194             st_bytes = rrdset_find_bytype_localhost("netfilter", "nfacct_bytes");
195             if(!st_bytes) {
196                 st_bytes = rrdset_create_localhost(
197                         "netfilter"
198                         , "nfacct_bytes"
199                         , NULL
200                         , "nfacct"
201                         , NULL
202                         , "Netfilter Accounting Bandwidth"
203                         , "kilobytes/s"
204                         , 3207
205                         , update_every
206                         , RRDSET_TYPE_STACKED
207                 );
208             }
209             else rrdset_next(st_bytes);
210
211             for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
212                 if(likely(d->updated)) {
213                     if(unlikely(!d->rd_bytes))
214                         d->rd_bytes = rrddim_add(
215                                 st_bytes
216                                 , d->name
217                                 , NULL
218                                 , 1
219                                 , 1000 * update_every
220                                 , RRD_ALGORITHM_INCREMENTAL
221                         );
222
223                     rrddim_set_by_pointer(
224                             st_bytes
225                             , d->rd_bytes
226                             , (collected_number)d->bytes
227                     );
228                 }
229             }
230
231             rrdset_done(st_bytes);
232
233
234             // ----------------------------------------------------------------
235             // prepare for the next loop
236
237             for(d = nfacct_root.nfacct_metrics; d ; d = d->next)
238                 d->updated = 0;
239         }
240     }
241
242 cleanup:
243     info("NFACCT thread exiting");
244
245     if(nl) mnl_socket_close(nl);
246
247     static_thread->enabled = 0;
248     pthread_exit(NULL);
249     return NULL;
250 }
251 #endif