/* * Original implementation on libmnl: * (C) 2011 by Pablo Neira Ayuso <pablo@netfilter.org> * (C) 2011 by Intra2net AG <http://www.intra2net.com> * * Port to libnl: * (C) 2013 by Mathieu J. Poirier <mathieu.poirier@linaro.org> * * 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 <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <dirent.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <errno.h> #include <endian.h> #include <netlink-private/object-api.h> #include <netlink-private/types.h> #include <linux/netlink.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_acct.h> #include <netlink/netfilter/nfnl.h> #include <netlink/netlink.h> #include <netlink/socket.h> #include <netlink/msg.h> #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 #define NL_DBG(LVL,FMT,ARG...) do { } while(0) 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<argc; i++) { if (strncmp(argv[i], "reset", strlen(argv[i])) == 0) { zeroctr = 1; } else if (strncmp(argv[i], "xml", strlen(argv[i])) == 0) { nfacct_perror("xml feature not implemented"); return -1; } else { nfacct_perror("unknown argument"); return -1; } } msg = nlmsg_alloc(); if (!msg) return -1; ret = nfnlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, NFNL_SUBSYS_ACCT, zeroctr ? NFNL_MSG_ACCT_GET_CTRZERO : NFNL_MSG_ACCT_GET, NLM_F_DUMP | 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; } nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM, valid_input, NULL); 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 int _nfacct_cmd_add(char *name, int pkts, int bytes) { struct nl_msg *msg; struct nl_sock *handle; char nfname[NFACCT_NAME_MAX]; int ret; strncpy(nfname, name, 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_NEW, NLM_F_CREATE | 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); nla_put_u64(msg, NFACCT_PKTS, htobe64(pkts)); nla_put_u64(msg, NFACCT_BYTES, htobe64(bytes)); 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 int nfacct_cmd_add(int argc, char *argv[]) { if (argc < 3) { nfacct_perror("missing object name"); return -1; } else if (argc > 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<argc; i++) { if (strncmp(argv[i], "reset", strlen(argv[i])) == 0) { zeroctr = 1; } else if (strncmp(argv[i], "xml", strlen(argv[i])) == 0) { nfacct_perror("xml feature not implemented"); return -1; } else { nfacct_perror("unknown argument"); 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, zeroctr ? NFNL_MSG_ACCT_GET_CTRZERO : NFNL_MSG_ACCT_GET, 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 (handle) { cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) goto fail; if (nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, message_received, NULL) < 0) goto fail; nl_socket_set_cb(handle,cb); } else { goto fail; } 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; } nl_socket_modify_cb(handle, NL_CB_VALID, NL_CB_CUSTOM, valid_input, NULL); 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 int nfacct_cmd_flush(int argc, char *argv[]) { struct nl_msg *msg; struct nl_sock *handle; int ret; if (argc > 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 <pablo@netfilter.org>\n" "Copyright (C) 2011 Intra2net AG <http://www.intra2net.com>\n" "Copyright (C) 2013 Mathieu Poirier <mathieu.poirier@linaro.org>\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; }