1 /*
2 * Original implementation on libmnl:
3 * (C) 2011 by Pablo Neira Ayuso <pablo@netfilter.org>
4 * (C) 2011 by Intra2net AG <http://www.intra2net.com>
5 *
6 * Port to libnl:
7 * (C) 2013 by Mathieu J. Poirier <mathieu.poirier@linaro.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <dirent.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <time.h>
25 #include <errno.h>
26
27 #include <netlink-local.h>
28 #include <linux/netlink.h>
29 #include <linux/netfilter/nfnetlink.h>
30 #include <linux/netfilter/nfnetlink_acct.h>
31 #include <netlink/netfilter/nfnl.h>
32 #include <netlink/netlink.h>
33 #include <netlink/socket.h>
34 #include <netlink/msg.h>
35
36 #define VERSION "1.0.1"
37
38 enum {
39 NFACCT_CMD_NONE = 0,
40 NFACCT_CMD_LIST,
41 NFACCT_CMD_ADD,
42 NFACCT_CMD_DELETE,
43 NFACCT_CMD_GET,
44 NFACCT_CMD_FLUSH,
45 NFACCT_CMD_VERSION,
46 NFACCT_CMD_HELP,
47 NFACCT_CMD_RESTORE,
48 };
49
50 static int nfacct_cmd_list(int argc, char *argv[]);
51 static int nfacct_cmd_add(int argc, char *argv[]);
52 static int nfacct_cmd_delete(int argc, char *argv[]);
53 static int nfacct_cmd_get(int argc, char *argv[]);
54 static int nfacct_cmd_flush(int argc, char *argv[]);
55 static int nfacct_cmd_version(int argc, char *argv[]);
56 static int nfacct_cmd_help(int argc, char *argv[]);
57 static int nfacct_cmd_restore(int argc, char *argv[]);
58
59 #ifndef HAVE_LIBNL20
60 #define nl_sock nl_handle
61 #define nl_socket_alloc nl_handle_alloc
62 #define nl_socket_free nl_handle_destroy
63 #endif
64
usage(char * argv[])65 static void usage(char *argv[])
66 {
67 fprintf(stderr, "Usage: %s command [parameters]...\n", argv[0]);
68 }
69
nfacct_perror(const char * msg)70 static void nfacct_perror(const char *msg)
71 {
72 if (errno == 0) {
73 fprintf(stderr, "nfacct v%s: %s\n", VERSION, msg);
74 } else {
75 fprintf(stderr, "nfacct v%s: %s: %s\n",
76 VERSION, msg, strerror(errno));
77 }
78 }
79
main(int argc,char * argv[])80 int main(int argc, char *argv[])
81 {
82 int cmd = NFACCT_CMD_NONE, ret = 0;
83
84 if (argc < 2) {
85 usage(argv);
86 exit(EXIT_FAILURE);
87 }
88
89 if (strncmp(argv[1], "list", strlen(argv[1])) == 0)
90 cmd = NFACCT_CMD_LIST;
91 else if (strncmp(argv[1], "add", strlen(argv[1])) == 0)
92 cmd = NFACCT_CMD_ADD;
93 else if (strncmp(argv[1], "delete", strlen(argv[1])) == 0)
94 cmd = NFACCT_CMD_DELETE;
95 else if (strncmp(argv[1], "get", strlen(argv[1])) == 0)
96 cmd = NFACCT_CMD_GET;
97 else if (strncmp(argv[1], "flush", strlen(argv[1])) == 0)
98 cmd = NFACCT_CMD_FLUSH;
99 else if (strncmp(argv[1], "version", strlen(argv[1])) == 0)
100 cmd = NFACCT_CMD_VERSION;
101 else if (strncmp(argv[1], "help", strlen(argv[1])) == 0)
102 cmd = NFACCT_CMD_HELP;
103 else if (strncmp(argv[1], "restore", strlen(argv[1])) == 0)
104 cmd = NFACCT_CMD_RESTORE;
105 else {
106 fprintf(stderr, "nfacct v%s: Unknown command: %s\n",
107 VERSION, argv[1]);
108 usage(argv);
109 exit(EXIT_FAILURE);
110 }
111
112 switch(cmd) {
113 case NFACCT_CMD_LIST:
114 ret = nfacct_cmd_list(argc, argv);
115 break;
116 case NFACCT_CMD_ADD:
117 ret = nfacct_cmd_add(argc, argv);
118 break;
119 case NFACCT_CMD_DELETE:
120 ret = nfacct_cmd_delete(argc, argv);
121 break;
122 case NFACCT_CMD_GET:
123 ret = nfacct_cmd_get(argc, argv);
124 break;
125 case NFACCT_CMD_FLUSH:
126 ret = nfacct_cmd_flush(argc, argv);
127 break;
128 case NFACCT_CMD_VERSION:
129 ret = nfacct_cmd_version(argc, argv);
130 break;
131 case NFACCT_CMD_HELP:
132 ret = nfacct_cmd_help(argc, argv);
133 break;
134 case NFACCT_CMD_RESTORE:
135 ret = nfacct_cmd_restore(argc, argv);
136 break;
137 }
138 return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
139 }
140
141
message_received(struct nl_msg * msg,void * arg)142 static int message_received(struct nl_msg *msg, void *arg)
143 {
144 struct nlmsghdr *hdr = msg->nm_nlh;
145
146 if (hdr->nlmsg_type == NLMSG_ERROR) {
147 struct nlmsgerr *err = nlmsg_data(hdr);
148
149 if (err->error == 0)
150 return NL_STOP;
151 }
152
153 return NL_OK;
154 }
155
valid_input(struct nl_msg * msg,void * arg)156 static int valid_input(struct nl_msg *msg, void *arg)
157 {
158 struct nlattr *nla = nlmsg_attrdata(nlmsg_hdr(msg),
159 sizeof(struct nfgenmsg));
160 struct nlattr *tb[NFACCT_NAME_MAX+1] = {};
161 char buf[4096];
162 int ret;
163
164 ret = nlmsg_parse(nlmsg_hdr(msg),
165 sizeof(struct nfgenmsg), tb, NFACCT_MAX, NULL);
166
167 if (ret < 0) {
168 nfacct_perror("Can't parse message\n");
169 return ret;
170 }
171
172 ret = snprintf(buf, sizeof(buf),
173 "{ pkts = %.20llu, bytes = %.20llu } = %s;",
174 (unsigned long long)be64toh(nla_get_u64(tb[NFACCT_PKTS])),
175 (unsigned long long)be64toh(nla_get_u64(tb[NFACCT_BYTES])),
176 nla_get_string(tb[NFACCT_NAME]));
177
178 printf("%s\n", buf);
179
180 return 0;
181 }
182
nfacct_cmd_list(int argc,char * argv[])183 static int nfacct_cmd_list(int argc, char *argv[])
184 {
185 struct nl_msg *msg;
186 struct nl_sock *handle;
187 int zeroctr = 0;
188 int ret, i;
189
190 for (i=2; i<argc; i++) {
191 if (strncmp(argv[i], "reset", strlen(argv[i])) == 0) {
192 zeroctr = 1;
193 } else if (strncmp(argv[i], "xml", strlen(argv[i])) == 0) {
194 nfacct_perror("xml feature not implemented");
195 return -1;
196 } else {
197 nfacct_perror("unknown argument");
198 return -1;
199 }
200 }
201
202 msg = nlmsg_alloc();
203 if (!msg)
204 return -1;
205
206 ret = nfnlmsg_put(msg,
207 NL_AUTO_PID,
208 NL_AUTO_SEQ,
209 NFNL_SUBSYS_ACCT,
210 zeroctr ?
211 NFNL_MSG_ACCT_GET_CTRZERO : NFNL_MSG_ACCT_GET,
212 NLM_F_DUMP | NLM_F_REQUEST,
213 AF_UNSPEC,
214 0);
215
216 if (ret) {
217 NL_DBG(2, "Can't append payload to message: %s line: %d\n",
218 __FUNCTION__, __LINE__);
219 goto fail;
220 }
221
222 handle = nl_socket_alloc();
223 if ((ret = nfnl_connect(handle))) {
224 NL_DBG(2, "Can't connect handle: %s line: %d\n",
225 __FUNCTION__, __LINE__);
226 goto fail;
227 }
228
229 if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
230 NL_DBG(2, "Can't send msg: %s line: %d\n",
231 __FUNCTION__, __LINE__);
232 goto fail_send;
233 }
234
235 nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM, valid_input, NULL);
236 ret = nl_recvmsgs_default(handle);
237 if (ret < 0) {
238 NL_DBG(2, "Can't receice msg: %s line: %d\n",
239 __FUNCTION__, __LINE__);
240 }
241
242 fail_send:
243 nl_close(handle);
244 nl_socket_free(handle);
245 fail:
246 nlmsg_free(msg);
247 return ret;
248 }
249
_nfacct_cmd_add(char * name,int pkts,int bytes)250 static int _nfacct_cmd_add(char *name, int pkts, int bytes)
251 {
252 struct nl_msg *msg;
253 struct nl_sock *handle;
254 char nfname[NFACCT_NAME_MAX];
255 int ret;
256
257 strncpy(nfname, name, NFACCT_NAME_MAX);
258 nfname[NFACCT_NAME_MAX-1] = '\0';
259
260 msg = nlmsg_alloc();
261 if (!msg)
262 return -1;
263
264 ret = nfnlmsg_put(msg,
265 NL_AUTO_PID,
266 NL_AUTO_SEQ,
267 NFNL_SUBSYS_ACCT,
268 NFNL_MSG_ACCT_NEW,
269 NLM_F_CREATE | NLM_F_ACK | NLM_F_REQUEST,
270 AF_UNSPEC,
271 0);
272
273 if (ret) {
274 NL_DBG(2, "Can't append payload to message: %s line: %d\n",
275 __FUNCTION__, __LINE__);
276 goto fail;
277 }
278
279 nla_put_string(msg, NFACCT_NAME, nfname);
280 nla_put_u64(msg, NFACCT_PKTS, htobe64(pkts));
281 nla_put_u64(msg, NFACCT_BYTES, htobe64(bytes));
282
283 handle = nl_socket_alloc();
284 if ((ret = nfnl_connect(handle))) {
285 NL_DBG(2, "Can't connect handle: %s line: %d\n",
286 __FUNCTION__, __LINE__);
287 goto fail;
288 }
289
290 if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
291 NL_DBG(2, "Can't send msg: %s line: %d\n",
292 __FUNCTION__, __LINE__);
293 goto fail_send;
294 }
295
296 ret = nl_recvmsgs_default(handle);
297 if (ret < 0) {
298 NL_DBG(2, "Can't receice msg: %s line: %d\n",
299 __FUNCTION__, __LINE__);
300 }
301
302 fail_send:
303 nl_close(handle);
304 nl_socket_free(handle);
305 fail:
306 nlmsg_free(msg);
307 return ret;
308 }
309
310
311
nfacct_cmd_add(int argc,char * argv[])312 static int nfacct_cmd_add(int argc, char *argv[])
313 {
314 if (argc < 3) {
315 nfacct_perror("missing object name");
316 return -1;
317 } else if (argc > 3) {
318 nfacct_perror("too many arguments");
319 return -1;
320 }
321
322 return _nfacct_cmd_add(argv[2], 0, 0);
323 }
324
nfacct_cmd_delete(int argc,char * argv[])325 static int nfacct_cmd_delete(int argc, char *argv[])
326 {
327 struct nl_msg *msg;
328 struct nl_sock *handle;
329 char nfname[NFACCT_NAME_MAX];
330 int ret;
331
332 if (argc < 3) {
333 nfacct_perror("missing object name");
334 return -1;
335 } else if (argc > 3) {
336 nfacct_perror("too many arguments");
337 return -1;
338 }
339
340 strncpy(nfname, argv[2], NFACCT_NAME_MAX);
341 nfname[NFACCT_NAME_MAX-1] = '\0';
342
343 msg = nlmsg_alloc();
344 if (!msg)
345 return -1;
346
347 ret = nfnlmsg_put(msg,
348 NL_AUTO_PID,
349 NL_AUTO_SEQ,
350 NFNL_SUBSYS_ACCT,
351 NFNL_MSG_ACCT_DEL,
352 NLM_F_ACK | NLM_F_REQUEST,
353 AF_UNSPEC,
354 0);
355
356 if (ret) {
357 NL_DBG(2, "Can't append payload to message: %s line: %d\n",
358 __FUNCTION__, __LINE__);
359 goto fail;
360 }
361
362 nla_put_string(msg, NFACCT_NAME, nfname);
363
364 handle = nl_socket_alloc();
365 if ((ret = nfnl_connect(handle))) {
366 NL_DBG(2, "Can't connect handle: %s line: %d\n",
367 __FUNCTION__, __LINE__);
368 goto fail;
369 }
370
371 if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
372 NL_DBG(2, "Can't send msg: %s line: %d\n",
373 __FUNCTION__, __LINE__);
374 goto fail_send;
375 }
376
377 ret = nl_recvmsgs_default(handle);
378 if (ret < 0) {
379 NL_DBG(2, "Can't receice msg: %s line: %d\n",
380 __FUNCTION__, __LINE__);
381 }
382
383 fail_send:
384 nl_close(handle);
385 nl_socket_free(handle);
386 fail:
387 nlmsg_free(msg);
388 return ret;
389 return 0;
390 }
391
392
nfacct_cmd_get(int argc,char * argv[])393 static int nfacct_cmd_get(int argc, char *argv[])
394 {
395 struct nl_msg *msg;
396 struct nl_sock *handle;
397 struct nl_cb *cb;
398 char nfname[NFACCT_NAME_MAX];
399 int zeroctr = 0;
400 int ret, i;
401
402 if (argc < 3) {
403 nfacct_perror("missing object name");
404 return -1;
405 }
406
407 for (i=3; i<argc; i++) {
408 if (strncmp(argv[i], "reset", strlen(argv[i])) == 0) {
409 zeroctr = 1;
410 } else if (strncmp(argv[i], "xml", strlen(argv[i])) == 0) {
411 nfacct_perror("xml feature not implemented");
412 return -1;
413 } else {
414 nfacct_perror("unknown argument");
415 return -1;
416 }
417 }
418
419 strncpy(nfname, argv[2], NFACCT_NAME_MAX);
420 nfname[NFACCT_NAME_MAX-1] = '\0';
421
422 msg = nlmsg_alloc();
423 if (!msg)
424 return -1;
425
426 ret = nfnlmsg_put(msg,
427 NL_AUTO_PID,
428 NL_AUTO_SEQ,
429 NFNL_SUBSYS_ACCT,
430 zeroctr ?
431 NFNL_MSG_ACCT_GET_CTRZERO : NFNL_MSG_ACCT_GET,
432 NLM_F_ACK | NLM_F_REQUEST,
433 AF_UNSPEC,
434 0);
435
436 if (ret) {
437 NL_DBG(2, "Can't append payload to message: %s line: %d\n",
438 __FUNCTION__, __LINE__);
439 goto fail;
440 }
441
442 nla_put_string(msg, NFACCT_NAME, nfname);
443
444 handle = nl_socket_alloc();
445
446 if (handle) {
447 cb = nl_cb_alloc(NL_CB_DEFAULT);
448 if (!cb)
449 goto fail;
450
451 if (nl_cb_set(cb, NL_CB_MSG_IN,
452 NL_CB_CUSTOM,
453 message_received, NULL) < 0)
454 goto fail;
455
456 nl_socket_set_cb(handle,cb);
457 } else {
458 goto fail;
459 }
460
461 if ((ret = nfnl_connect(handle))) {
462 NL_DBG(2, "Can't connect handle: %s line: %d\n",
463 __FUNCTION__, __LINE__);
464 goto fail;
465 }
466
467 if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
468 NL_DBG(2, "Can't send msg: %s line: %d\n",
469 __FUNCTION__, __LINE__);
470 goto fail_send;
471 }
472
473 nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM, valid_input, NULL);
474 ret = nl_recvmsgs_default(handle);
475 if (ret < 0) {
476 NL_DBG(2, "Can't receice msg: %s line: %d\n",
477 __FUNCTION__, __LINE__);
478 }
479
480 fail_send:
481 nl_close(handle);
482 nl_socket_free(handle);
483 fail:
484 nlmsg_free(msg);
485 return ret;
486 }
487
nfacct_cmd_flush(int argc,char * argv[])488 static int nfacct_cmd_flush(int argc, char *argv[])
489 {
490 struct nl_msg *msg;
491 struct nl_sock *handle;
492 int ret;
493
494 if (argc > 2) {
495 nfacct_perror("too many arguments");
496 return -1;
497 }
498
499 msg = nlmsg_alloc();
500 if (!msg)
501 return -1;
502
503 ret = nfnlmsg_put(msg,
504 NL_AUTO_PID,
505 NL_AUTO_SEQ,
506 NFNL_SUBSYS_ACCT,
507 NFNL_MSG_ACCT_DEL,
508 NLM_F_ACK | NLM_F_REQUEST,
509 AF_UNSPEC,
510 0);
511
512 if (ret) {
513 NL_DBG(2, "Can't append payload to message: %s line: %d\n",
514 __FUNCTION__, __LINE__);
515 goto fail;
516 }
517
518 handle = nl_socket_alloc();
519 if ((ret = nfnl_connect(handle))) {
520 NL_DBG(2, "Can't connect handle: %s line: %d\n",
521 __FUNCTION__, __LINE__);
522 goto fail;
523 }
524
525 if ((ret = nl_send_auto_complete(handle, msg)) < 0) {
526 NL_DBG(2, "Can't send msg: %s line: %d\n",
527 __FUNCTION__, __LINE__);
528 goto fail_send;
529 }
530
531 ret = nl_recvmsgs_default(handle);
532 if (ret < 0) {
533 NL_DBG(2, "Can't receice msg: %s line: %d\n",
534 __FUNCTION__, __LINE__);
535 }
536
537 fail_send:
538 nl_close(handle);
539 nl_socket_free(handle);
540 fail:
541 nlmsg_free(msg);
542 return ret;
543 }
544
545 static const char version_msg[] =
546 "nfacct v%s: utility for the Netfilter extended accounting "
547 "infrastructure\n"
548 "Copyright (C) 2011 Pablo Neira Ayuso <pablo@netfilter.org>\n"
549 "Copyright (C) 2011 Intra2net AG <http://www.intra2net.com>\n"
550 "Copyright (C) 2013 Mathieu Poirier <mathieu.poirier@linaro.org>\n"
551 "This program comes with ABSOLUTELY NO WARRANTY.\n"
552 "This is free software, and you are welcome to redistribute it under "
553 "certain \nconditions; see LICENSE file distributed in this package "
554 "for details.\n";
555
nfacct_cmd_version(int argc,char * argv[])556 static int nfacct_cmd_version(int argc, char *argv[])
557 {
558 printf(version_msg, VERSION);
559 return 0;
560 }
561
562 static const char help_msg[] =
563 "nfacct v%s: utility for the Netfilter extended accounting "
564 "infrastructure\n"
565 "Usage: %s command [parameters]...\n\n"
566 "Commands:\n"
567 " list [reset]\t\tList the accounting object table (and reset)\n"
568 " add object-name\tAdd new accounting object to table\n"
569 " delete object-name\tDelete existing accounting object\n"
570 " get object-name\tGet existing accounting object\n"
571 " flush\t\t\tFlush accounting object table\n"
572 " restore\t\tRestore accounting object table reading 'list' output from stdin\n"
573 " version\t\tDisplay version and disclaimer\n"
574 " help\t\t\tDisplay this help message\n";
575
nfacct_cmd_help(int argc,char * argv[])576 static int nfacct_cmd_help(int argc, char *argv[])
577 {
578 printf(help_msg, VERSION, argv[0]);
579 return 0;
580 }
581
nfacct_cmd_restore(int argc,char * argv[])582 static int nfacct_cmd_restore(int argc, char *argv[])
583 {
584 uint64_t pkts, bytes;
585 char name[512];
586 char buffer[512];
587 int ret;
588 while (fgets(buffer, sizeof(buffer), stdin)) {
589 char *semicolon = strchr(buffer, ';');
590 if (semicolon == NULL) {
591 nfacct_perror("invalid line");
592 return -1;
593 }
594 *semicolon = 0;
595 ret = sscanf(buffer, "{ pkts = %llu, bytes = %llu } = %s",
596 &pkts, &bytes, name);
597 if (ret != 3) {
598 nfacct_perror("error reading input");
599 return -1;
600 }
601 if ((ret = _nfacct_cmd_add(name, pkts, bytes)) != 0)
602 return ret;
603
604 }
605 return 0;
606 }
607