Home | History | Annotate | Download | only in nfacct
      1 /*
      2  * Original implementation on libmnl:
      3  * (C) 2011 by Pablo Neira Ayuso <pablo (at) 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 (at) 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 
     69 static void usage(char *argv[])
     70 {
     71 	fprintf(stderr, "Usage: %s command [parameters]...\n", argv[0]);
     72 }
     73 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 (at) netfilter.org>\n"
    553 	"Copyright (C) 2011 Intra2net AG <http://www.intra2net.com>\n"
    554 	"Copyright (C) 2013 Mathieu Poirier <mathieu.poirier (at) 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 
    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 
    580 static int nfacct_cmd_help(int argc, char *argv[])
    581 {
    582 	printf(help_msg, VERSION, argv[0]);
    583 	return 0;
    584 }
    585 
    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