From 3388d7df304f26617c4487418c05734ae4fce5b8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 28 Dec 2011 20:16:46 +0100 Subject: src: major API redesign This patch reworks the initial API. Now it provides functions to: - allocate/release accounting objects. - set/unset/get attributes of accounting objects. - build one netlink message from one accounting object. - parse one netlink message to one accounting object. - print one accounting object into a buffer. Binary layout of nfacct objects are opaque. This is good for extensibility without breaking backward compatibility. Signed-off-by: Pablo Neira Ayuso --- examples/nfacct-add.c | 22 ++- examples/nfacct-del.c | 67 +++++++ examples/nfacct-get.c | 34 +++- include/libnetfilter_acct/libnetfilter_acct.h | 36 +++- src/libnetfilter_acct.c | 257 ++++++++++++++++++-------- src/libnetfilter_acct.map | 19 +- 6 files changed, 333 insertions(+), 102 deletions(-) create mode 100644 examples/nfacct-del.c diff --git a/examples/nfacct-add.c b/examples/nfacct-add.c index 64f94d1..40e0e31 100644 --- a/examples/nfacct-add.c +++ b/examples/nfacct-add.c @@ -10,18 +10,28 @@ int main(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; - struct nfacct nfacct = {}; + struct nfacct *nfacct; int ret; if (argc != 2) { fprintf(stderr, "Usage: %s [name]\n", argv[0]); exit(EXIT_FAILURE); } - strncpy(nfacct.name, argv[1], NFACCT_NAME_MAX); - nfacct.name[NFACCT_NAME_MAX-1] = '\0'; - nlh = nfacct_add(buf, &nfacct); - seq = nlh->nlmsg_seq = time(NULL); + nfacct = nfacct_alloc(); + if (nfacct == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + nfacct_attr_set(nfacct, NFACCT_ATTR_NAME, argv[1]); + + seq = time(NULL); + nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_NEW, + NLM_F_CREATE | NLM_F_ACK, seq); + nfacct_nlmsg_build_payload(nlh, nfacct); + + nfacct_free(nfacct); nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) { @@ -42,7 +52,7 @@ int main(int argc, char *argv[]) ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, nfacct_list_cb, NULL); + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); if (ret <= 0) break; ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); diff --git a/examples/nfacct-del.c b/examples/nfacct-del.c new file mode 100644 index 0000000..7a1190c --- /dev/null +++ b/examples/nfacct-del.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfacct *nfacct; + int ret; + + if (argc != 2) { + fprintf(stderr, "Usage: %s [name]\n", argv[0]); + exit(EXIT_FAILURE); + } + + nfacct = nfacct_alloc(); + if (nfacct == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + nfacct_attr_set(nfacct, NFACCT_ATTR_NAME, argv[1]); + + seq = time(NULL); + nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_DEL, + NLM_F_ACK, seq); + nfacct_nlmsg_build_payload(nlh, nfacct); + + nfacct_free(nfacct); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + mnl_socket_close(nl); + + return EXIT_SUCCESS; +} diff --git a/examples/nfacct-get.c b/examples/nfacct-get.c index 7b22352..fffa3ec 100644 --- a/examples/nfacct-get.c +++ b/examples/nfacct-get.c @@ -4,6 +4,31 @@ #include #include +static int nfacct_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nfacct *nfacct; + char buf[4096]; + + nfacct = nfacct_alloc(); + if (nfacct == NULL) { + perror("OOM"); + goto err; + } + + if (nfacct_nlmsg_parse_payload(nlh, nfacct) < 0) { + perror("nfacct_parse_nl_msg"); + goto err_free; + } + + nfacct_snprintf(buf, sizeof(buf), nfacct, NFACCT_SNPRINTF_F_FULL); + printf("%s\n", buf); + +err_free: + nfacct_free(nfacct); +err: + return MNL_CB_OK; +} + int main(int argc, char *argv[]) { struct mnl_socket *nl; @@ -21,8 +46,11 @@ int main(int argc, char *argv[]) if (argc == 2 && strncmp(argv[1], "-z", strlen("-z")) == 0) zeroctr = true; - nlh = nfacct_list(buf, zeroctr); - seq = nlh->nlmsg_seq = time(NULL); + seq = time(NULL); + nlh = nfacct_nlmsg_build_hdr(buf, zeroctr ? + NFNL_MSG_ACCT_GET_CTRZERO : + NFNL_MSG_ACCT_GET, + NLM_F_DUMP, seq); nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) { @@ -43,7 +71,7 @@ int main(int argc, char *argv[]) ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, nfacct_list_cb, &full); + ret = mnl_cb_run(buf, ret, seq, portid, nfacct_cb, &full); if (ret <= 0) break; ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); diff --git a/include/libnetfilter_acct/libnetfilter_acct.h b/include/libnetfilter_acct/libnetfilter_acct.h index f5b0040..d877407 100644 --- a/include/libnetfilter_acct/libnetfilter_acct.h +++ b/include/libnetfilter_acct/libnetfilter_acct.h @@ -4,16 +4,34 @@ #include #include -struct nfacct { - char name[NFACCT_NAME_MAX]; - uint64_t pkts; - uint64_t bytes; +struct nfacct; + +enum nfacct_attr_type { + NFACCT_ATTR_NAME = 0, + NFACCT_ATTR_PKTS, + NFACCT_ATTR_BYTES, }; -struct nlmsghdr *nfacct_add(char *buf, struct nfacct *nfacct); -struct nlmsghdr *nfacct_list(char *buf, bool ctrzero); -int nfacct_list_cb(const struct nlmsghdr *nlh, void *data); -struct nlmsghdr *nfacct_flush(char *buf); -struct nlmsghdr *nfacct_delete(char *buf, const char *filter_name); +struct nfacct *nfacct_alloc(void); +void nfacct_free(struct nfacct *nfacct); + +void nfacct_attr_set(struct nfacct *nfacct, enum nfacct_attr_type type, const void *data); +void nfacct_attr_set_str(struct nfacct *nfacct, enum nfacct_attr_type type, const char *name); +void nfacct_attr_set_u64(struct nfacct *nfacct, enum nfacct_attr_type type, uint64_t value); +void nfacct_attr_unset(struct nfacct *nfacct, enum nfacct_attr_type type); + +const void *nfacct_attr_get(struct nfacct *nfacct, enum nfacct_attr_type type); +const char *nfacct_attr_get_str(struct nfacct *nfacct, enum nfacct_attr_type type); +uint64_t nfacct_attr_get_u64(struct nfacct *nfacct, enum nfacct_attr_type type); + +struct nlmsghdr; + +struct nlmsghdr *nfacct_nlmsg_build_hdr(char *buf, uint8_t cmd, uint16_t flags, uint32_t seq); +void nfacct_nlmsg_build_payload(struct nlmsghdr *nlh, struct nfacct *nfacct); +int nfacct_nlmsg_parse_payload(const struct nlmsghdr *nlh, struct nfacct *nfacct); + +#define NFACCT_SNPRINTF_F_FULL (1 << 0) + +int nfacct_snprintf(char *buf, size_t size, struct nfacct *nfacct, unsigned int flags); #endif diff --git a/src/libnetfilter_acct.c b/src/libnetfilter_acct.c index 777c960..86d7b96 100644 --- a/src/libnetfilter_acct.c +++ b/src/libnetfilter_acct.c @@ -11,48 +11,159 @@ #include #include +#include +#include + #include #include #include #include -struct nlmsghdr *nfacct_add(char *buf, struct nfacct *nfacct) +struct nfacct { + char name[NFACCT_NAME_MAX]; + uint64_t pkts; + uint64_t bytes; + uint32_t bitset; +}; + +struct nfacct *nfacct_alloc(void) { - struct nlmsghdr *nlh; - struct nfgenmsg *nfh; + return calloc(1, sizeof(struct nfacct)); +} +EXPORT_SYMBOL(nfacct_alloc); - nlh = mnl_nlmsg_put_header(buf); - nlh->nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_NEW; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; - nlh->nlmsg_seq = time(NULL); +void nfacct_free(struct nfacct *nfacct) +{ + free(nfacct); +} +EXPORT_SYMBOL(nfacct_free); - nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); - nfh->nfgen_family = AF_UNSPEC; - nfh->version = NFNETLINK_V0; - nfh->res_id = 0; +void +nfacct_attr_set(struct nfacct *nfacct, enum nfacct_attr_type type, + const void *data) +{ + switch(type) { + case NFACCT_ATTR_NAME: + strncpy(nfacct->name, data, NFACCT_NAME_MAX); + nfacct->name[NFACCT_NAME_MAX-1] = '\0'; + nfacct->bitset |= (1 << NFACCT_ATTR_NAME); + break; + case NFACCT_ATTR_PKTS: + nfacct->bytes = *((uint64_t *) data); + nfacct->bitset |= (1 << NFACCT_ATTR_PKTS); + break; + case NFACCT_ATTR_BYTES: + nfacct->pkts = *((uint64_t *) data); + nfacct->bitset |= (1 << NFACCT_ATTR_BYTES); + break; + } +} +EXPORT_SYMBOL(nfacct_attr_set); - mnl_attr_put_strz(nlh, NFACCT_NAME, nfacct->name); - mnl_attr_put_u64(nlh, NFACCT_PKTS, htobe64(nfacct->pkts)); - mnl_attr_put_u64(nlh, NFACCT_PKTS, htobe64(nfacct->bytes)); +void +nfacct_attr_set_str(struct nfacct *nfacct, enum nfacct_attr_type type, + const char *name) +{ + nfacct_attr_set(nfacct, type, name); +} +EXPORT_SYMBOL(nfacct_attr_set_str); - return nlh; +void +nfacct_attr_set_u64(struct nfacct *nfacct, enum nfacct_attr_type type, + uint64_t value) +{ + nfacct_attr_set(nfacct, type, &value); } -EXPORT_SYMBOL(nfacct_add); +EXPORT_SYMBOL(nfacct_attr_set_u64); -struct nlmsghdr *nfacct_list(char *buf, bool ctrzero) +void +nfacct_attr_unset(struct nfacct *nfacct, enum nfacct_attr_type type) +{ + switch(type) { + case NFACCT_ATTR_NAME: + nfacct->bitset &= ~(1 << NFACCT_ATTR_NAME); + break; + case NFACCT_ATTR_PKTS: + nfacct->bitset &= ~(1 << NFACCT_ATTR_PKTS); + break; + case NFACCT_ATTR_BYTES: + nfacct->bitset &= ~(1 << NFACCT_ATTR_BYTES); + break; + } +} +EXPORT_SYMBOL(nfacct_attr_unset); + +const void *nfacct_attr_get(struct nfacct *nfacct, enum nfacct_attr_type type) +{ + const void *ret = NULL; + + switch(type) { + case NFACCT_ATTR_NAME: + ret = nfacct->name; + break; + case NFACCT_ATTR_PKTS: + ret = &nfacct->pkts; + break; + case NFACCT_ATTR_BYTES: + ret = &nfacct->bytes; + break; + } + return ret; +} +EXPORT_SYMBOL(nfacct_attr_get); + +const char * +nfacct_attr_get_str(struct nfacct *nfacct, enum nfacct_attr_type type) +{ + return (char *)nfacct_attr_get(nfacct, type); +} +EXPORT_SYMBOL(nfacct_attr_get_str); + +uint64_t nfacct_attr_get_u64(struct nfacct *nfacct, enum nfacct_attr_type type) +{ + return *((uint64_t *)nfacct_attr_get(nfacct, type)); +} +EXPORT_SYMBOL(nfacct_attr_get_u64); + +/** + * nfacct_nlmsg_build_hdr - build netlink message header for nfacct subsystem + * @buf: buffer where this function outputs the netlink message. + * @cmd: nfacct nfnetlink command. + * @flags: netlink flags. + * @seq: sequence number for this message. + * + * Possible commands: + * - NFNL_MSG_ACCT_NEW: new accounting object. + * - NFNL_MSG_ACCT_GET: get accounting object. + * - NFNL_MSG_ACCT_GET_CTRZERO: get accounting object and atomically reset. + * + * Examples: + * - Command NFNL_MSG_ACCT_NEW + flags NLM_F_CREATE | NLM_F_ACK, to create + * one new accounting object (if it does not already exists). You receive + * one acknoledgment in any case with the result of the operation. + * + * - Command NFNL_MSG_ACCT_GET + flags NLM_F_DUMP, to obtain all the + * existing accounting objects. + * + * - Command NFNL_MSG_ACCT_GET_CTRZERO + flags NLM_F_DUMP, to atomically + * obtain all the existing accounting objects and reset them. + * + * - Command NFNL_MSG_ACCT_DEL, to delete all existing unused objects. + * + * - Command NFNL_MSG_ACCT_DEL, to delete one specific nfacct object (if + * unused, otherwise you hit EBUSY). + */ +struct nlmsghdr * +nfacct_nlmsg_build_hdr(char *buf, uint8_t cmd, uint16_t flags, uint32_t seq) { struct nlmsghdr *nlh; struct nfgenmsg *nfh; - uint32_t msg_type = NFNL_MSG_ACCT_GET; - - if (ctrzero) - msg_type = NFNL_MSG_ACCT_GET_CTRZERO; nlh = mnl_nlmsg_put_header(buf); - nlh->nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | msg_type; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - nlh->nlmsg_seq = time(NULL); + nlh->nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | cmd; + nlh->nlmsg_flags = NLM_F_REQUEST | flags; + nlh->nlmsg_seq = seq; nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); nfh->nfgen_family = AF_UNSPEC; @@ -61,9 +172,22 @@ struct nlmsghdr *nfacct_list(char *buf, bool ctrzero) return nlh; } -EXPORT_SYMBOL(nfacct_list); +EXPORT_SYMBOL(nfacct_nlmsg_build_hdr); + +void nfacct_nlmsg_build_payload(struct nlmsghdr *nlh, struct nfacct *nfacct) +{ + if (nfacct->name) + mnl_attr_put_strz(nlh, NFACCT_NAME, nfacct->name); + + if (nfacct->pkts) + mnl_attr_put_u64(nlh, NFACCT_PKTS, htobe64(nfacct->pkts)); + + if (nfacct->bytes) + mnl_attr_put_u64(nlh, NFACCT_PKTS, htobe64(nfacct->bytes)); +} +EXPORT_SYMBOL(nfacct_nlmsg_build_payload); -static int nfacct_list_attr_cb(const struct nlattr *attr, void *data) +static int nfacct_nlmsg_parse_attr_cb(const struct nlattr *attr, void *data) { const struct nlattr **tb = data; int type = mnl_attr_get_type(attr); @@ -95,67 +219,44 @@ static int nfacct_list_attr_cb(const struct nlattr *attr, void *data) return MNL_CB_OK; } -int nfacct_list_cb(const struct nlmsghdr *nlh, void *data) +int +nfacct_nlmsg_parse_payload(const struct nlmsghdr *nlh, struct nfacct *nfacct) { struct nlattr *tb[NFACCT_MAX+1] = {}; struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); - bool *full = data; - mnl_attr_parse(nlh, sizeof(*nfg), nfacct_list_attr_cb, tb); + mnl_attr_parse(nlh, sizeof(*nfg), nfacct_nlmsg_parse_attr_cb, tb); if (!tb[NFACCT_NAME] && !tb[NFACCT_PKTS] && !tb[NFACCT_BYTES]) - return MNL_CB_OK; + return -1; - if (full) { - printf("%s = { pkts = %.12llu,\tbytes = %.12llu }; \n", - mnl_attr_get_str(tb[NFACCT_NAME]), - (unsigned long long) - be64toh(mnl_attr_get_u64(tb[NFACCT_PKTS])), - (unsigned long long) - be64toh(mnl_attr_get_u64(tb[NFACCT_BYTES]))); - } else { - printf("%s\n", mnl_attr_get_str(tb[NFACCT_NAME])); - } + nfacct_attr_set_str(nfacct, NFACCT_ATTR_NAME, + mnl_attr_get_str(tb[NFACCT_NAME])); + nfacct_attr_set_u64(nfacct, NFACCT_ATTR_PKTS, + be64toh(mnl_attr_get_u64(tb[NFACCT_PKTS]))); + nfacct_attr_set_u64(nfacct, NFACCT_ATTR_BYTES, + be64toh(mnl_attr_get_u64(tb[NFACCT_BYTES]))); - return MNL_CB_OK; + return 0; } -EXPORT_SYMBOL(nfacct_list_cb); +EXPORT_SYMBOL(nfacct_nlmsg_parse_payload); -struct nlmsghdr *nfacct_flush(char *buf) +int nfacct_snprintf(char *buf, size_t size, struct nfacct *nfacct, + unsigned int flags) { - struct nlmsghdr *nlh; - struct nfgenmsg *nfh; - - nlh = mnl_nlmsg_put_header(buf); - nlh->nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_DEL; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; - nlh->nlmsg_seq = time(NULL); + int ret; - nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); - nfh->nfgen_family = AF_UNSPEC; - nfh->version = NFNETLINK_V0; - nfh->res_id = 0; - - return nlh; -} -EXPORT_SYMBOL(nfacct_flush); - -struct nlmsghdr *nfacct_delete(char *buf, const char *filter_name) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfh; - - nlh = mnl_nlmsg_put_header(buf); - nlh->nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | NFNL_MSG_ACCT_DEL; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - nlh->nlmsg_seq = time(NULL); - - nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); - nfh->nfgen_family = AF_UNSPEC; - nfh->version = NFNETLINK_V0; - nfh->res_id = 0; - - mnl_attr_put_strz(nlh, NFACCT_NAME, filter_name); - - return nlh; + if (flags & NFACCT_SNPRINTF_F_FULL) { + ret = snprintf(buf, size, + "%s = { pkts = %.12llu,\tbytes = %.12llu };", + nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME), + (unsigned long long) + nfacct_attr_get_u64(nfacct, NFACCT_ATTR_BYTES), + (unsigned long long) + nfacct_attr_get_u64(nfacct, NFACCT_ATTR_PKTS)); + } else { + ret = snprintf(buf, size, "%s\n", + nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME)); + } + return ret; } -EXPORT_SYMBOL(nfacct_delete); +EXPORT_SYMBOL(nfacct_snprintf); diff --git a/src/libnetfilter_acct.map b/src/libnetfilter_acct.map index f5c3172..2a56eba 100644 --- a/src/libnetfilter_acct.map +++ b/src/libnetfilter_acct.map @@ -1,11 +1,18 @@ LIBNETFILTER_ACCT_1.0 { global: - nfacct_add; - nfacct_list; - nfacct_list_cb; - nfacct_flush; - nfacct_delete; - nfacct_list; + nfacct_alloc; + nfacct_free; + nfacct_attr_set; + nfacct_attr_set_str; + nfacct_attr_set_u64; + nfacct_attr_unset; + nfacct_attr_get; + nfacct_attr_get_str; + nfacct_attr_get_u64; + nfacct_nlmsg_build_hdr; + nfacct_nlmsg_build_payload; + nfacct_nlmsg_parse_payload; + nfacct_snprintf; local: *; }; -- cgit v1.2.3