@@ -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}
new file mode 100644
@@ -0,0 +1,136 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ *
+ * 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;
+}
new file mode 100644
@@ -0,0 +1,133 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ *
+ * 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;
+}
new file mode 100644
@@ -0,0 +1,135 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ *
+ * 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;
+}
new file mode 100644
@@ -0,0 +1,121 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ *
+ * 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;
+}
new file mode 100644
@@ -0,0 +1,220 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ *
+ * 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;
+}
@@ -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)
{
@@ -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"
@@ -5,4 +5,5 @@ pkginclude_HEADERS = table.h \
set.h \
ruleset.h \
common.h \
- gen.h
+ gen.h \
+ acct.h
new file mode 100644
@@ -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_ */
@@ -167,6 +167,9 @@ enum {
NFT_EXPR_REDIR_FLAGS,
};
+enum {
+ NFT_EXPR_ACCT_NAME = NFT_RULE_EXPR_ATTR_BASE,
+};
#ifdef __cplusplus
} /* extern "C" */
#endif
@@ -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 */
@@ -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 \
new file mode 100644
@@ -0,0 +1,612 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ *
+ * 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);
new file mode 100644
@@ -0,0 +1,201 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ *
+ * 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);
+}
@@ -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;