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