diff options
author | Mathieu Poirier <mathieu.poirier@linaro.org> | 2013-12-12 09:50:55 -0700 |
---|---|---|
committer | Mathieu Poirier <mathieu.poirier@linaro.org> | 2013-12-12 10:53:20 -0700 |
commit | c66949ff6d81fa3675f43a39b3148533447da53d (patch) | |
tree | b20fb116410807c9080d473b993f46039d0b6955 | |
download | nfacct-c66949ff6d81fa3675f43a39b3148533447da53d.tar.gz |
nfacct: Initial check-innougat-mr1-arc
This is a port of Plablo Niera Ayuso's nfacct tool from
libmnl to libnl. The behavior and usage have been kept
compliant with the original version.
Change-Id: I2f1d573226764d59494d393a905fcc54a46b7fac
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org.
-rw-r--r-- | Android.mk | 15 | ||||
-rw-r--r-- | CleanSpec.mk | 49 | ||||
-rw-r--r-- | nfacct.c | 600 |
3 files changed, 664 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..a72a64c --- /dev/null +++ b/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) + +INCLUDES = $(LOCAL_PATH) +INCLUDES += external/libnl/include +LIBS += -lnl-3 + +######################## +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= nfacct.c +LOCAL_MODULE := nfacct + +LOCAL_SHARED_LIBRARIES += libnl +LOCAL_C_INCLUDES := $(INCLUDES) + +include $(BUILD_EXECUTABLE) diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 0000000..b84e1b6 --- /dev/null +++ b/CleanSpec.mk @@ -0,0 +1,49 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/nfacct.c b/nfacct.c new file mode 100644 index 0000000..4ea3a9d --- /dev/null +++ b/nfacct.c @@ -0,0 +1,600 @@ +/* + * 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 <netlink-local.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[]); + +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_handle *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_handle_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_handle_destroy(handle); +fail: + nlmsg_free(msg); + return ret; +} + +static int _nfacct_cmd_add(char *name, int pkts, int bytes) +{ + struct nl_msg *msg; + struct nl_handle *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_handle_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_handle_destroy(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_handle *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_handle_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_handle_destroy(handle); +fail: + nlmsg_free(msg); + return ret; + return 0; +} + + +static int nfacct_cmd_get(int argc, char *argv[]) +{ + struct nl_msg *msg; + struct nl_handle *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_handle_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_handle_destroy(handle); +fail: + nlmsg_free(msg); + return ret; +} + +static int nfacct_cmd_flush(int argc, char *argv[]) +{ + struct nl_msg *msg; + struct nl_handle *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_handle_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_handle_destroy(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; +} |