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 
     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 
     65 static void usage(char *argv[])
     66 {
     67 	fprintf(stderr, "Usage: %s command [parameters]...\n", argv[0]);
     68 }
     69 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 (at) netfilter.org>\n"
    549 	"Copyright (C) 2011 Intra2net AG <http://www.intra2net.com>\n"
    550 	"Copyright (C) 2013 Mathieu Poirier <mathieu.poirier (at) 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 
    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 
    576 static int nfacct_cmd_help(int argc, char *argv[])
    577 {
    578 	printf(help_msg, VERSION, argv[0]);
    579 	return 0;
    580 }
    581 
    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