/* * Original implementation on libmnl: * (C) 2011 by Pablo Neira Ayuso * (C) 2011 by Intra2net AG * * Port to libnl: * (C) 2013 by Mathieu J. Poirier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VERSION "1.0.1" enum { NFACCT_CMD_NONE = 0, NFACCT_CMD_LIST, NFACCT_CMD_ADD, NFACCT_CMD_DELETE, NFACCT_CMD_GET, NFACCT_CMD_FLUSH, NFACCT_CMD_VERSION, NFACCT_CMD_HELP, NFACCT_CMD_RESTORE, }; static int nfacct_cmd_list(int argc, char *argv[]); static int nfacct_cmd_add(int argc, char *argv[]); static int nfacct_cmd_delete(int argc, char *argv[]); static int nfacct_cmd_get(int argc, char *argv[]); static int nfacct_cmd_flush(int argc, char *argv[]); static int nfacct_cmd_version(int argc, char *argv[]); static int nfacct_cmd_help(int argc, char *argv[]); static int nfacct_cmd_restore(int argc, char *argv[]); #ifndef HAVE_LIBNL20 #define nl_sock nl_handle #define nl_socket_alloc nl_handle_alloc #define nl_socket_free nl_handle_destroy #endif static void usage(char *argv[]) { fprintf(stderr, "Usage: %s command [parameters]...\n", argv[0]); } static void nfacct_perror(const char *msg) { if (errno == 0) { fprintf(stderr, "nfacct v%s: %s\n", VERSION, msg); } else { fprintf(stderr, "nfacct v%s: %s: %s\n", VERSION, msg, strerror(errno)); } } int main(int argc, char *argv[]) { int cmd = NFACCT_CMD_NONE, ret = 0; if (argc < 2) { usage(argv); exit(EXIT_FAILURE); } if (strncmp(argv[1], "list", strlen(argv[1])) == 0) cmd = NFACCT_CMD_LIST; else if (strncmp(argv[1], "add", strlen(argv[1])) == 0) cmd = NFACCT_CMD_ADD; else if (strncmp(argv[1], "delete", strlen(argv[1])) == 0) cmd = NFACCT_CMD_DELETE; else if (strncmp(argv[1], "get", strlen(argv[1])) == 0) cmd = NFACCT_CMD_GET; else if (strncmp(argv[1], "flush", strlen(argv[1])) == 0) cmd = NFACCT_CMD_FLUSH; else if (strncmp(argv[1], "version", strlen(argv[1])) == 0) cmd = NFACCT_CMD_VERSION; else if (strncmp(argv[1], "help", strlen(argv[1])) == 0) cmd = NFACCT_CMD_HELP; else if (strncmp(argv[1], "restore", strlen(argv[1])) == 0) cmd = NFACCT_CMD_RESTORE; else { fprintf(stderr, "nfacct v%s: Unknown command: %s\n", VERSION, argv[1]); usage(argv); exit(EXIT_FAILURE); } switch(cmd) { case NFACCT_CMD_LIST: ret = nfacct_cmd_list(argc, argv); break; case NFACCT_CMD_ADD: ret = nfacct_cmd_add(argc, argv); break; case NFACCT_CMD_DELETE: ret = nfacct_cmd_delete(argc, argv); break; case NFACCT_CMD_GET: ret = nfacct_cmd_get(argc, argv); break; case NFACCT_CMD_FLUSH: ret = nfacct_cmd_flush(argc, argv); break; case NFACCT_CMD_VERSION: ret = nfacct_cmd_version(argc, argv); break; case NFACCT_CMD_HELP: ret = nfacct_cmd_help(argc, argv); break; case NFACCT_CMD_RESTORE: ret = nfacct_cmd_restore(argc, argv); break; } return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } static int message_received(struct nl_msg *msg, void *arg) { struct nlmsghdr *hdr = msg->nm_nlh; if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = nlmsg_data(hdr); if (err->error == 0) return NL_STOP; } return NL_OK; } static int valid_input(struct nl_msg *msg, void *arg) { struct nlattr *nla = nlmsg_attrdata(nlmsg_hdr(msg), sizeof(struct nfgenmsg)); struct nlattr *tb[NFACCT_NAME_MAX+1] = {}; char buf[4096]; int ret; ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(struct nfgenmsg), tb, NFACCT_MAX, NULL); if (ret < 0) { nfacct_perror("Can't parse message\n"); return ret; } ret = snprintf(buf, sizeof(buf), "{ pkts = %.20llu, bytes = %.20llu } = %s;", (unsigned long long)be64toh(nla_get_u64(tb[NFACCT_PKTS])), (unsigned long long)be64toh(nla_get_u64(tb[NFACCT_BYTES])), nla_get_string(tb[NFACCT_NAME])); printf("%s\n", buf); return 0; } static int nfacct_cmd_list(int argc, char *argv[]) { struct nl_msg *msg; struct nl_sock *handle; int zeroctr = 0; int ret, i; for (i=2; i 3) { nfacct_perror("too many arguments"); return -1; } return _nfacct_cmd_add(argv[2], 0, 0); } static int nfacct_cmd_delete(int argc, char *argv[]) { struct nl_msg *msg; struct nl_sock *handle; char nfname[NFACCT_NAME_MAX]; int ret; if (argc < 3) { nfacct_perror("missing object name"); return -1; } else if (argc > 3) { nfacct_perror("too many arguments"); return -1; } strncpy(nfname, argv[2], NFACCT_NAME_MAX); nfname[NFACCT_NAME_MAX-1] = '\0'; msg = nlmsg_alloc(); if (!msg) return -1; ret = nfnlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, NFNL_SUBSYS_ACCT, NFNL_MSG_ACCT_DEL, NLM_F_ACK | NLM_F_REQUEST, AF_UNSPEC, 0); if (ret) { NL_DBG(2, "Can't append payload to message: %s line: %d\n", __FUNCTION__, __LINE__); goto fail; } nla_put_string(msg, NFACCT_NAME, nfname); handle = nl_socket_alloc(); if ((ret = nfnl_connect(handle))) { NL_DBG(2, "Can't connect handle: %s line: %d\n", __FUNCTION__, __LINE__); goto fail; } if ((ret = nl_send_auto_complete(handle, msg)) < 0) { NL_DBG(2, "Can't send msg: %s line: %d\n", __FUNCTION__, __LINE__); goto fail_send; } ret = nl_recvmsgs_default(handle); if (ret < 0) { NL_DBG(2, "Can't receice msg: %s line: %d\n", __FUNCTION__, __LINE__); } fail_send: nl_close(handle); nl_socket_free(handle); fail: nlmsg_free(msg); return ret; return 0; } static int nfacct_cmd_get(int argc, char *argv[]) { struct nl_msg *msg; struct nl_sock *handle; struct nl_cb *cb; char nfname[NFACCT_NAME_MAX]; int zeroctr = 0; int ret, i; if (argc < 3) { nfacct_perror("missing object name"); return -1; } for (i=3; i 2) { nfacct_perror("too many arguments"); return -1; } msg = nlmsg_alloc(); if (!msg) return -1; ret = nfnlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, NFNL_SUBSYS_ACCT, NFNL_MSG_ACCT_DEL, NLM_F_ACK | NLM_F_REQUEST, AF_UNSPEC, 0); if (ret) { NL_DBG(2, "Can't append payload to message: %s line: %d\n", __FUNCTION__, __LINE__); goto fail; } handle = nl_socket_alloc(); if ((ret = nfnl_connect(handle))) { NL_DBG(2, "Can't connect handle: %s line: %d\n", __FUNCTION__, __LINE__); goto fail; } if ((ret = nl_send_auto_complete(handle, msg)) < 0) { NL_DBG(2, "Can't send msg: %s line: %d\n", __FUNCTION__, __LINE__); goto fail_send; } ret = nl_recvmsgs_default(handle); if (ret < 0) { NL_DBG(2, "Can't receice msg: %s line: %d\n", __FUNCTION__, __LINE__); } fail_send: nl_close(handle); nl_socket_free(handle); fail: nlmsg_free(msg); return ret; } static const char version_msg[] = "nfacct v%s: utility for the Netfilter extended accounting " "infrastructure\n" "Copyright (C) 2011 Pablo Neira Ayuso \n" "Copyright (C) 2011 Intra2net AG \n" "Copyright (C) 2013 Mathieu Poirier \n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it under " "certain \nconditions; see LICENSE file distributed in this package " "for details.\n"; static int nfacct_cmd_version(int argc, char *argv[]) { printf(version_msg, VERSION); return 0; } static const char help_msg[] = "nfacct v%s: utility for the Netfilter extended accounting " "infrastructure\n" "Usage: %s command [parameters]...\n\n" "Commands:\n" " list [reset]\t\tList the accounting object table (and reset)\n" " add object-name\tAdd new accounting object to table\n" " delete object-name\tDelete existing accounting object\n" " get object-name\tGet existing accounting object\n" " flush\t\t\tFlush accounting object table\n" " restore\t\tRestore accounting object table reading 'list' output from stdin\n" " version\t\tDisplay version and disclaimer\n" " help\t\t\tDisplay this help message\n"; static int nfacct_cmd_help(int argc, char *argv[]) { printf(help_msg, VERSION, argv[0]); return 0; } static int nfacct_cmd_restore(int argc, char *argv[]) { uint64_t pkts, bytes; char name[512]; char buffer[512]; int ret; while (fgets(buffer, sizeof(buffer), stdin)) { char *semicolon = strchr(buffer, ';'); if (semicolon == NULL) { nfacct_perror("invalid line"); return -1; } *semicolon = 0; ret = sscanf(buffer, "{ pkts = %llu, bytes = %llu } = %s", &pkts, &bytes, name); if (ret != 3) { nfacct_perror("error reading input"); return -1; } if ((ret = _nfacct_cmd_add(name, pkts, bytes)) != 0) return ret; } return 0; }