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