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