From: Ana Rey <ana@xxxxxxxxx> This adds userspace support to acct: support to accounting objects and acct expresion. Moreover, this adds some examples to add/delete/get/reset the accounter object and add rules using an existing acct. Example of how to use those examples: * Add a new acct: # ./examples/nft-acct-add ip test acct1 # ./examples/nft-acct-get ip test acct1 default table test family ip acct acct1 packet 0 bytes 0 * Delete an acct: # ./examples/nft-acct-del ip test acct1 * Add a rule using the acct expresion. # ./examples/nft-rule-add ip test output * Reset the acct: # ./examples/nft-acct-reset ip test acct1 The kernel support is added in the commit: netfilter: acct: add support to accounters in nftables Signed-off-by: Ana Rey Botello <ana@xxxxxxxxx> --- examples/Makefile.am | 23 +- examples/nft-acct-add.c | 136 ++++++++ examples/nft-acct-del.c | 133 ++++++++ examples/nft-acct-get.c | 135 ++++++++ examples/nft-acct-reset.c | 121 +++++++ examples/nft-rule-acct-add.c | 220 +++++++++++++ examples/nft-rule-get.c | 1 + include/buffer.h | 1 + include/libnftnl/Makefile.am | 3 +- include/libnftnl/acct.h | 87 +++++ include/libnftnl/expr.h | 3 + include/linux/netfilter/nf_tables.h | 41 +++ src/Makefile.am | 2 + src/acct.c | 612 +++++++++++++++++++++++++++++++++++ src/expr/acct.c | 201 ++++++++++++ src/libnftnl.map | 30 ++ 16 files changed, 1747 insertions(+), 2 deletions(-) create mode 100644 examples/nft-acct-add.c create mode 100644 examples/nft-acct-del.c create mode 100644 examples/nft-acct-get.c create mode 100644 examples/nft-acct-reset.c create mode 100644 examples/nft-rule-acct-add.c create mode 100644 include/libnftnl/acct.h create mode 100644 src/acct.c create mode 100644 src/expr/acct.c diff --git a/examples/Makefile.am b/examples/Makefile.am index fafcb76..d83c2b9 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -22,7 +22,13 @@ check_PROGRAMS = nft-table-add \ nft-set-elem-get \ nft-set-elem-del \ nft-ruleset-get \ - nft-compat-get + nft-compat-get \ + nft-acct-add \ + nft-acct-get \ + nft-acct-reset \ + nft-rule-acct-add \ + nft-acct-del + nft_table_add_SOURCES = nft-table-add.c nft_table_add_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} @@ -92,3 +98,18 @@ nft_ruleset_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} nft_compat_get_SOURCES = nft-compat-get.c nft_compat_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} + +nft_acct_add_SOURCES = nft-acct-add.c +nft_acct_add_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} + +nft_acct_get_SOURCES = nft-acct-get.c +nft_acct_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} + +nft_acct_reset_SOURCES = nft-acct-reset.c +nft_acct_reset_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} + +nft_acct_del_SOURCES = nft-acct-del.c +nft_acct_del_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} + +nft_rule_acct_add_SOURCES = nft-rule-acct-add.c +nft_rule_acct_add_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} diff --git a/examples/nft-acct-add.c b/examples/nft-acct-add.c new file mode 100644 index 0000000..2598947 --- /dev/null +++ b/examples/nft-acct-add.c @@ -0,0 +1,136 @@ +/* + * (C) 2014 by Ana Rey Botello <ana@xxxxxxxxx> + * + * 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 <stdlib.h> +#include <time.h> +#include <string.h> +#include <netinet/in.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/table.h> +#include <libnftnl/acct.h> + +static struct nft_acct *acct_add_parse(int argc, char *argv[]) +{ + struct nft_acct *acct; + uint16_t family; + + if (strcmp(argv[1], "ip") == 0) + family = NFPROTO_IPV4; + else if (strcmp(argv[1], "ip6") == 0) + family = NFPROTO_IPV6; + else if (strcmp(argv[1], "bridge") == 0) + family = NFPROTO_BRIDGE; + else if (strcmp(argv[1], "arp") == 0) + family = NFPROTO_ARP; + else { + fprintf(stderr, "Unknown family: ip, ip6, bridge, arp\n"); + return NULL; + } + + acct = nft_acct_alloc(); + if (acct == NULL) { + perror("OOM"); + return NULL; + } + + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_NAME, argv[3]); + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_TABLE, argv[2]); + nft_acct_attr_set_u32(acct, NFT_ACCT_ATTR_FAMILY, family); + + return acct; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, acct_seq, family; + struct nft_acct *acct; + struct mnl_nlmsg_batch *batch; + int ret, batching; + + if (argc != 4) { + fprintf(stderr, "%s <family> <table> <acct>\n", argv[0]); + exit(EXIT_FAILURE); + } + + acct = acct_add_parse(argc, argv); + if (acct == NULL) + exit(EXIT_FAILURE); + + + 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); + } + + batching = nft_batch_is_supported(); + if (batching < 0) { + perror("cannot talk to nfnetlink"); + exit(EXIT_FAILURE); + } + + seq = time(NULL); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + if (batching) { + nft_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + } + acct_seq = seq; + family = nft_acct_attr_get_u32(acct, NFT_ACCT_ATTR_FAMILY); + nlh = nft_acct_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWACCT, family, + NLM_F_CREATE|NLM_F_ACK, seq++); + + nft_acct_nlmsg_build_payload(nlh, acct); + nft_acct_free(acct); + mnl_nlmsg_batch_next(batch); + + if (batching) { + nft_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + } + + ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch)); + if (ret == -1) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + mnl_nlmsg_batch_stop(batch); + portid = mnl_socket_get_portid(nl); + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, acct_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/nft-acct-del.c b/examples/nft-acct-del.c new file mode 100644 index 0000000..2b88b9a --- /dev/null +++ b/examples/nft-acct-del.c @@ -0,0 +1,133 @@ +/* + * (C) 2014 by Ana Rey Botello <ana@xxxxxxxxx> + * + * 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 <stdlib.h> +#include <time.h> +#include <string.h> +#include <netinet/in.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/acct.h> + +static struct nft_acct *acct_del_parse(int argc, char *argv[]) +{ + struct nft_acct *acct; + uint16_t family; + + if (strcmp(argv[1], "ip") == 0) + family = NFPROTO_IPV4; + else if (strcmp(argv[1], "ip6") == 0) + family = NFPROTO_IPV6; + else if (strcmp(argv[1], "bridge") == 0) + family = NFPROTO_BRIDGE; + else if (strcmp(argv[1], "arp") == 0) + family = NFPROTO_ARP; + else { + fprintf(stderr, "Unknown family: ip, ip6, bridge, arp\n"); + return NULL; + } + + acct = nft_acct_alloc(); + if (acct == NULL) { + perror("OOM"); + return NULL; + } + + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_TABLE, argv[2]); + nft_acct_attr_set_u32(acct, NFT_ACCT_ATTR_FAMILY, family); + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_NAME, argv[3]); + + return acct; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, table_seq, family; + struct nft_acct *acct; + struct mnl_nlmsg_batch *batch; + int ret, batching; + + if (argc != 4) { + fprintf(stderr, "%s <family> <table> <acct>\n", argv[0]); + exit(EXIT_FAILURE); + } + + acct = acct_del_parse(argc, argv); + if (acct == NULL) + exit(EXIT_FAILURE); + + batching = nft_batch_is_supported(); + if (batching < 0) { + perror("cannot talk to nfnetlink"); + exit(EXIT_FAILURE); + } + + seq = time(NULL); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + if (batching) { + nft_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + } + + table_seq = seq; + family = nft_acct_attr_get_u32(acct, NFT_ACCT_ATTR_FAMILY); + nlh = nft_acct_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_DELACCT, family, + NLM_F_ACK, seq++); + nft_acct_nlmsg_build_payload(nlh, acct); + mnl_nlmsg_batch_next(batch); + nft_acct_free(acct); + + if (batching) { + nft_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + } + + 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, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch)) < 0) { + perror("mnl_socket_send"); + exit(EXIT_FAILURE); + } + + mnl_nlmsg_batch_stop(batch); + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, table_seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + printf("error\n"); + exit(EXIT_FAILURE); + } + mnl_socket_close(nl); + + return EXIT_SUCCESS; +} diff --git a/examples/nft-acct-get.c b/examples/nft-acct-get.c new file mode 100644 index 0000000..4c9c5bc --- /dev/null +++ b/examples/nft-acct-get.c @@ -0,0 +1,135 @@ +/* + * (C) 2014 by Ana Rey Botello <ana@xxxxxxxxx> + * + * 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 <stdlib.h> +#include <time.h> +#include <string.h> +#include <netinet/in.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/acct.h> + +static int acct_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_acct *acct; + char buf[4096]; + uint32_t *type = data; + + acct = nft_acct_alloc(); + if (acct == NULL) { + perror("OOM"); + goto err; + } + + if (nft_acct_nlmsg_parse(nlh, acct) < 0) { + perror("nft_acct_nlmsg_parse"); + goto err_free; + } + nft_acct_snprintf(buf, sizeof(buf), acct, *type, 0); + printf("%s\n", buf); + +err_free: + nft_acct_free(acct); +err: + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, family; + struct nft_acct *acct = NULL; + int ret; + uint32_t type = NFT_OUTPUT_DEFAULT; + + if (argc != 5) { + fprintf(stderr, + "%s <family> <table> <acct> [<default|xml|json>]\n", + argv[0]); + return EXIT_FAILURE; + } + + if (strcmp(argv[1], "ip") == 0) + family = NFPROTO_IPV4; + else if (strcmp(argv[1], "ip6") == 0) + family = NFPROTO_IPV6; + else if (strcmp(argv[1], "bridge") == 0) + family = NFPROTO_BRIDGE; + else if (strcmp(argv[1], "arp") == 0) + family = NFPROTO_ARP; + else if (strcmp(argv[1], "unspec") == 0) + family = NFPROTO_UNSPEC; + else { + fprintf(stderr, + "Unknown family: ip, ip6, bridge, arp, unspec\n"); + exit(EXIT_FAILURE); + } + if (strcmp(argv[argc-1], "xml") == 0) { + type = NFT_OUTPUT_XML; + argv[argc-1] = NULL; + argc--; + } else if (strcmp(argv[argc-1], "json") == 0) { + type = NFT_OUTPUT_JSON; + argv[argc-1] = NULL; + argc--; + } else if (strcmp(argv[argc - 1], "default") == 0) { + argc--; + } + acct = nft_acct_alloc(); + if (acct == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + seq = time(NULL); + nlh = nft_acct_nlmsg_build_hdr(buf, NFT_MSG_GETACCT, family, NLM_F_ACK, + seq); + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_NAME, argv[3]); + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_TABLE, argv[2]); + nft_acct_nlmsg_build_payload(nlh, acct); + nft_acct_free(acct); + + 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, acct_cb, &type); + 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/nft-acct-reset.c b/examples/nft-acct-reset.c new file mode 100644 index 0000000..9bcba31 --- /dev/null +++ b/examples/nft-acct-reset.c @@ -0,0 +1,121 @@ +/* + * (C) 2014 by Ana Rey Botello <ana@xxxxxxxxx> + * + * 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 <stdlib.h> +#include <time.h> +#include <string.h> +#include <netinet/in.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/acct.h> + +static int acct_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_acct *acct; + + acct = nft_acct_alloc(); + if (acct == NULL) { + perror("OOM"); + goto err; + } + + if (nft_acct_nlmsg_parse(nlh, acct) < 0) { + perror("nft_acct_nlmsg_parse"); + goto err_free; + } +err_free: + nft_acct_free(acct); +err: + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, family; + struct nft_acct *acct = NULL; + int ret; + uint32_t type = NFT_OUTPUT_DEFAULT; + + if (argc != 4) { + fprintf(stderr, + "%s <family> <table> <acct>\n", + argv[0]); + return EXIT_FAILURE; + } + + if (strcmp(argv[1], "ip") == 0) + family = NFPROTO_IPV4; + else if (strcmp(argv[1], "ip6") == 0) + family = NFPROTO_IPV6; + else if (strcmp(argv[1], "bridge") == 0) + family = NFPROTO_BRIDGE; + else if (strcmp(argv[1], "arp") == 0) + family = NFPROTO_ARP; + else if (strcmp(argv[1], "unspec") == 0) + family = NFPROTO_UNSPEC; + else { + fprintf(stderr, + "Unknown family: ip, ip6, bridge, arp, unspec\n"); + exit(EXIT_FAILURE); + } + + acct = nft_acct_alloc(); + if (acct == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + seq = time(NULL); + nlh = nft_acct_nlmsg_build_hdr(buf, NFT_MSG_GETACCT_ZERO, family, + NLM_F_ACK, seq); + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_NAME, argv[3]); + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_TABLE, argv[2]); + nft_acct_nlmsg_build_payload(nlh, acct); + nft_acct_free(acct); + + 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, acct_cb, &type); + 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/nft-rule-acct-add.c b/examples/nft-rule-acct-add.c new file mode 100644 index 0000000..2214a21 --- /dev/null +++ b/examples/nft-rule-acct-add.c @@ -0,0 +1,220 @@ +/* + * (C) 2014 by Ana Rey Botello <ana@xxxxxxxxx> + * + * 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 <stdlib.h> +#include <time.h> +#include <string.h> +#include <stddef.h> /* for offsetof */ +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/rule.h> +#include <libnftnl/expr.h> + +static void add_payload(struct nft_rule *r, uint32_t base, uint32_t dreg, + uint32_t offset, uint32_t len) +{ + struct nft_rule_expr *e; + + e = nft_rule_expr_alloc("payload"); + if (e == NULL) { + perror("expr payload oom"); + exit(EXIT_FAILURE); + } + + nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_BASE, base); + nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_DREG, dreg); + nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_OFFSET, offset); + nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_LEN, len); + + nft_rule_add_expr(r, e); +} + +static void add_cmp(struct nft_rule *r, uint32_t sreg, uint32_t op, + const void *data, uint32_t data_len) +{ + struct nft_rule_expr *e; + + e = nft_rule_expr_alloc("cmp"); + if (e == NULL) { + perror("expr cmp oom"); + exit(EXIT_FAILURE); + } + + nft_rule_expr_set_u32(e, NFT_EXPR_CMP_SREG, sreg); + nft_rule_expr_set_u32(e, NFT_EXPR_CMP_OP, op); + nft_rule_expr_set(e, NFT_EXPR_CMP_DATA, data, data_len); + nft_rule_add_expr(r, e); +} + +static void add_acct(struct nft_rule *r) +{ + struct nft_rule_expr *e; + + e = nft_rule_expr_alloc("acct"); + if (e == NULL) { + perror("expr acct oom"); + exit(EXIT_FAILURE); + } + nft_rule_expr_set(e, NFT_EXPR_ACCT_NAME, "acct1", sizeof("acct1")); + nft_rule_expr_set_str(e, NFT_EXPR_ACCT_NAME, "acct1"); + + nft_rule_add_expr(r, e); +} + +static struct nft_rule *setup_rule(uint8_t family, const char *table, + const char *chain, const char *handle) +{ + struct nft_rule *r = NULL; + uint8_t proto; + uint16_t dport; + uint64_t handle_num; + + r = nft_rule_alloc(); + if (r == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + + nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, table); + nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, chain); + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, family); + if (handle != NULL) { + handle_num = atoll(handle); + nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle_num); + } + + proto = IPPROTO_TCP; + add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, + offsetof(struct iphdr, protocol), sizeof(uint8_t)); + add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t)); + + dport = htons(80); + add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, + offsetof(struct tcphdr, dest), sizeof(uint16_t)); + add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t)); + + add_acct(r); + return r; +} + +static void nft_mnl_batch_put(char *buf, uint16_t type, uint32_t seq) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfg; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_seq = seq; + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = NFNL_SUBSYS_NFTABLES; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + struct nft_rule *r; + struct nlmsghdr *nlh; + struct mnl_nlmsg_batch *batch; + uint8_t family; + char buf[MNL_SOCKET_BUFFER_SIZE]; + char buf2[MNL_SOCKET_BUFFER_SIZE]; + uint32_t seq = time(NULL); + int ret; + + if (argc < 4 || argc > 5) { + fprintf(stderr, + "Usage: %s <family> <table> <chain>\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (strcmp(argv[1], "ip") == 0) + family = NFPROTO_IPV4; + else if (strcmp(argv[1], "ip6") == 0) + family = NFPROTO_IPV6; + else { + fprintf(stderr, "Unknown family: ip, ip6\n"); + exit(EXIT_FAILURE); + } + + if (argc != 5) + r = setup_rule(family, argv[2], argv[3], NULL); + else + r = setup_rule(family, argv[2], argv[3], argv[4]); + + 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); + } + + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + nft_mnl_batch_put(mnl_nlmsg_batch_current(batch), + NFNL_MSG_BATCH_BEGIN, seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nft_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWRULE, + nft_rule_attr_get_u32(r, NFT_RULE_ATTR_FAMILY), + NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); + + nft_rule_nlmsg_build_payload(nlh, r); + nft_rule_snprintf(buf2, sizeof(buf), r, NFT_OUTPUT_JSON, 0); + nft_rule_free(r); + mnl_nlmsg_batch_next(batch); + + nft_mnl_batch_put(mnl_nlmsg_batch_current(batch), NFNL_MSG_BATCH_END, + seq++); + mnl_nlmsg_batch_next(batch); + + ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch)); + if (ret == -1) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + mnl_nlmsg_batch_stop(batch); + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), NULL, NULL); + if (ret < 0) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + + mnl_socket_close(nl); + return EXIT_SUCCESS; +} diff --git a/examples/nft-rule-get.c b/examples/nft-rule-get.c index 5803143..5467d7e 100644 --- a/examples/nft-rule-get.c +++ b/examples/nft-rule-get.c @@ -19,6 +19,7 @@ #include <libmnl/libmnl.h> #include <libnftnl/rule.h> +#include <libnftnl/expr.h> static int table_cb(const struct nlmsghdr *nlh, void *data) { diff --git a/include/buffer.h b/include/buffer.h index 2b497f2..d8aa5c0 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -33,6 +33,7 @@ int nft_buf_str(struct nft_buf *b, int type, const char *str, const char *tag); int nft_buf_reg(struct nft_buf *b, int type, union nft_data_reg *reg, int reg_type, const char *tag); +#define ACCT "acct" #define BASE "base" #define BYTES "bytes" #define CHAIN "chain" diff --git a/include/libnftnl/Makefile.am b/include/libnftnl/Makefile.am index 010c01f..2381589 100644 --- a/include/libnftnl/Makefile.am +++ b/include/libnftnl/Makefile.am @@ -5,4 +5,5 @@ pkginclude_HEADERS = table.h \ set.h \ ruleset.h \ common.h \ - gen.h + gen.h \ + acct.h diff --git a/include/libnftnl/acct.h b/include/libnftnl/acct.h new file mode 100644 index 0000000..af5dc1d --- /dev/null +++ b/include/libnftnl/acct.h @@ -0,0 +1,87 @@ +#ifndef _LIBNFTNL_ACCT_H_ +#define _LIBNFTNL_ACCT_H_ + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <sys/types.h> + +#include <libnftnl/common.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct nft_acct; + +struct nft_acct *nft_acct_alloc(void); +void nft_acct_free(struct nft_acct *); + +enum { + NFT_ACCT_ATTR_NAME = 0, + NFT_ACCT_ATTR_TABLE, + NFT_ACCT_ATTR_FAMILY, + NFT_ACCT_ATTR_PKTS, + NFT_ACCT_ATTR_BYTES, + NFT_ACCT_ATTR_FLAGS, + NFT_ACCT_ATTR_ID, + __NFT_ACCT_ATTR_MAX +}; +#define NFT_ACCT_ATTR_MAX (__NFT_ACCT_ATTR_MAX - 1) + +bool nft_acct_attr_is_set(const struct nft_acct *acct, uint16_t attr); +void nft_acct_attr_unset(struct nft_acct *acct, uint16_t attr); +void nft_acct_attr_set(struct nft_acct *acct, uint16_t attr, const void *data); +void nft_acct_attr_set_data(struct nft_acct *acct, uint16_t attr, + const void *data, uint32_t data_len); +const void *nft_acct_attr_get(struct nft_acct *t, uint16_t attr); +const void *nft_acct_attr_get_data(struct nft_acct *t, uint16_t attr, + uint64_t *data_len); + +void nft_acct_attr_set_u32(struct nft_acct *acct, uint16_t attr, uint32_t data); +void nft_acct_attr_set_u64(struct nft_acct *acct, uint16_t attr, uint64_t data); +void nft_acct_attr_set_str(struct nft_acct *acct, uint16_t attr, + const char *str); +uint32_t nft_acct_attr_get_u32(struct nft_acct *acct, uint16_t attr); +uint64_t nft_acct_attr_get_u64(struct nft_acct *acct, uint16_t attr); +const char *nft_acct_attr_get_str(struct nft_acct *acct, uint16_t attr); + +struct nlmsghdr; + +void nft_acct_nlmsg_build_payload(struct nlmsghdr *nlh, + const struct nft_acct *acct); + +int nft_acct_parse(struct nft_acct *acct, enum nft_parse_type type, + const char *data, struct nft_parse_err *err); +int nft_acct_parse_file(struct nft_acct *t, enum nft_parse_type type, + FILE *fp, struct nft_parse_err *err); +int nft_acct_snprintf(char *buf, size_t size, struct nft_acct *acct, + uint32_t type, uint32_t flags); +int nft_acct_fprintf(FILE *fp, struct nft_acct *acct, uint32_t type, + uint32_t flags); + +#define nft_acct_nlmsg_build_hdr nft_nlmsg_build_hdr +int nft_acct_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_acct *acct); +struct nft_acct_list; + +struct nft_acct_list *nft_acct_list_alloc(void); +void nft_acct_list_free(struct nft_acct_list *list); +int nft_acct_list_is_empty(struct nft_acct_list *list); +int nft_acct_list_foreach(struct nft_acct_list *acct_list, + int (*cb)(struct nft_acct *t, void *data), + void *data); +void nft_acct_list_add(struct nft_acct *r, struct nft_acct_list *list); +void nft_acct_list_add_tail(struct nft_acct *r, struct nft_acct_list *list); +void nft_acct_list_del(struct nft_acct *r); + +struct nft_acct_list_iter; + +struct nft_acct_list_iter *nft_acct_list_iter_create(struct nft_acct_list *l); +struct nft_acct *nft_acct_list_iter_next(struct nft_acct_list_iter *iter); +void nft_acct_list_iter_destroy(struct nft_acct_list_iter *iter); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _LIBNFTNL_ACCT_H_ */ diff --git a/include/libnftnl/expr.h b/include/libnftnl/expr.h index 9f25993..6909ebe 100644 --- a/include/libnftnl/expr.h +++ b/include/libnftnl/expr.h @@ -167,6 +167,9 @@ enum { NFT_EXPR_REDIR_FLAGS, }; +enum { + NFT_EXPR_ACCT_NAME = NFT_RULE_EXPR_ATTR_BASE, +}; #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 832bc46..ee00dec 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -53,6 +53,10 @@ enum nft_verdicts { * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes) * @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes) * @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes) + * @NFT_MSG_NEWACCT: create an new accounter (enum nft_acct_attributes) + * @NFT_MSG_GETACCT: get an accounter (enum nft_acct_attributes) + * @NFT_MSG_GETACCT_ZERO: get a reset accounter (enum nft_acct_attributes) + * @NFT_MSG_DELACCT: delete an accounter (enum nft_acct_attributes) */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, @@ -72,6 +76,10 @@ enum nf_tables_msg_types { NFT_MSG_DELSETELEM, NFT_MSG_NEWGEN, NFT_MSG_GETGEN, + NFT_MSG_NEWACCT, + NFT_MSG_GETACCT, + NFT_MSG_GETACCT_ZERO, + NFT_MSG_DELACCT, NFT_MSG_MAX, }; @@ -867,4 +875,37 @@ enum nft_gen_attributes { }; #define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1) +/* + * enum nft_acct_attributes - nf_tables acct netlink attributes + * + * @NFTA_ACCT_NAME: name of the accounter (NLA_STRING) + * @NFTA_ACCT_TABLE: table name (NLA_STRING) + * @NFTA_ACCT_BYTES: number of bytes (NLA_U64) + * @NFTA_ACCT_PACKETS: number of packets (NLA_U64) + * @NFTA_ACCT_USE: number of rule in this accts (NLA_U32) + * @NFTA_ACCT_ID: uniquely identifies a acct in a transaction (NLA_U32) + */ +enum nft_acct_attributes { + NFTA_ACCT_UNSPEC, + NFTA_ACCT_NAME, + NFTA_ACCT_TABLE, + NFTA_ACCT_BYTES, + NFTA_ACCT_PACKETS, + NFTA_ACCT_USE, + NFTA_ACCT_ID, + __NFTA_ACCT_MAX +}; +#define NFTA_ACCT_MAX (__NFTA_ACCT_MAX - 1) + +enum nft_acct_expr_attr { + NFTA_ACCT_EXPR_UNSPEC, + NFTA_ACCT_EXPR_NAME, + __NFTA_ACCT_EXPR_MAX +}; +#define NFTA_ACCT_EXPR_MAX (__NFTA_ACCT_EXPR_MAX - 1) + +#ifndef NFTA_ACCT_NAME_MAX +#define NFTA_ACCT_NAME_MAX 32 +#endif + #endif /* _LINUX_NF_TABLES_H */ diff --git a/src/Makefile.am b/src/Makefile.am index c77c3cc..0ae91b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,6 +6,7 @@ libnftnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnftnl.map \ -version-info $(LIBVERSION) libnftnl_la_SOURCES = utils.c \ + acct.c \ buffer.c \ common.c \ gen.c \ @@ -19,6 +20,7 @@ libnftnl_la_SOURCES = utils.c \ jansson.c \ expr.c \ expr_ops.c \ + expr/acct.c \ expr/bitwise.c \ expr/byteorder.c \ expr/cmp.c \ diff --git a/src/acct.c b/src/acct.c new file mode 100644 index 0000000..908c1b5 --- /dev/null +++ b/src/acct.c @@ -0,0 +1,612 @@ +/* + * (C) 2014 by Ana Rey Botello <ana@xxxxxxxxx> + * + * 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 "internal.h" + +#include <time.h> +#include <endian.h> +#include <stdint.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/types.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/table.h> +#include <libnftnl/acct.h> +#include <buffer.h> + +struct nft_acct { + struct list_head list; + const char *name; + const char *table; + uint32_t family; + unsigned long flags; + uint64_t pkts; + uint64_t bytes; + uint32_t use; +}; + +struct nft_acct *nft_acct_alloc(void) +{ + struct nft_acct *acct; + + acct = calloc(1, sizeof(struct nft_acct)); + if (acct == NULL) + return NULL; + + return acct; +} +EXPORT_SYMBOL(nft_acct_alloc); + +void nft_acct_free(struct nft_acct *acct) +{ + if (acct->name != NULL) + xfree(acct->name); + if (acct->table != NULL) + xfree(acct->table); + + xfree(acct); +} +EXPORT_SYMBOL(nft_acct_free); + +bool nft_acct_attr_is_set(const struct nft_acct *acct, uint16_t attr) +{ + return acct->flags & (1 << attr); +} +EXPORT_SYMBOL(nft_acct_attr_is_set); + +void nft_acct_attr_unset(struct nft_acct *acct, uint16_t attr) +{ + if (!(acct->flags & (1 << attr))) + return; + + switch (attr) { + case NFT_ACCT_ATTR_NAME: + if (acct->name) { + xfree(acct->name); + acct->name = NULL; + } + break; + case NFT_ACCT_ATTR_TABLE: + if (acct->table) { + xfree(acct->table); + acct->table = NULL; + } + break; + case NFT_ACCT_ATTR_BYTES: + case NFT_ACCT_ATTR_PKTS: + case NFT_ACCT_ATTR_FAMILY: + break; + } + acct->flags &= ~(1 << attr); +} +EXPORT_SYMBOL(nft_acct_attr_unset); + +static uint32_t nft_acct_attr_validate[NFT_ACCT_ATTR_MAX + 1] = { + [NFT_ACCT_ATTR_BYTES] = sizeof(uint64_t), + [NFT_ACCT_ATTR_PKTS] = sizeof(uint64_t), + [NFT_ACCT_ATTR_FAMILY] = sizeof(uint32_t), +}; + +void nft_acct_attr_set_data(struct nft_acct *acct, uint16_t attr, + const void *data, uint32_t data_len) +{ + if (attr > NFT_ACCT_ATTR_MAX) + return; + + nft_assert_validate(data, nft_acct_attr_validate, attr, data_len); + + switch (attr) { + case NFT_ACCT_ATTR_NAME: + if (acct->name) + xfree(acct->name); + + acct->name = strdup(data); + break; + case NFT_ACCT_ATTR_TABLE: + if (acct->table) + xfree(acct->table); + + acct->table = strdup(data); + break; + case NFT_ACCT_ATTR_BYTES: + acct->bytes = *((uint64_t *)data); + break; + case NFT_ACCT_ATTR_PKTS: + acct->pkts = *((uint64_t *)data); + break; + case NFT_ACCT_ATTR_FAMILY: + acct->family = *((uint32_t *)data); + break; + } + acct->flags |= (1 << attr); +} +EXPORT_SYMBOL(nft_acct_attr_set_data); + +void nft_acct_attr_set(struct nft_acct *acct, uint16_t attr, const void *data) +{ + nft_acct_attr_set_data(acct, attr, data, nft_acct_attr_validate[attr]); +} +EXPORT_SYMBOL(nft_acct_attr_set); + +void nft_acct_attr_set_u64(struct nft_acct *acct, uint16_t attr, uint64_t val) +{ + nft_acct_attr_set_data(acct, attr, &val, sizeof(uint64_t)); +} +EXPORT_SYMBOL(nft_acct_attr_set_u64); + +void nft_acct_attr_set_u32(struct nft_acct *acct, uint16_t attr, uint32_t val) +{ + nft_acct_attr_set_data(acct, attr, &val, sizeof(uint32_t)); +} +EXPORT_SYMBOL(nft_acct_attr_set_u32); + +void nft_acct_attr_set_str(struct nft_acct *acct, uint16_t attr, + const char *str) +{ + nft_acct_attr_set_data(acct, attr, str, 0); +} +EXPORT_SYMBOL(nft_acct_attr_set_str); + +const void *nft_acct_attr_get_data(struct nft_acct *acct, uint16_t attr, + uint64_t *data_len) +{ + if (!(acct->flags & (1 << attr))) + return NULL; + + switch (attr) { + case NFT_ACCT_ATTR_NAME: + return acct->name; + case NFT_ACCT_ATTR_TABLE: + return acct->table; + case NFT_ACCT_ATTR_BYTES: + *data_len = sizeof(uint64_t); + return &acct->bytes; + case NFT_ACCT_ATTR_PKTS: + *data_len = sizeof(uint64_t); + return &acct->pkts; + case NFT_ACCT_ATTR_FAMILY: + *data_len = sizeof(uint32_t); + return &acct->family; + } + return NULL; +} +EXPORT_SYMBOL(nft_acct_attr_get_data); + +const void *nft_acct_attr_get(struct nft_acct *acct, uint16_t attr) +{ + uint64_t data_len; + + return nft_acct_attr_get_data(acct, attr, &data_len); +} +EXPORT_SYMBOL(nft_acct_attr_get); + +uint64_t nft_acct_attr_get_u64(struct nft_acct *acct, uint16_t attr) +{ + const void *ret = nft_acct_attr_get(acct, attr); + + return ret == NULL ? 0 : *((uint64_t *)ret); +} +EXPORT_SYMBOL(nft_acct_attr_get_u64); + +uint32_t nft_acct_attr_get_u32(struct nft_acct *acct, uint16_t attr) +{ + const void *ret = nft_acct_attr_get(acct, attr); + + return ret == NULL ? 0 : *((uint32_t *)ret); +} +EXPORT_SYMBOL(nft_acct_attr_get_u32); + +const char *nft_acct_attr_get_str(struct nft_acct *acct, uint16_t attr) +{ + return nft_acct_attr_get(acct, attr); +} +EXPORT_SYMBOL(nft_acct_attr_get_str); + +void nft_acct_nlmsg_build_payload(struct nlmsghdr *nlh, + const struct nft_acct *acct) +{ + if (acct->flags & (1 << NFT_ACCT_ATTR_NAME)) + mnl_attr_put_strz(nlh, NFTA_ACCT_NAME, acct->name); + if (acct->flags & (1 << NFT_ACCT_ATTR_TABLE)) + mnl_attr_put_strz(nlh, NFTA_ACCT_TABLE, acct->table); + if (acct->flags & (1 << NFT_ACCT_ATTR_BYTES)) + mnl_attr_put_u64(nlh, NFTA_ACCT_BYTES, htobe64(acct->bytes)); + if (acct->flags & (1 << NFT_ACCT_ATTR_PKTS)) + mnl_attr_put_u64(nlh, NFTA_ACCT_PACKETS, htobe64(acct->pkts)); +} +EXPORT_SYMBOL(nft_acct_nlmsg_build_payload); + +static int nft_acct_parse_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_ACCT_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_ACCT_NAME: + case NFTA_ACCT_TABLE: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + case NFTA_ACCT_BYTES: + case NFTA_ACCT_PACKETS: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +int nft_acct_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_acct *acct) +{ + struct nlattr *tb[NFTA_ACCT_MAX + 1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + + if (mnl_attr_parse(nlh, sizeof(*nfg), nft_acct_parse_attr_cb, tb) < 0) + return -1; + + if (tb[NFTA_ACCT_NAME]) { + acct->name = strdup(mnl_attr_get_str(tb[NFTA_ACCT_NAME])); + acct->flags |= (1 << NFT_ACCT_ATTR_NAME); + } + if (tb[NFTA_ACCT_TABLE]) { + acct->table = strdup(mnl_attr_get_str(tb[NFTA_ACCT_TABLE])); + acct->flags |= (1 << NFT_ACCT_ATTR_TABLE); + } + if (tb[NFTA_ACCT_BYTES]) { + acct->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_ACCT_BYTES])); + acct->flags |= (1 << NFT_ACCT_ATTR_BYTES); + } + if (tb[NFTA_ACCT_PACKETS]) { + acct->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_ACCT_PACKETS])); + acct->flags |= (1 << NFT_ACCT_ATTR_PKTS); + } + acct->family = nfg->nfgen_family; + acct->flags |= (1 << NFT_ACCT_ATTR_FAMILY); + + return 0; +} +EXPORT_SYMBOL(nft_acct_nlmsg_parse); + +#ifdef XML_PARSING +int nft_mxml_acct_parse(mxml_node_t *tree, struct nft_accte *acct, + struct nft_parse_err *err) +{ + const char *name, *table; + uint64_t pkts, bytes; + uint32_t family; + + name = nft_mxml_str_parse(tree, "acct", MXML_DESCEND_FIRST, + NFT_XML_MAND, err); + if (name != NULL) + nft_acct_attr_set_str(t, NFT_ACCT_ATTR_NAME, name); + + table = nft_mxml_str_parse(tree, "table", MXML_DESCEND_FIRST, + NFT_XML_MAND, err); + if (table != NULL) + nft_acct_attr_set_str(t, NFT_ACCT_ATTR_TABLE, table); + + if (nft_mxml_num_parse(tree, "family", MXML_DESCEND, BASE_DEC, + &use, NFT_TYPE_U32, NFT_XML_MAND, err) == 0) + nft_acct_attr_set_u32(t, NFT_ACCT_ATTR_FAMILY, family); + + if (nft_mxml_num_parse(tree, "pkts", MXML_DESCEND, BASE_DEC, + &use, NFT_TYPE_U64, NFT_XML_MAND, err) == 0) + nft_acct_attr_set_u64(t, NFT_ACCT_ATTR_PKTS, pkts); + + if (nft_mxml_num_parse(tree, "bytes", MXML_DESCEND, BASE_DEC, + &use, NFT_TYPE_U64, NFT_XML_MAND, err) == 0) + nft_acct_attr_set_u64(t, NFT_ACCT_ATTR_BYTES, bytes); + + + return 0; +} +#endif + +static int nft_acct_xml_parse(struct nft_acct *acct, const void *data, + struct nft_parse_err *err, + enum nft_parse_input input) +{ +#ifdef XML_PARSING + int ret; + mxml_node_t *tree = nft_mxml_build_tree(data, "acct", err, input); + + if (tree == NULL) + return -1; + + ret = nft_mxml_acct_parse(tree, acct, err); + mxmlDelete(tree); + return ret; +#else + errno = EOPNOTSUPP; + return -1; +#endif +} + +#ifdef JSON_PARSING +int nft_jansson_parse_acct(struct nft_acct *acct, json_t *tree, + struct nft_parse_err *err) +{ + json_t *root; + uint64_t bytes, pkts; + const char *name, table; + + root = nft_jansson_get_node(tree, "acct", err); + if (root == NULL) + return -1; + + name = nft_jansson_parse_str(root, "name", err); + if (name != NULL) + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_NAME, name); + + table = nft_jansson_parse_str(root, "table", err); + if (table != NULL) + nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_TABLE, table); + + if (nft_jansson_parse_val(root, "family", NFT_TYPE_U32, &family, + err) == 0) + nft_acct_attr_set_u32(t, NFT_ACCT_ATTR_PKTS, family); + + if (nft_jansson_parse_val(root, "pkts", NFT_TYPE_U64, &pkts, err) == 0) + nft_acct_attr_set_u64(t, NFT_ACCT_ATTR_PKTS, pkts); + + if (nft_jansson_parse_val(root, "bytes", NFT_TYPE_U64, &bytes, + err) == 0) + nft_acct_attr_set_u64(t, NFT_ACCT_ATTR_BYTES, bytes); + + return 0; +} +#endif + +static int nft_acct_json_parse(struct nft_acct *acct, const void *json, + struct nft_parse_err *err, + enum nft_parse_input input) +{ +#ifdef JSON_PARSING + json_t *tree; + json_error_t error; + int ret; + + tree = nft_jansson_create_root(json, &error, err, input); + if (tree == NULL) + return -1; + + ret = nft_jansson_parse_acct(acct, tree, err); + + nft_jansson_free_root(tree); + + return ret; +#else + errno = EOPNOTSUPP; + return -1; +#endif +} + +static int nft_acct_do_parse(struct nft_acct *acct, enum nft_parse_type type, + const void *data, struct nft_parse_err *err, + enum nft_parse_input input) +{ + int ret; + struct nft_parse_err perr; + + switch (type) { + case NFT_PARSE_XML: + ret = nft_acct_xml_parse(acct, data, &perr, input); + break; + case NFT_PARSE_JSON: + ret = nft_acct_json_parse(acct, data, &perr, input); + break; + default: + ret = -1; + errno = EOPNOTSUPP; + break; + } + + if (err != NULL) + *err = perr; + + return ret; +} + +int nft_acct_parse(struct nft_acct *acct, enum nft_parse_type type, + const char *data, struct nft_parse_err *err) +{ + return nft_acct_do_parse(acct, type, data, err, NFT_PARSE_BUFFER); +} +EXPORT_SYMBOL(nft_acct_parse); + +int nft_acct_parse_file(struct nft_acct *acct, enum nft_parse_type type, + FILE *fp, struct nft_parse_err *err) +{ + return nft_acct_do_parse(acct, type, fp, err, NFT_PARSE_FILE); +} +EXPORT_SYMBOL(nft_acct_parse_file); + +static int nft_acct_export(char *buf, size_t size, struct nft_acct *acct, + int type) +{ + NFT_BUF_INIT(b, buf, size); + nft_buf_open(&b, type, ACCT); + + if (acct->flags & (1 << NFT_ACCT_ATTR_NAME)) + nft_buf_str(&b, type, acct->name, NAME); + if (acct->flags & (1 << NFT_ACCT_ATTR_TABLE)) + nft_buf_str(&b, type, acct->table, TABLE); + if (acct->flags & (1 << NFT_ACCT_ATTR_FAMILY)) + nft_buf_str(&b, type, nft_family2str(acct->family), FAMILY); + if (acct->flags & (1 << NFT_ACCT_ATTR_PKTS)) + nft_buf_u64(&b, type, acct->pkts, PACKETS); + if (acct->flags & (1 << NFT_ACCT_ATTR_BYTES)) + nft_buf_u64(&b, type, acct->bytes, BYTES); + nft_buf_close(&b, type, ACCT); + + return nft_buf_done(&b); +} + +static int nft_acct_snprintf_default(char *buf, size_t size, + struct nft_acct *acct) +{ + return snprintf(buf, size, + "table %s family %s acct %s packet %"PRIu64" bytes %"PRIu64"", + acct->table, + nft_family2str(acct->family), + acct->name, + acct->pkts, + acct->bytes); +} + +int nft_acct_snprintf(char *buf, size_t len, struct nft_acct *acct, + uint32_t type, uint32_t flags) +{ + switch (type) { + case NFT_OUTPUT_DEFAULT: + return nft_acct_snprintf_default(buf, len, acct); + case NFT_OUTPUT_XML: + case NFT_OUTPUT_JSON: + return nft_acct_export(buf, len, acct, type); + default: + break; + } + return -1; +} +EXPORT_SYMBOL(nft_acct_snprintf); + +static inline int nft_acct_do_snprintf(char *buf, size_t size, void *acct, + uint32_t type, uint32_t flags) +{ + return nft_acct_snprintf(buf, size, acct, type, flags); +} + +int nft_acct_fprintf(FILE *fp, struct nft_acct *acct, uint32_t type, + uint32_t flags) +{ + return nft_fprintf(fp, acct, type, flags, nft_acct_do_snprintf); +} +EXPORT_SYMBOL(nft_acct_fprintf); + +struct nft_acct_list { + struct list_head list; +}; + +struct nft_acct_list *nft_acct_list_alloc(void) +{ + struct nft_acct_list *list; + + list = calloc(1, sizeof(struct nft_acct_list)); + if (list == NULL) + return NULL; + + INIT_LIST_HEAD(&list->list); + + return list; +} +EXPORT_SYMBOL(nft_acct_list_alloc); + +void nft_acct_list_free(struct nft_acct_list *list) +{ + struct nft_acct *acct, *tmp; + + list_for_each_entry_safe(acct, tmp, &list->list, list) { + list_del(&acct->list); + nft_acct_free(acct); + } + xfree(list); +} +EXPORT_SYMBOL(nft_acct_list_free); + +int nft_acct_list_is_empty(struct nft_acct_list *list) +{ + return list_empty(&list->list); +} +EXPORT_SYMBOL(nft_acct_list_is_empty); + +void nft_acct_list_add(struct nft_acct *acct, struct nft_acct_list *list) +{ + list_add(&acct->list, &list->list); +} +EXPORT_SYMBOL(nft_acct_list_add); + +void nft_acct_list_add_tail(struct nft_acct *acct, struct nft_acct_list *list) +{ + list_add_tail(&acct->list, &list->list); +} +EXPORT_SYMBOL(nft_acct_list_add_tail); + +void nft_acct_list_del(struct nft_acct *acct) +{ + list_del(&acct->list); +} +EXPORT_SYMBOL(nft_acct_list_del); + +int nft_acct_list_foreach(struct nft_acct_list *acct_list, + int (*cb)(struct nft_acct *acct, void *data), + void *data) +{ + struct nft_acct *cur, *tmp; + int ret; + + list_for_each_entry_safe(cur, tmp, &acct_list->list, list) { + ret = cb(cur, data); + if (ret < 0) + return ret; + } + return 0; +} +EXPORT_SYMBOL(nft_acct_list_foreach); + +struct nft_acct_list_iter { + struct nft_acct_list *list; + struct nft_acct *cur; +}; + +struct nft_acct_list_iter *nft_acct_list_iter_create(struct nft_acct_list *l) +{ + struct nft_acct_list_iter *iter; + + iter = calloc(1, sizeof(struct nft_acct_list_iter)); + if (iter == NULL) + return NULL; + + iter->list = l; + iter->cur = list_entry(l->list.next, struct nft_acct, list); + + return iter; +} +EXPORT_SYMBOL(nft_acct_list_iter_create); + +struct nft_acct *nft_acct_list_iter_next(struct nft_acct_list_iter *iter) +{ + struct nft_acct *r = iter->cur; + + /* get next acct, if any */ + iter->cur = list_entry(iter->cur->list.next, struct nft_acct, list); + if (&iter->cur->list == iter->list->list.next) + return NULL; + + return r; +} +EXPORT_SYMBOL(nft_acct_list_iter_next); + +void nft_acct_list_iter_destroy(struct nft_acct_list_iter *iter) +{ + xfree(iter); +} +EXPORT_SYMBOL(nft_acct_list_iter_destroy); diff --git a/src/expr/acct.c b/src/expr/acct.c new file mode 100644 index 0000000..9aabdd5 --- /dev/null +++ b/src/expr/acct.c @@ -0,0 +1,201 @@ +/* + * (C) 2014 by Ana Rey Botello <ana@xxxxxxxxx> + * + * 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 <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> +#include "expr_ops.h" +#include <buffer.h> + + +struct nft_expr_acct { + const char *name; +}; + +static int nft_rule_expr_acct_set(struct nft_rule_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nft_expr_acct *acct = nft_expr_data(e); + + switch (type) { + case NFT_EXPR_ACCT_NAME: + if (acct->name) + xfree(acct->name); + acct->name = strdup(data); + break; + default: + return -1; + } + + return 0; +} + +static const void *nft_rule_expr_acct_get(const struct nft_rule_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nft_expr_acct *acct = nft_expr_data(e); + + switch (type) { + case NFT_EXPR_ACCT_NAME: + *data_len = sizeof(acct->name); + return acct->name; + } + + return NULL; +} + +static int nft_rule_expr_acct_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_ACCT_EXPR_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_ACCT_EXPR_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + + return MNL_CB_OK; +} + +static void nft_rule_expr_acct_build(struct nlmsghdr *nlh, + struct nft_rule_expr *e) +{ + struct nft_expr_acct *acct = nft_expr_data(e); + + if (e->flags & (1 << NFT_EXPR_ACCT_NAME)) + mnl_attr_put_strz(nlh, NFTA_ACCT_EXPR_NAME, acct->name); +} + +static int nft_rule_expr_acct_parse(struct nft_rule_expr *e, + struct nlattr *attr) +{ + struct nft_expr_acct *acct = nft_expr_data(e); + struct nlattr *tb[NFTA_ACCT_EXPR_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_acct_cb, tb) < 0) + return -1; + + if (tb[NFTA_ACCT_EXPR_NAME]) { + if (acct->name) + xfree(acct->name); + acct->name = strdup(mnl_attr_get_str(tb[NFTA_ACCT_EXPR_NAME])); + e->flags |= (1 << NFT_EXPR_ACCT_NAME); + } + + return 0; +} + +static int nft_rule_expr_acct_json_parse(struct nft_rule_expr *e, json_t *root, + struct nft_parse_err *err) +{ +#ifdef JSON_PARSING + const char *name; + + name = nft_jansson_parse_str(root, "acct", err); + if (name != NULL) + nft_rule_expr_set_str(root, NFT_EXPR_ACCT_NAME, name); + + return 0; +#else + errno = EOPNOTSUPP; + return -1; +#endif +} + +static int nft_rule_expr_acct_xml_parse(struct nft_rule_expr *e, + mxml_node_t *tree, + struct nft_parse_err *err) +{ +#ifdef XML_PARSING + const char *name; + + name = nft_mxml_str_parse(tree, "acct", MXML_DESCEND_FIRST, + NFT_XML_MAND, err); + + if (name != NULL) + nft_rule_expr_set_str(e, NFT_EXPR_ACCT_NAME, acct); + + return 0; +#else + errno = EOPNOTSUPP; + return -1; +#endif +} + +static int nft_rule_expr_acct_export(char *buf, size_t size, + struct nft_rule_expr *e, int type) +{ + struct nft_expr_acct *acct = nft_expr_data(e); + + NFT_BUF_INIT(b, buf, size); + + if (e->flags & (1 << NFT_EXPR_ACCT_NAME)) + nft_buf_str(&b, type, acct->name, NAME); + + return nft_buf_done(&b); +} + +static int nft_rule_expr_acct_snprintf_default(char *buf, size_t size, + struct nft_rule_expr *e) +{ + struct nft_expr_acct *acct = nft_expr_data(e); + + return snprintf(buf, size, "%s", acct->name); +} + +static int nft_rule_expr_acct_snprintf(char *buf, size_t len, uint32_t type, + uint32_t flags, struct nft_rule_expr *e) +{ + switch (type) { + case NFT_OUTPUT_DEFAULT: + return nft_rule_expr_acct_snprintf_default(buf, len, e); + case NFT_OUTPUT_XML: + case NFT_OUTPUT_JSON: + return nft_rule_expr_acct_export(buf, len, e, type); + default: + break; + } + + return -1; +} + +struct expr_ops expr_ops_acct = { + .name = "acct", + .alloc_len = sizeof(struct nft_expr_acct), + .max_attr = NFTA_ACCT_EXPR_MAX, + .set = nft_rule_expr_acct_set, + .get = nft_rule_expr_acct_get, + .parse = nft_rule_expr_acct_parse, + .build = nft_rule_expr_acct_build, + .snprintf = nft_rule_expr_acct_snprintf, + .xml_parse = nft_rule_expr_acct_xml_parse, + .json_parse = nft_rule_expr_acct_json_parse, +}; + +static void __init expr_acct_init(void) +{ + nft_expr_ops_register(&expr_ops_acct); +} diff --git a/src/libnftnl.map b/src/libnftnl.map index be7b998..f81089d 100644 --- a/src/libnftnl.map +++ b/src/libnftnl.map @@ -226,4 +226,34 @@ LIBNFTNL_1.2 { nft_gen_nlmsg_parse; nft_gen_snprintf; nft_gen_fprintf; + + nft_acct_alloc; + nft_acct_free; + nft_acct_attr_is_set; + nft_acct_attr_unset; + nft_acct_attr_set; + nft_acct_attr_get; + nft_acct_attr_set_u64; + nft_acct_attr_set_u32; + nft_acct_attr_set_str; + nft_acct_attr_get_u64; + nft_acct_attr_get_u32; + nft_acct_attr_get_str; + nft_acct_parse; + nft_acct_parse_file; + nft_acct_snprintf; + nft_acct_fprintf; + nft_acct_nlmsg_build_payload; + nft_acct_nlmsg_parse; + nft_acct_list_alloc; + nft_acct_list_free; + nft_acct_list_is_empty; + nft_acct_list_foreach; + nft_acct_list_add; + nft_acct_list_add_tail; + nft_acct_list_del; + nft_acct_list_iter_create; + nft_acct_list_iter_next; + nft_acct_list_iter_destroy; + } LIBNFTNL_1.1; -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html