blob: b3dfaadc66c8d976b2f82b6953896721adfe6bcd [file] [log] [blame]
Mathieu Poirierc66949f2013-12-12 09:50:55 -07001/*
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>
Paul Stewart68446ae2016-11-15 09:28:11 -080026#include <endian.h>
Mathieu Poirierc66949f2013-12-12 09:50:55 -070027
Paul Stewart68446ae2016-11-15 09:28:11 -080028#include <netlink-private/object-api.h>
29#include <netlink-private/types.h>
Mathieu Poirierc66949f2013-12-12 09:50:55 -070030#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
40enum {
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
52static int nfacct_cmd_list(int argc, char *argv[]);
53static int nfacct_cmd_add(int argc, char *argv[]);
54static int nfacct_cmd_delete(int argc, char *argv[]);
55static int nfacct_cmd_get(int argc, char *argv[]);
56static int nfacct_cmd_flush(int argc, char *argv[]);
57static int nfacct_cmd_version(int argc, char *argv[]);
58static int nfacct_cmd_help(int argc, char *argv[]);
59static int nfacct_cmd_restore(int argc, char *argv[]);
60
Mathieu Poirier6f7aae02013-12-20 12:18:31 -070061#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
Paul Stewart68446ae2016-11-15 09:28:11 -080067#define NL_DBG(LVL,FMT,ARG...) do { } while(0)
68
Mathieu Poirierc66949f2013-12-12 09:50:55 -070069static void usage(char *argv[])
70{
71 fprintf(stderr, "Usage: %s command [parameters]...\n", argv[0]);
72}
73
74static 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
84int 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
146static 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
160static 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
187static int nfacct_cmd_list(int argc, char *argv[])
188{
189 struct nl_msg *msg;
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700190 struct nl_sock *handle;
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700191 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
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700226 handle = nl_socket_alloc();
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700227 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
246fail_send:
247 nl_close(handle);
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700248 nl_socket_free(handle);
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700249fail:
250 nlmsg_free(msg);
251 return ret;
252}
253
254static int _nfacct_cmd_add(char *name, int pkts, int bytes)
255{
256 struct nl_msg *msg;
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700257 struct nl_sock *handle;
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700258 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
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700287 handle = nl_socket_alloc();
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700288 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
306fail_send:
307 nl_close(handle);
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700308 nl_socket_free(handle);
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700309fail:
310 nlmsg_free(msg);
311 return ret;
312}
313
314
315
316static 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
329static int nfacct_cmd_delete(int argc, char *argv[])
330{
331 struct nl_msg *msg;
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700332 struct nl_sock *handle;
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700333 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
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700368 handle = nl_socket_alloc();
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700369 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
387fail_send:
388 nl_close(handle);
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700389 nl_socket_free(handle);
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700390fail:
391 nlmsg_free(msg);
392 return ret;
393 return 0;
394}
395
396
397static int nfacct_cmd_get(int argc, char *argv[])
398{
399 struct nl_msg *msg;
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700400 struct nl_sock *handle;
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700401 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
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700448 handle = nl_socket_alloc();
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700449
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
484fail_send:
485 nl_close(handle);
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700486 nl_socket_free(handle);
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700487fail:
488 nlmsg_free(msg);
489 return ret;
490}
491
492static int nfacct_cmd_flush(int argc, char *argv[])
493{
494 struct nl_msg *msg;
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700495 struct nl_sock *handle;
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700496 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
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700522 handle = nl_socket_alloc();
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700523 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
541fail_send:
542 nl_close(handle);
Mathieu Poirier6f7aae02013-12-20 12:18:31 -0700543 nl_socket_free(handle);
Mathieu Poirierc66949f2013-12-12 09:50:55 -0700544fail:
545 nlmsg_free(msg);
546 return ret;
547}
548
549static 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
560static int nfacct_cmd_version(int argc, char *argv[])
561{
562 printf(version_msg, VERSION);
563 return 0;
564}
565
566static 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
580static int nfacct_cmd_help(int argc, char *argv[])
581{
582 printf(help_msg, VERSION, argv[0]);
583 return 0;
584}
585
586static 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}