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