* [nf v2 0/6] Accounting objects support in nft
@ 2015-01-26 19:43 Ana Rey Botello
2015-01-26 19:43 ` [nf v2 1/2] netfilter: Rename from nft_counter to nft_counter_priv Ana Rey Botello
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Ana Rey Botello @ 2015-01-26 19:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Ana Rey Botello
Hi,
With this patchset, we add accounting objects support to let us
manipulate extended accounting objects.
Example of use in nft:
# nft add counter ip filter http-traffic
# nft add counter ip filter https-traffic
# nft add rule ip filter output tcp dport 80 counter name http-traffic
# nft add rule ip filter output tcp dport 443 counter name https-traffic
# nft delete counter ip filter https-traffic
# nft list table ip test
table ip filter {
counter http-traffic { pkts 779 bytes 99495}
counter https-traffic { pkts 189 bytes 37824}
chain output {
type filter hook output priority 0;
tcp dport http counter http-traffic
tcp dport https counter https-traffic
}
}
It is difficult to reuse the existing code of nfacct because:
* nfacct does not have transation support transactions.
* We need something that integrated well to nf_tables.
There is a reset accounter support in the kernel-space and libnftnl. But
not in nft-tool yet.
No quota support yet.
[Changes in v2]
* This deletes the acct module and uses the counter module.
* This renames from nft_counter to nft_counter_priv struct
* This uses _COUNTER_ names instead of _ACCT_ names in variables and functions
* Rename acct netlink attributes to named counter netlink attributes. The new
names are NFTA_NAMED_CTR_XXX
* This limits NFT_CTR_MAXNAMELEN to 16
* This fixes some memory problems
These changes were sugguested by Pablo Neira and Patrick McHardy.
[kernel-nf]
Ana Rey (1):
netfilter: named counter: add support to counters in nftables
Ana Rey Botello (1):
netfilter: Rename from nft_counter to nft_counter_priv
include/net/netfilter/nf_tables.h | 49 +++
include/uapi/linux/netfilter/nf_tables.h | 34 +++
net/netfilter/nf_tables_api.c | 486 +++++++++++++++++++++++++++++-
net/netfilter/nft_counter.c | 130 ++++++--
4 files changed, 677 insertions(+), 22 deletions(-)
[libnftnl]
Ana Rey (1):
src: Add counters support
Ana Rey Botello (1):
tests: add unit tests for counters
examples/Makefile.am | 27 +-
examples/nft-counter-add.c | 140 ++++++++
examples/nft-counter-del.c | 135 +++++++
examples/nft-counter-get.c | 137 +++++++
examples/nft-counter-reset.c | 123 +++++++
examples/nft-counters-get.c | 136 +++++++
examples/nft-rule-add.c | 2 +-
examples/nft-rule-counter-add.c | 221 ++++++++++++
examples/nft-rule-get.c | 1 +
include/buffer.h | 1 +
include/libnftnl/Makefile.am | 3 +-
include/libnftnl/counter.h | 97 +++++
include/libnftnl/expr.h | 1 +
include/linux/netfilter/nf_tables.h | 33 ++
src/Makefile.am | 1 +
src/counter.c | 671 +++++++++++++++++++++++++++++++++++
src/expr/counter.c | 48 ++-
src/internal.h | 6 +
src/libnftnl.map | 30 ++
tests/Makefile.am | 4 +
tests/nft-counter-test.c | 86 +++++
tests/nft-expr_counter-test.c | 4 +
22 files changed, 1902 insertions(+), 5 deletions(-)
create mode 100644 examples/nft-counter-add.c
create mode 100644 examples/nft-counter-del.c
create mode 100644 examples/nft-counter-get.c
create mode 100644 examples/nft-counter-reset.c
create mode 100644 examples/nft-counters-get.c
create mode 100644 examples/nft-rule-counter-add.c
create mode 100644 include/libnftnl/counter.h
create mode 100644 src/counter.c
create mode 100644 tests/nft-counter-test.c
[nft]
Ana Rey (2):
src: Add the accounter support
tests: regression: Add counters support
include/linux/netfilter/nf_tables.h | 32 +++++
include/mnl.h | 8 ++
include/netlink.h | 22 ++++
include/rule.h | 47 +++++++
include/statement.h | 1 +
src/evaluate.c | 13 +-
src/mnl.c | 119 ++++++++++++++++++
src/netlink.c | 235 +++++++++++++++++++++++++++++++++++
src/netlink_delinearize.c | 3 +
src/netlink_linearize.c | 4 +
src/parser_bison.y | 60 ++++++++-
src/rule.c | 139 +++++++++++++++++++++
src/scanner.l | 1 +
src/statement.c | 8 +-
tests/regression/ip/counter.t | 15 +++
tests/regression/nft-test.py | 110 ++++++++++++++++
16 files changed, 810 insertions(+), 7 deletions(-)
create mode 100644 tests/regression/ip/counter.t
--
1.7.10.4
^ permalink raw reply [flat|nested] 7+ messages in thread
* [nf v2 1/2] netfilter: Rename from nft_counter to nft_counter_priv
2015-01-26 19:43 [nf v2 0/6] Accounting objects support in nft Ana Rey Botello
@ 2015-01-26 19:43 ` Ana Rey Botello
[not found] ` <cover.1422299750.git.ana@soleta.eu>
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Ana Rey Botello @ 2015-01-26 19:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Ana Rey Botello
We need this change for the patch:
"netfilter: named counter: add support to accounters in nftables"
Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
net/netfilter/nft_counter.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c
index c89ee48..8815312 100644
--- a/net/netfilter/nft_counter.c
+++ b/net/netfilter/nft_counter.c
@@ -17,7 +17,7 @@
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
-struct nft_counter {
+struct nft_counter_priv {
seqlock_t lock;
u64 bytes;
u64 packets;
@@ -27,7 +27,7 @@ static void nft_counter_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
- struct nft_counter *priv = nft_expr_priv(expr);
+ struct nft_counter_priv *priv = nft_expr_priv(expr);
write_seqlock_bh(&priv->lock);
priv->bytes += pkt->skb->len;
@@ -37,7 +37,7 @@ static void nft_counter_eval(const struct nft_expr *expr,
static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
- struct nft_counter *priv = nft_expr_priv(expr);
+ struct nft_counter_priv *priv = nft_expr_priv(expr);
unsigned int seq;
u64 bytes;
u64 packets;
@@ -67,7 +67,7 @@ static int nft_counter_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
- struct nft_counter *priv = nft_expr_priv(expr);
+ struct nft_counter_priv *priv = nft_expr_priv(expr);
if (tb[NFTA_COUNTER_PACKETS])
priv->packets = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
@@ -81,7 +81,7 @@ static int nft_counter_init(const struct nft_ctx *ctx,
static struct nft_expr_type nft_counter_type;
static const struct nft_expr_ops nft_counter_ops = {
.type = &nft_counter_type,
- .size = NFT_EXPR_SIZE(sizeof(struct nft_counter)),
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_counter_priv)),
.eval = nft_counter_eval,
.init = nft_counter_init,
.dump = nft_counter_dump,
--
1.7.10.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [libnftnl v2 1/2] src: Add counters support
[not found] ` <cover.1422299705.git.ana@soleta.eu>
@ 2015-01-26 19:43 ` Ana Rey Botello
2015-01-26 19:43 ` [libnftnl v2 2/2] tests: add unit tests for counters Ana Rey Botello
1 sibling, 0 replies; 7+ messages in thread
From: Ana Rey Botello @ 2015-01-26 19:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Ana Rey
From: Ana Rey <ana@soleta.eu>
This adds userspace support to counters: support to counter objects and
counter expression.
Moreover, this adds some examples to add/delete/get/reset the counter
object and add rules using an existing counter.
Example of how to use those examples:
* Add a new counter:
# ./examples/nft-counter-add ip test counter1
# ./examples/nft-counter-get ip test counter1 default
table test family ip counter counter1 packet 0 bytes 0
* Delete a counter:
# ./examples/nft-counter-del ip test counter1
* Add a rule using the counter expression.
# ./examples/nft-rule-add ip test output
* Reset the counter:
# ./examples/nft-counter-reset ip test counter1
The kernel support is added in the commit:
netfilter: named counter: add support to counters in nftables
Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
examples/Makefile.am | 27 +-
examples/nft-counter-add.c | 140 ++++++++
examples/nft-counter-del.c | 135 +++++++
examples/nft-counter-get.c | 137 +++++++
examples/nft-counter-reset.c | 123 +++++++
examples/nft-counters-get.c | 136 +++++++
examples/nft-rule-add.c | 2 +-
examples/nft-rule-counter-add.c | 221 ++++++++++++
examples/nft-rule-get.c | 1 +
include/buffer.h | 1 +
include/libnftnl/Makefile.am | 3 +-
include/libnftnl/counter.h | 97 +++++
include/libnftnl/expr.h | 1 +
include/linux/netfilter/nf_tables.h | 33 ++
src/Makefile.am | 1 +
src/counter.c | 671 +++++++++++++++++++++++++++++++++++
src/expr/counter.c | 48 ++-
src/internal.h | 6 +
src/libnftnl.map | 30 ++
19 files changed, 1808 insertions(+), 5 deletions(-)
create mode 100644 examples/nft-counter-add.c
create mode 100644 examples/nft-counter-del.c
create mode 100644 examples/nft-counter-get.c
create mode 100644 examples/nft-counter-reset.c
create mode 100644 examples/nft-counters-get.c
create mode 100644 examples/nft-rule-counter-add.c
create mode 100644 include/libnftnl/counter.h
create mode 100644 src/counter.c
diff --git a/examples/Makefile.am b/examples/Makefile.am
index fafcb76..167680a 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -22,7 +22,14 @@ check_PROGRAMS = nft-table-add \
nft-set-elem-get \
nft-set-elem-del \
nft-ruleset-get \
- nft-compat-get
+ nft-compat-get \
+ nft-counter-add \
+ nft-counter-get \
+ nft-counters-get \
+ nft-counter-reset \
+ nft-rule-counter-add \
+ nft-counter-del
+
nft_table_add_SOURCES = nft-table-add.c
nft_table_add_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
@@ -92,3 +99,21 @@ 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_counter_add_SOURCES = nft-counter-add.c
+nft_counter_add_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+
+nft_counter_get_SOURCES = nft-counter-get.c
+nft_counter_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+
+nft_counters_get_SOURCES = nft-counters-get.c
+nft_counters_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+
+nft_counter_reset_SOURCES = nft-counter-reset.c
+nft_counter_reset_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+
+nft_counter_del_SOURCES = nft-counter-del.c
+nft_counter_del_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+
+nft_rule_counter_add_SOURCES = nft-rule-counter-add.c
+nft_rule_counter_add_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
diff --git a/examples/nft-counter-add.c b/examples/nft-counter-add.c
new file mode 100644
index 0000000..34af691
--- /dev/null
+++ b/examples/nft-counter-add.c
@@ -0,0 +1,140 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ * (C) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2014 Intra2net AG <http://www.intra2net.com>
+ *
+ * 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/counter.h>
+
+static struct nft_counter *counter_add_parse(int argc, char *argv[])
+{
+ struct nft_counter *counter;
+ 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;
+ }
+
+ counter = nft_counter_alloc();
+ if (counter == NULL) {
+ perror("OOM");
+ return NULL;
+ }
+
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_NAME, argv[3]);
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE, argv[2]);
+ nft_counter_attr_set_u32(counter, NFT_COUNTER_ATTR_FAMILY, family);
+ nft_counter_attr_set_u64(counter, NFT_COUNTER_ATTR_PKTS, 0);
+ nft_counter_attr_set_u64(counter, NFT_COUNTER_ATTR_BYTES, 0);
+
+ return counter;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ uint32_t portid, seq, counter_seq, family;
+ struct nft_counter *counter;
+ struct mnl_nlmsg_batch *batch;
+ int ret, batching;
+
+ if (argc != 4) {
+ fprintf(stderr, "%s <family> <table> <counter>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ counter = counter_add_parse(argc, argv);
+ if (counter == 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);
+ }
+ counter_seq = seq;
+ family = nft_counter_attr_get_u32(counter, NFT_COUNTER_ATTR_FAMILY);
+ nlh = nft_counter_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ NFT_MSG_NEWCOUNTER, family,
+ NLM_F_CREATE|NLM_F_ACK, seq++);
+
+ nft_counter_nlmsg_build_payload(nlh, counter);
+ nft_counter_free(counter);
+ 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, counter_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-counter-del.c b/examples/nft-counter-del.c
new file mode 100644
index 0000000..38e68e7
--- /dev/null
+++ b/examples/nft-counter-del.c
@@ -0,0 +1,135 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ * (C) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2014 Intra2net AG <http://www.intra2net.com>
+ *
+ * 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/counter.h>
+
+static struct nft_counter *counter_del_parse(int argc, char *argv[])
+{
+ struct nft_counter *counter;
+ 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;
+ }
+
+ counter = nft_counter_alloc();
+ if (counter == NULL) {
+ perror("OOM");
+ return NULL;
+ }
+
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE, argv[2]);
+ nft_counter_attr_set_u32(counter, NFT_COUNTER_ATTR_FAMILY, family);
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_NAME, argv[3]);
+
+ return counter;
+}
+
+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_counter *counter;
+ struct mnl_nlmsg_batch *batch;
+ int ret, batching;
+
+ if (argc != 4) {
+ fprintf(stderr, "%s <family> <table> <counter>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ counter = counter_del_parse(argc, argv);
+ if (counter == 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_counter_attr_get_u32(counter, NFT_COUNTER_ATTR_FAMILY);
+ nlh = nft_counter_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ NFT_MSG_DELCOUNTER, family,
+ NLM_F_ACK, seq++);
+ nft_counter_nlmsg_build_payload(nlh, counter);
+ mnl_nlmsg_batch_next(batch);
+ nft_counter_free(counter);
+
+ 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-counter-get.c b/examples/nft-counter-get.c
new file mode 100644
index 0000000..b30c8b2
--- /dev/null
+++ b/examples/nft-counter-get.c
@@ -0,0 +1,137 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ * (C) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2014 Intra2net AG <http://www.intra2net.com>
+ *
+ * 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/counter.h>
+
+static int counter_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_counter *counter;
+ char buf[4096];
+ uint32_t *type = data;
+
+ counter = nft_counter_alloc();
+ if (counter == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_counter_nlmsg_parse(nlh, counter) < 0) {
+ perror("nft_counter_nlmsg_parse");
+ goto err_free;
+ }
+ nft_counter_snprintf(buf, sizeof(buf), counter, *type, 0);
+ printf("%s\n", buf);
+
+err_free:
+ nft_counter_free(counter);
+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_counter *counter = NULL;
+ int ret;
+ uint32_t type = NFT_OUTPUT_DEFAULT;
+
+ if (argc < 4 || argc > 5) {
+ fprintf(stderr,
+ "%s <family> <table> <counter> [<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--;
+ }
+ counter = nft_counter_alloc();
+ if (counter == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ seq = time(NULL);
+ nlh = nft_counter_nlmsg_build_hdr(buf, NFT_MSG_GETCOUNTER, family,
+ NLM_F_ACK, seq);
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_NAME, argv[3]);
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE, argv[2]);
+ nft_counter_nlmsg_build_payload(nlh, counter);
+ nft_counter_free(counter);
+
+ 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, counter_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-counter-reset.c b/examples/nft-counter-reset.c
new file mode 100644
index 0000000..fc5775c
--- /dev/null
+++ b/examples/nft-counter-reset.c
@@ -0,0 +1,123 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ * (C) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2014 Intra2net AG <http://www.intra2net.com>
+ *
+ * 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/counter.h>
+
+static int counter_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_counter *counter;
+
+ counter = nft_counter_alloc();
+ if (counter == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_counter_nlmsg_parse(nlh, counter) < 0) {
+ perror("nft_counter_nlmsg_parse");
+ goto err_free;
+ }
+err_free:
+ nft_counter_free(counter);
+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_counter *counter = NULL;
+ int ret;
+ uint32_t type = NFT_OUTPUT_DEFAULT;
+
+ if (argc != 4) {
+ fprintf(stderr,
+ "%s <family> <table> <counter>\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);
+ }
+
+ counter = nft_counter_alloc();
+ if (counter == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ seq = time(NULL);
+ nlh = nft_counter_nlmsg_build_hdr(buf, NFT_MSG_GETCOUNTER_ZERO, family,
+ NLM_F_ACK, seq);
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_NAME, argv[3]);
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE, argv[2]);
+ nft_counter_nlmsg_build_payload(nlh, counter);
+ nft_counter_free(counter);
+
+ 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, counter_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-counters-get.c b/examples/nft-counters-get.c
new file mode 100644
index 0000000..b023325
--- /dev/null
+++ b/examples/nft-counters-get.c
@@ -0,0 +1,136 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ * (C) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2014 Intra2net AG <http://www.intra2net.com>
+ *
+ * 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/counter.h>
+
+static int counter_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_counter *counter;
+ char buf[4096];
+ uint32_t *type = data;
+
+ counter = nft_counter_alloc();
+ if (counter == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_counter_nlmsg_parse(nlh, counter) < 0) {
+ perror("nft_counter_nlmsg_parse");
+ goto err_free;
+ }
+ nft_counter_snprintf(buf, sizeof(buf), counter, *type, 0);
+ printf("%s\n", buf);
+
+err_free:
+ nft_counter_free(counter);
+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_counter *counter = NULL;
+ int ret;
+ uint32_t type = NFT_OUTPUT_DEFAULT;
+
+ if (argc < 3 || argc > 4) {
+ fprintf(stderr,
+ "%s <family> <table> [<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--;
+ }
+ counter = nft_counter_alloc();
+ if (counter == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ seq = time(NULL);
+ nlh = nft_counter_nlmsg_build_hdr(buf, NFT_MSG_GETCOUNTER, family,
+ NLM_F_DUMP | NLM_F_ACK, seq);
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE, argv[2]);
+ nft_counter_nlmsg_build_payload(nlh, counter);
+ nft_counter_free(counter);
+
+ 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, counter_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-add.c b/examples/nft-rule-add.c
index 6961d0d..f845c48 100644
--- a/examples/nft-rule-add.c
+++ b/examples/nft-rule-add.c
@@ -107,7 +107,7 @@ static struct nft_rule *setup_rule(uint8_t family, const char *table,
offsetof(struct iphdr, protocol), sizeof(uint8_t));
add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));
- dport = htons(22);
+ 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));
diff --git a/examples/nft-rule-counter-add.c b/examples/nft-rule-counter-add.c
new file mode 100644
index 0000000..6335102
--- /dev/null
+++ b/examples/nft-rule-counter-add.c
@@ -0,0 +1,221 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ * (C) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2014 Intra2net AG <http://www.intra2net.com>
+ *
+ * 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_counter(struct nft_rule *r)
+{
+ struct nft_rule_expr *e;
+
+ e = nft_rule_expr_alloc("counter");
+ if (e == NULL) {
+ perror("expr counter oom");
+ exit(EXIT_FAILURE);
+ }
+ nft_rule_expr_set_str(e, NFT_EXPR_CTR_NAME, "counter1");
+
+ 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_counter(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..aef7dbb 100644
--- a/include/buffer.h
+++ b/include/buffer.h
@@ -37,6 +37,7 @@ int nft_buf_reg(struct nft_buf *b, int type, union nft_data_reg *reg,
#define BYTES "bytes"
#define CHAIN "chain"
#define CODE "code"
+#define COUNTER "counter"
#define DATA "data"
#define DIR "dir"
#define DREG "dreg"
diff --git a/include/libnftnl/Makefile.am b/include/libnftnl/Makefile.am
index 010c01f..52051d0 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 \
+ counter.h
diff --git a/include/libnftnl/counter.h b/include/libnftnl/counter.h
new file mode 100644
index 0000000..55118e8
--- /dev/null
+++ b/include/libnftnl/counter.h
@@ -0,0 +1,97 @@
+#ifndef _LIBNFTNL_COUNTER_H_
+#define _LIBNFTNL_COUNTER_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_counter;
+
+struct nft_counter *nft_counter_alloc(void);
+void nft_counter_free(struct nft_counter *counter);
+
+enum {
+ NFT_COUNTER_ATTR_PKTS = 0,
+ NFT_COUNTER_ATTR_BYTES,
+ NFT_COUNTER_ATTR_NAME,
+ NFT_COUNTER_ATTR_TABLE,
+ NFT_COUNTER_ATTR_FAMILY,
+ NFT_COUNTER_ATTR_USE,
+ NFT_COUNTER_ATTR_ID,
+ __NFT_COUNTER_ATTR_MAX
+};
+#define NFT_COUNTER_ATTR_MAX (__NFT_COUNTER_ATTR_MAX - 1)
+
+bool nft_counter_attr_is_set(const struct nft_counter *counter, uint16_t attr);
+void nft_counter_attr_unset(struct nft_counter *counter, uint16_t attr);
+void nft_counter_attr_set(struct nft_counter *counter, uint16_t attr,
+ const void *data);
+void nft_counter_attr_set_data(struct nft_counter *counter, uint16_t attr,
+ const void *data, uint32_t data_len);
+const void *nft_counter_attr_get(struct nft_counter *counter, uint16_t attr);
+const void *nft_counter_attr_get_data(struct nft_counter *counter,
+ uint16_t attr, uint64_t *data_len);
+
+void nft_counter_attr_set_u32(struct nft_counter *counter, uint16_t attr,
+ uint32_t data);
+void nft_counter_attr_set_u64(struct nft_counter *counter, uint16_t attr,
+ uint64_t data);
+void nft_counter_attr_set_str(struct nft_counter *counter, uint16_t attr,
+ const char *str);
+uint32_t nft_counter_attr_get_u32(struct nft_counter *counter, uint16_t attr);
+uint64_t nft_counter_attr_get_u64(struct nft_counter *counter, uint16_t attr);
+const char *nft_counter_attr_get_str(struct nft_counter *counter,
+ uint16_t attr);
+
+struct nlmsghdr;
+
+void nft_counter_nlmsg_build_payload(struct nlmsghdr *nlh,
+ const struct nft_counter *counter);
+
+int nft_counter_parse(struct nft_counter *counter, enum nft_parse_type type,
+ const char *data, struct nft_parse_err *err);
+int nft_counter_parse_file(struct nft_counter *counter,
+ enum nft_parse_type type, FILE *fp,
+ struct nft_parse_err *err);
+int nft_counter_snprintf(char *buf, size_t size, struct nft_counter *counter,
+ uint32_t type, uint32_t flags);
+int nft_counter_fprintf(FILE *fp, struct nft_counter *counter, uint32_t type,
+ uint32_t flags);
+
+#define nft_counter_nlmsg_build_hdr nft_nlmsg_build_hdr
+int nft_counter_nlmsg_parse(const struct nlmsghdr *nlh,
+ struct nft_counter *counter);
+struct nft_counter_list;
+
+struct nft_counter_list *nft_counter_list_alloc(void);
+void nft_counter_list_free(struct nft_counter_list *list);
+int nft_counter_list_is_empty(struct nft_counter_list *list);
+int nft_counter_list_foreach(struct nft_counter_list *counter_list,
+ int (*cb)(struct nft_counter *counter, void *data),
+ void *data);
+void nft_counter_list_add(struct nft_counter *counter,
+ struct nft_counter_list *list);
+void nft_counter_list_add_tail(struct nft_counter *counter,
+ struct nft_counter_list *list);
+void nft_counter_list_del(struct nft_counter *counter);
+
+struct nft_counter_list_iter;
+
+struct nft_counter_list_iter *
+nft_counter_list_iter_create(struct nft_counter_list *l);
+struct nft_counter *
+nft_counter_list_iter_next(struct nft_counter_list_iter *iter);
+void nft_counter_list_iter_destroy(struct nft_counter_list_iter *iter);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* _LIBNFTNL_COUNTER_H_ */
diff --git a/include/libnftnl/expr.h b/include/libnftnl/expr.h
index 9f25993..263d37a 100644
--- a/include/libnftnl/expr.h
+++ b/include/libnftnl/expr.h
@@ -71,6 +71,7 @@ enum {
enum {
NFT_EXPR_CTR_PACKETS = NFT_RULE_EXPR_ATTR_BASE,
NFT_EXPR_CTR_BYTES,
+ NFT_EXPR_CTR_NAME,
};
enum {
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 832bc46..5eeb60c 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -2,6 +2,7 @@
#define _LINUX_NF_TABLES_H
#define NFT_CHAIN_MAXNAMELEN 32
+#define NFT_CTR_MAXNAMELEN 16
#define NFT_USERDATA_MAXLEN 256
enum nft_registers {
@@ -53,6 +54,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_NEWCOUNTER: create a new counter (enum nft_counter_attributes)
+ * @NFT_MSG_GETCOUNTER: get a counter (enum nft_counter_attributes)
+ * @NFT_MSG_GETCOUNTER_ZERO: get a reset counter (enum nft_counter_attributes)
+ * @NFT_MSG_DELCOUNTER: delete a counter (enum nft_counter_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -72,6 +77,10 @@ enum nf_tables_msg_types {
NFT_MSG_DELSETELEM,
NFT_MSG_NEWGEN,
NFT_MSG_GETGEN,
+ NFT_MSG_NEWCOUNTER,
+ NFT_MSG_GETCOUNTER,
+ NFT_MSG_GETCOUNTER_ZERO,
+ NFT_MSG_DELCOUNTER,
NFT_MSG_MAX,
};
@@ -695,16 +704,40 @@ enum nft_limit_attributes {
*
* @NFTA_COUNTER_BYTES: number of bytes (NLA_U64)
* @NFTA_COUNTER_PACKETS: number of packets (NLA_U64)
+ * @NFTA_COUNTER_NAME: name of the counter (NLA_STRING)
*/
enum nft_counter_attributes {
NFTA_COUNTER_UNSPEC,
NFTA_COUNTER_BYTES,
NFTA_COUNTER_PACKETS,
+ NFTA_COUNTER_NAME,
__NFTA_COUNTER_MAX
};
#define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1)
/**
+ * enum nft_named_counter_attributes - nf_tables named counter netlink attributes
+ *
+ * @NFTA_NAMED_CTR_NAME: named counter name (NLA_STRING)
+ * @NFTA_NAMED_CTR_TABLE: table name (NLA_STRING)
+ * @NFTA_NAMED_CTR_USE: number of references to this named counter (NLA_U32)
+ * @NFTA_NAMED_CTR_ID: uniquely identifies a named counter in a transaction (NLA_U32)
+ * @NFTA_NAMED_CTR_BYTES: number of bytes (NLA_U64)
+ * @NFTA_NAMED_CTR_PACKETS: number of packets (NLA_U64)
+ */
+enum nft_named_counter_attributes {
+ NFTA_NAMED_CTR_UNSPEC,
+ NFTA_NAMED_CTR_NAME,
+ NFTA_NAMED_CTR_TABLE,
+ NFTA_NAMED_CTR_USE,
+ NFTA_NAMED_CTR_ID,
+ NFTA_NAMED_CTR_BYTES,
+ NFTA_NAMED_CTR_PACKETS,
+ __NFTA_NAMED_CTR_MAX
+};
+#define NFTA_NAMED_CTR_MAX (__NFTA_NAMED_CTR_MAX - 1)
+
+/**
* enum nft_log_attributes - nf_tables log expression netlink attributes
*
* @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
diff --git a/src/Makefile.am b/src/Makefile.am
index c77c3cc..934fa9b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ libnftnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnftnl.map \
libnftnl_la_SOURCES = utils.c \
buffer.c \
common.c \
+ counter.c \
gen.c \
table.c \
chain.c \
diff --git a/src/counter.c b/src/counter.c
new file mode 100644
index 0000000..1ee58ae
--- /dev/null
+++ b/src/counter.c
@@ -0,0 +1,671 @@
+/*
+ * (C) 2014 by Ana Rey Botello <ana@soleta.eu>
+ * (C) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2014 Intra2net AG <http://www.intra2net.com>
+ *
+ * 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/counter.h>
+#include <buffer.h>
+
+struct nft_counter {
+ struct list_head list;
+ const char *name;
+ const char *table;
+ uint32_t family;
+ uint64_t pkts;
+ uint64_t bytes;
+ uint32_t use;
+ uint32_t flags;
+};
+
+struct nft_counter *nft_counter_alloc(void)
+{
+ struct nft_counter *counter;
+
+ counter = calloc(1, sizeof(struct nft_counter));
+ if (counter == NULL)
+ return NULL;
+
+ return counter;
+}
+EXPORT_SYMBOL(nft_counter_alloc);
+
+void nft_counter_free(struct nft_counter *counter)
+{
+ if (counter->name != NULL)
+ xfree(counter->name);
+ if (counter->table != NULL)
+ xfree(counter->table);
+
+ xfree(counter);
+}
+EXPORT_SYMBOL(nft_counter_free);
+
+bool nft_counter_attr_is_set(const struct nft_counter *counter, uint16_t attr)
+{
+ return counter->flags & (1 << attr);
+}
+EXPORT_SYMBOL(nft_counter_attr_is_set);
+
+void nft_counter_attr_unset(struct nft_counter *counter, uint16_t attr)
+{
+ if (!(counter->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFT_COUNTER_ATTR_NAME:
+ if (counter->name) {
+ xfree(counter->name);
+ counter->name = NULL;
+ }
+ break;
+ case NFT_COUNTER_ATTR_TABLE:
+ if (counter->table) {
+ xfree(counter->table);
+ counter->table = NULL;
+ }
+ break;
+ case NFT_COUNTER_ATTR_BYTES:
+ case NFT_COUNTER_ATTR_PKTS:
+ case NFT_COUNTER_ATTR_USE:
+ case NFT_COUNTER_ATTR_FAMILY:
+ break;
+ }
+ counter->flags &= ~(1 << attr);
+}
+EXPORT_SYMBOL(nft_counter_attr_unset);
+
+static uint32_t nft_counter_attr_validate[NFT_COUNTER_ATTR_MAX + 1] = {
+ [NFT_COUNTER_ATTR_BYTES] = sizeof(uint64_t),
+ [NFT_COUNTER_ATTR_PKTS] = sizeof(uint64_t),
+ [NFT_COUNTER_ATTR_FAMILY] = sizeof(uint32_t),
+};
+
+void nft_counter_attr_set_data(struct nft_counter *counter, uint16_t attr,
+ const void *data, uint32_t data_len)
+{
+ if (attr > NFT_COUNTER_ATTR_MAX)
+ return;
+
+ nft_assert_validate(data, nft_counter_attr_validate, attr, data_len);
+
+ switch (attr) {
+ case NFT_COUNTER_ATTR_NAME:
+ if (counter->name)
+ xfree(counter->name);
+
+ counter->name = strdup(data);
+ break;
+ case NFT_COUNTER_ATTR_TABLE:
+ if (counter->table)
+ xfree(counter->table);
+
+ counter->table = strdup(data);
+ break;
+ case NFT_COUNTER_ATTR_BYTES:
+ counter->bytes = *((uint64_t *)data);
+ break;
+ case NFT_COUNTER_ATTR_PKTS:
+ counter->pkts = *((uint64_t *)data);
+ break;
+ case NFT_COUNTER_ATTR_USE:
+ counter->use = *((uint32_t *)data);
+ break;
+ case NFT_COUNTER_ATTR_FAMILY:
+ counter->family = *((uint32_t *)data);
+ break;
+ }
+ counter->flags |= (1 << attr);
+}
+EXPORT_SYMBOL(nft_counter_attr_set_data);
+
+void nft_counter_attr_set(struct nft_counter *counter, uint16_t attr,
+ const void *data)
+{
+ nft_counter_attr_set_data(counter, attr, data,
+ nft_counter_attr_validate[attr]);
+}
+EXPORT_SYMBOL(nft_counter_attr_set);
+
+void nft_counter_attr_set_u64(struct nft_counter *counter, uint16_t attr,
+ uint64_t val)
+{
+ nft_counter_attr_set_data(counter, attr, &val, sizeof(uint64_t));
+}
+EXPORT_SYMBOL(nft_counter_attr_set_u64);
+
+void nft_counter_attr_set_u32(struct nft_counter *counter, uint16_t attr,
+ uint32_t val)
+{
+ nft_counter_attr_set_data(counter, attr, &val, sizeof(uint32_t));
+}
+EXPORT_SYMBOL(nft_counter_attr_set_u32);
+
+void nft_counter_attr_set_str(struct nft_counter *counter, uint16_t attr,
+ const char *str)
+{
+ nft_counter_attr_set_data(counter, attr, str, 0);
+}
+EXPORT_SYMBOL(nft_counter_attr_set_str);
+
+const void *nft_counter_attr_get_data(struct nft_counter *counter,
+ uint16_t attr, uint64_t *data_len)
+{
+ if (!(counter->flags & (1 << attr)))
+ return NULL;
+
+ switch (attr) {
+ case NFT_COUNTER_ATTR_NAME:
+ return counter->name;
+ case NFT_COUNTER_ATTR_TABLE:
+ return counter->table;
+ case NFT_COUNTER_ATTR_BYTES:
+ *data_len = sizeof(uint64_t);
+ return &counter->bytes;
+ case NFT_COUNTER_ATTR_PKTS:
+ *data_len = sizeof(uint64_t);
+ return &counter->pkts;
+ case NFT_COUNTER_ATTR_FAMILY:
+ *data_len = sizeof(uint32_t);
+ return &counter->family;
+ case NFT_COUNTER_ATTR_USE:
+ *data_len = sizeof(uint32_t);
+ return &counter->use;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(nft_counter_attr_get_data);
+
+const void *nft_counter_attr_get(struct nft_counter *counter, uint16_t attr)
+{
+ uint64_t data_len;
+
+ return nft_counter_attr_get_data(counter, attr, &data_len);
+}
+EXPORT_SYMBOL(nft_counter_attr_get);
+
+uint64_t nft_counter_attr_get_u64(struct nft_counter *counter, uint16_t attr)
+{
+ const void *ret = nft_counter_attr_get(counter, attr);
+
+ return ret == NULL ? 0 : *((uint64_t *)ret);
+}
+EXPORT_SYMBOL(nft_counter_attr_get_u64);
+
+uint32_t nft_counter_attr_get_u32(struct nft_counter *counter, uint16_t attr)
+{
+ const void *ret = nft_counter_attr_get(counter, attr);
+
+ return ret == NULL ? 0 : *((uint32_t *)ret);
+}
+EXPORT_SYMBOL(nft_counter_attr_get_u32);
+
+const char *nft_counter_attr_get_str(struct nft_counter *counter, uint16_t attr)
+{
+ return nft_counter_attr_get(counter, attr);
+}
+EXPORT_SYMBOL(nft_counter_attr_get_str);
+
+void nft_counter_nlmsg_build_payload(struct nlmsghdr *nlh,
+ const struct nft_counter *counter)
+{
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_NAME))
+ mnl_attr_put_strz(nlh, NFTA_NAMED_CTR_NAME, counter->name);
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_NAMED_CTR_TABLE, counter->table);
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_BYTES)) {
+ mnl_attr_put_u64(nlh, NFTA_NAMED_CTR_BYTES,
+ htobe64(counter->bytes));
+ }
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_PKTS)) {
+ mnl_attr_put_u64(nlh, NFTA_NAMED_CTR_PACKETS,
+ htobe64(counter->pkts));
+ }
+}
+EXPORT_SYMBOL(nft_counter_nlmsg_build_payload);
+
+static int nft_counter_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_NAMED_CTR_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_NAMED_CTR_NAME:
+ case NFTA_NAMED_CTR_TABLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_NAMED_CTR_BYTES:
+ case NFTA_NAMED_CTR_PACKETS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_NAMED_CTR_USE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+int nft_counter_nlmsg_parse(const struct nlmsghdr *nlh,
+ struct nft_counter *counter)
+{
+ struct nlattr *tb[NFTA_NAMED_CTR_MAX + 1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nft_counter_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_NAMED_CTR_NAME]) {
+ counter->name =
+ strdup(mnl_attr_get_str(tb[NFTA_NAMED_CTR_NAME]));
+ counter->flags |= (1 << NFT_COUNTER_ATTR_NAME);
+ }
+ if (tb[NFTA_NAMED_CTR_TABLE]) {
+ counter->table =
+ strdup(mnl_attr_get_str(tb[NFTA_NAMED_CTR_TABLE]));
+ counter->flags |= (1 << NFT_COUNTER_ATTR_TABLE);
+ }
+ if (tb[NFTA_NAMED_CTR_BYTES]) {
+ counter->bytes =
+ be64toh(mnl_attr_get_u64(tb[NFTA_NAMED_CTR_BYTES]));
+ counter->flags |= (1 << NFT_COUNTER_ATTR_BYTES);
+ }
+ if (tb[NFTA_NAMED_CTR_PACKETS]) {
+ counter->pkts =
+ be64toh(mnl_attr_get_u64(tb[NFTA_NAMED_CTR_PACKETS]));
+ counter->flags |= (1 << NFT_COUNTER_ATTR_PKTS);
+ }
+ if (tb[NFTA_NAMED_CTR_USE]) {
+ counter->use = ntohl(mnl_attr_get_u32(tb[NFTA_TABLE_USE]));
+ counter->flags |= (1 << NFT_COUNTER_ATTR_USE);
+ }
+
+ counter->family = nfg->nfgen_family;
+ counter->flags |= (1 << NFT_COUNTER_ATTR_FAMILY);
+
+ return 0;
+}
+EXPORT_SYMBOL(nft_counter_nlmsg_parse);
+
+#ifdef XML_PARSING
+int nft_mxml_counter_parse(mxml_node_t *tree, struct nft_counter *counter,
+ struct nft_parse_err *err)
+{
+ const char *name, *table;
+ uint64_t pkts, bytes;
+ uint32_t family, use;
+
+ name = nft_mxml_str_parse(tree, "counter", MXML_DESCEND_FIRST,
+ NFT_XML_MAND, err);
+ if (name != NULL)
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_NAME, name);
+
+ table = nft_mxml_str_parse(tree, "table", MXML_DESCEND_FIRST,
+ NFT_XML_MAND, err);
+ if (table != NULL) {
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE,
+ table);
+ }
+ if (nft_mxml_num_parse(tree, "family", MXML_DESCEND, BASE_DEC,
+ &family, NFT_TYPE_U32, NFT_XML_MAND,
+ err) == 0) {
+ nft_counter_attr_set_u32(counter, NFT_COUNTER_ATTR_FAMILY,
+ family);
+ }
+
+ if (nft_mxml_num_parse(tree, "pkts", MXML_DESCEND, BASE_DEC,
+ &pkts, NFT_TYPE_U64, NFT_XML_MAND, err) == 0)
+ nft_counter_attr_set_u64(counter, NFT_COUNTER_ATTR_PKTS, pkts);
+
+ if (nft_mxml_num_parse(tree, "bytes", MXML_DESCEND, BASE_DEC,
+ &bytes, NFT_TYPE_U64, NFT_XML_MAND, err) == 0) {
+ nft_counter_attr_set_u64(counter, NFT_COUNTER_ATTR_BYTES,
+ bytes);
+ }
+ if (nft_mxml_num_parse(tree, "use", MXML_DESCEND, BASE_DEC,
+ &use, NFT_TYPE_U32, NFT_XML_MAND, err) == 0)
+ nft_counter_attr_set_u32(counter, NFT_COUNTER_ATTR_USE, use);
+
+
+ return 0;
+}
+#endif
+
+static int nft_counter_xml_parse(struct nft_counter *counter, 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, "counter", err, input);
+
+ if (tree == NULL)
+ return -1;
+
+ ret = nft_mxml_counter_parse(tree, counter, err);
+ mxmlDelete(tree);
+ return ret;
+#else
+ errno = EOPNOTSUPP;
+ return -1;
+#endif
+}
+
+#ifdef JSON_PARSING
+int nft_jansson_parse_counter(struct nft_counter *counter, json_t *tree,
+ struct nft_parse_err *err)
+{
+ json_t *root;
+ const char *name, *table;
+ uint64_t bytes, pkts;
+ uint32_t family, use;
+
+ root = nft_jansson_get_node(tree, "counter", err);
+ if (root == NULL)
+ return -1;
+
+ name = nft_jansson_parse_str(root, "name", err);
+ if (name != NULL)
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_NAME, name);
+
+ table = nft_jansson_parse_str(root, "table", err);
+ if (table != NULL)
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE, table);
+
+ if (nft_jansson_parse_val(root, "family", NFT_TYPE_U32, &family,
+ err) == 0)
+ nft_counter_attr_set_u32(counter, NFT_COUNTER_ATTR_PKTS, family);
+
+ if (nft_jansson_parse_val(root, "pkts", NFT_TYPE_U64, &pkts, err) == 0)
+ nft_counter_attr_set_u64(counter, NFT_COUNTER_ATTR_PKTS, pkts);
+
+ if (nft_jansson_parse_val(root, "bytes", NFT_TYPE_U64, &bytes,
+ err) == 0)
+ nft_counter_attr_set_u64(counter, NFT_COUNTER_ATTR_BYTES, bytes);
+
+ if (nft_jansson_parse_val(root, "use", NFT_TYPE_U32, &use,
+ err) == 0)
+ nft_counter_attr_set_u32(counter, NFT_COUNTER_ATTR_USE, use);
+
+ return 0;
+}
+#endif
+
+static int nft_counter_json_parse(struct nft_counter *counter, 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_counter(counter, tree, err);
+
+ nft_jansson_free_root(tree);
+
+ return ret;
+#else
+ errno = EOPNOTSUPP;
+ return -1;
+#endif
+}
+
+static int nft_counter_do_parse(struct nft_counter *counter,
+ 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_counter_xml_parse(counter, data, &perr, input);
+ break;
+ case NFT_PARSE_JSON:
+ ret = nft_counter_json_parse(counter, data, &perr, input);
+ break;
+ default:
+ ret = -1;
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (err != NULL)
+ *err = perr;
+
+ return ret;
+}
+
+int nft_counter_parse(struct nft_counter *counter, enum nft_parse_type type,
+ const char *data, struct nft_parse_err *err)
+{
+ return nft_counter_do_parse(counter, type, data, err, NFT_PARSE_BUFFER);
+}
+EXPORT_SYMBOL(nft_counter_parse);
+
+int nft_counter_parse_file(struct nft_counter *counter,
+ enum nft_parse_type type, FILE *fp,
+ struct nft_parse_err *err)
+{
+ return nft_counter_do_parse(counter, type, fp, err, NFT_PARSE_FILE);
+}
+EXPORT_SYMBOL(nft_counter_parse_file);
+
+static int nft_counter_export(char *buf, size_t size,
+ struct nft_counter *counter, int type)
+{
+ NFT_BUF_INIT(b, buf, size);
+ nft_buf_open(&b, type, COUNTER);
+
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_NAME))
+ nft_buf_str(&b, type, counter->name, NAME);
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_TABLE))
+ nft_buf_str(&b, type, counter->table, TABLE);
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_FAMILY))
+ nft_buf_str(&b, type, nft_family2str(counter->family), FAMILY);
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_PKTS))
+ nft_buf_u64(&b, type, counter->pkts, PACKETS);
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_BYTES))
+ nft_buf_u64(&b, type, counter->bytes, BYTES);
+ if (counter->flags & (1 << NFT_COUNTER_ATTR_USE))
+ nft_buf_u32(&b, type, counter->use, USE);
+ nft_buf_close(&b, type, COUNTER);
+
+ return nft_buf_done(&b);
+}
+
+static int nft_counter_snprintf_default(char *buf, size_t size,
+ struct nft_counter *counter)
+{
+ return snprintf(buf, size,
+ "table %s family %s counter %s packet %"PRIu64" bytes %"PRIu64" use %d",
+ counter->table,
+ nft_family2str(counter->family),
+ counter->name,
+ counter->pkts,
+ counter->bytes,
+ counter->use);
+}
+
+int nft_counter_snprintf(char *buf, size_t len, struct nft_counter *counter,
+ uint32_t type, uint32_t flags)
+{
+ switch (type) {
+ case NFT_OUTPUT_DEFAULT:
+ return nft_counter_snprintf_default(buf, len, counter);
+ case NFT_OUTPUT_XML:
+ case NFT_OUTPUT_JSON:
+ return nft_counter_export(buf, len, counter, type);
+ default:
+ break;
+ }
+ return -1;
+}
+EXPORT_SYMBOL(nft_counter_snprintf);
+
+static int nft_counter_do_snprintf(char *buf, size_t size, void *counter,
+ uint32_t type, uint32_t flags)
+{
+ return nft_counter_snprintf(buf, size, counter, type, flags);
+}
+
+int nft_counter_fprintf(FILE *fp, struct nft_counter *counter, uint32_t type,
+ uint32_t flags)
+{
+ return nft_fprintf(fp, counter, type, flags, nft_counter_do_snprintf);
+}
+EXPORT_SYMBOL(nft_counter_fprintf);
+
+struct nft_counter_list {
+ struct list_head list;
+};
+
+struct nft_counter_list *nft_counter_list_alloc(void)
+{
+ struct nft_counter_list *list;
+
+ list = calloc(1, sizeof(struct nft_counter_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+
+ return list;
+}
+EXPORT_SYMBOL(nft_counter_list_alloc);
+
+void nft_counter_list_free(struct nft_counter_list *list)
+{
+ struct nft_counter *counter, *tmp;
+
+ list_for_each_entry_safe(counter, tmp, &list->list, list) {
+ list_del(&counter->list);
+ nft_counter_free(counter);
+ }
+ xfree(list);
+}
+EXPORT_SYMBOL(nft_counter_list_free);
+
+int nft_counter_list_is_empty(struct nft_counter_list *list)
+{
+ return list_empty(&list->list);
+}
+EXPORT_SYMBOL(nft_counter_list_is_empty);
+
+void nft_counter_list_add(struct nft_counter *counter,
+ struct nft_counter_list *list)
+{
+ list_add(&counter->list, &list->list);
+}
+EXPORT_SYMBOL(nft_counter_list_add);
+
+void nft_counter_list_add_tail(struct nft_counter *counter,
+ struct nft_counter_list *list)
+{
+ list_add_tail(&counter->list, &list->list);
+}
+EXPORT_SYMBOL(nft_counter_list_add_tail);
+
+void nft_counter_list_del(struct nft_counter *counter)
+{
+ list_del(&counter->list);
+}
+EXPORT_SYMBOL(nft_counter_list_del);
+
+int nft_counter_list_foreach(struct nft_counter_list *counter_list,
+ int (*cb)(struct nft_counter *counter, void *data),
+ void *data)
+{
+ struct nft_counter *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &counter_list->list, list) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(nft_counter_list_foreach);
+
+struct nft_counter_list_iter {
+ struct nft_counter_list *list;
+ struct nft_counter *cur;
+};
+
+struct nft_counter_list_iter *
+nft_counter_list_iter_create(struct nft_counter_list *l)
+{
+ struct nft_counter_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nft_counter_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ if (nft_counter_list_is_empty(l))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(l->list.next, struct nft_counter, list);
+
+ return iter;
+}
+EXPORT_SYMBOL(nft_counter_list_iter_create);
+
+struct nft_counter *
+nft_counter_list_iter_next(struct nft_counter_list_iter *iter)
+{
+ struct nft_counter *counter = iter->cur;
+
+ if (!counter)
+ return NULL;
+
+ iter->cur = list_entry(iter->cur->list.next, struct nft_counter, list);
+ if (&iter->cur->list == iter->list->list.next)
+ return NULL;
+
+ return counter;
+}
+EXPORT_SYMBOL(nft_counter_list_iter_next);
+
+void nft_counter_list_iter_destroy(struct nft_counter_list_iter *iter)
+{
+ xfree(iter);
+}
+EXPORT_SYMBOL(nft_counter_list_iter_destroy);
diff --git a/src/expr/counter.c b/src/expr/counter.c
index e9abc5b..7d9a7bc 100644
--- a/src/expr/counter.c
+++ b/src/expr/counter.c
@@ -27,6 +27,7 @@
struct nft_expr_counter {
uint64_t pkts;
uint64_t bytes;
+ const char *name;
};
static int
@@ -42,6 +43,11 @@ nft_rule_expr_counter_set(struct nft_rule_expr *e, uint16_t type,
case NFT_EXPR_CTR_PACKETS:
ctr->pkts = *((uint64_t *)data);
break;
+ case NFT_EXPR_CTR_NAME:
+ if (ctr->name)
+ xfree(ctr->name);
+ ctr->name = strdup(data);
+ break;
default:
return -1;
}
@@ -61,6 +67,9 @@ nft_rule_expr_counter_get(const struct nft_rule_expr *e, uint16_t type,
case NFT_EXPR_CTR_PACKETS:
*data_len = sizeof(ctr->pkts);
return &ctr->pkts;
+ case NFT_EXPR_CTR_NAME:
+ *data_len = sizeof(ctr->name);
+ return ctr->name;
}
return NULL;
}
@@ -79,6 +88,10 @@ static int nft_rule_expr_counter_cb(const struct nlattr *attr, void *data)
if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
abi_breakage();
break;
+ case NFTA_COUNTER_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
+ abi_breakage();
+ break;
}
tb[type] = attr;
@@ -94,6 +107,8 @@ nft_rule_expr_counter_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, htobe64(ctr->bytes));
if (e->flags & (1 << NFT_EXPR_CTR_PACKETS))
mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, htobe64(ctr->pkts));
+ if (e->flags & (1 << NFT_EXPR_CTR_NAME))
+ mnl_attr_put_strz(nlh, NFTA_COUNTER_NAME, ctr->name);
}
static int
@@ -113,6 +128,12 @@ nft_rule_expr_counter_parse(struct nft_rule_expr *e, struct nlattr *attr)
ctr->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS]));
e->flags |= (1 << NFT_EXPR_CTR_PACKETS);
}
+ if (tb[NFTA_COUNTER_NAME]) {
+ if (ctr->name)
+ xfree(ctr->name);
+ ctr->name = strdup(mnl_attr_get_str(tb[NFTA_COUNTER_NAME]));
+ e->flags |= (1 << NFT_EXPR_CTR_NAME);
+ }
return 0;
}
@@ -123,6 +144,7 @@ nft_rule_expr_counter_json_parse(struct nft_rule_expr *e, json_t *root,
{
#ifdef JSON_PARSING
uint64_t uval64;
+ const char *counter_name;
if (nft_jansson_parse_val(root, "pkts", NFT_TYPE_U64, &uval64,
err) == 0)
@@ -132,6 +154,10 @@ nft_rule_expr_counter_json_parse(struct nft_rule_expr *e, json_t *root,
err) == 0)
nft_rule_expr_set_u64(e, NFT_EXPR_CTR_BYTES, uval64);
+ counter_name = nft_jansson_parse_str(root, "name", err);
+ if (counter_name != NULL)
+ nft_rule_expr_set_str(e, NFT_EXPR_CTR_NAME, counter_name);
+
return 0;
#else
errno = EOPNOTSUPP;
@@ -145,6 +171,7 @@ nft_rule_expr_counter_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
{
#ifdef XML_PARSING
uint64_t pkts, bytes;
+ const char *counter_name;
if (nft_mxml_num_parse(tree, "pkts", MXML_DESCEND_FIRST, BASE_DEC,
&pkts, NFT_TYPE_U64, NFT_XML_MAND, err) == 0)
@@ -154,6 +181,12 @@ nft_rule_expr_counter_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
&bytes, NFT_TYPE_U64, NFT_XML_MAND, err) == 0)
nft_rule_expr_set_u64(e, NFT_EXPR_CTR_BYTES, bytes);
+ counter_name = nft_mxml_str_parse(tree, "name", MXML_DESCEND_FIRST,
+ NFT_XML_MAND, err);
+
+ if (counter_name != NULL)
+ nft_rule_expr_set_str(e, NFT_EXPR_CTR_NAME, counter_name);
+
return 0;
#else
errno = EOPNOTSUPP;
@@ -161,6 +194,7 @@ nft_rule_expr_counter_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
#endif
}
+
static int nft_rule_expr_counter_export(char *buf, size_t size,
struct nft_rule_expr *e, int type)
{
@@ -171,6 +205,8 @@ static int nft_rule_expr_counter_export(char *buf, size_t size,
nft_buf_u64(&b, type, ctr->pkts, PKTS);
if (e->flags & (1 << NFT_EXPR_CTR_BYTES))
nft_buf_u64(&b, type, ctr->bytes, BYTES);
+ if (e->flags & (1 << NFT_EXPR_CTR_NAME))
+ nft_buf_str(&b, type, ctr->name, NAME);
return nft_buf_done(&b);
}
@@ -179,9 +215,17 @@ static int nft_rule_expr_counter_snprintf_default(char *buf, size_t len,
struct nft_rule_expr *e)
{
struct nft_expr_counter *ctr = nft_expr_data(e);
+ int size = len, offset = 0, ret = 0;
+
+ if (e->flags & (1 << NFT_EXPR_CTR_NAME)) {
+ ret = snprintf(buf, len, "%s ", ctr->name);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+ ret = snprintf(buf + offset, len, "pkts %"PRIu64" bytes %"PRIu64" ",
+ ctr->pkts, ctr->bytes);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- return snprintf(buf, len, "pkts %"PRIu64" bytes %"PRIu64" ",
- ctr->pkts, ctr->bytes);
+ return offset;
}
static int nft_rule_expr_counter_snprintf(char *buf, size_t len, uint32_t type,
diff --git a/src/internal.h b/src/internal.h
index db9af11..4d32ea3 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -90,6 +90,9 @@ int nft_mxml_rule_parse(mxml_node_t *tree, struct nft_rule *r,
struct nft_set;
int nft_mxml_set_parse(mxml_node_t *tree, struct nft_set *s,
struct nft_parse_err *err);
+struct nft_counter;
+int nft_mxml_counter_parse(mxml_node_t *tree, struct nft_counter *counter,
+ struct nft_parse_err *err);
#endif
struct nft_set_list;
@@ -139,6 +142,9 @@ int nft_jansson_parse_rule(struct nft_rule *r, json_t *tree,
struct nft_set;
int nft_jansson_parse_set(struct nft_set *s, json_t *tree,
struct nft_parse_err *err);
+struct nft_counter;
+int nft_jansson_parse_counter(struct nft_counter *counter, json_t *tree,
+ struct nft_parse_err *err);
#endif
const char *nft_family2str(uint32_t family);
diff --git a/src/libnftnl.map b/src/libnftnl.map
index be7b998..3f3a9e9 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_counter_alloc;
+ nft_counter_free;
+ nft_counter_attr_is_set;
+ nft_counter_attr_unset;
+ nft_counter_attr_set;
+ nft_counter_attr_get;
+ nft_counter_attr_set_u64;
+ nft_counter_attr_set_u32;
+ nft_counter_attr_set_str;
+ nft_counter_attr_get_u64;
+ nft_counter_attr_get_u32;
+ nft_counter_attr_get_str;
+ nft_counter_parse;
+ nft_counter_parse_file;
+ nft_counter_snprintf;
+ nft_counter_fprintf;
+ nft_counter_nlmsg_build_payload;
+ nft_counter_nlmsg_parse;
+ nft_counter_list_alloc;
+ nft_counter_list_free;
+ nft_counter_list_is_empty;
+ nft_counter_list_foreach;
+ nft_counter_list_add;
+ nft_counter_list_add_tail;
+ nft_counter_list_del;
+ nft_counter_list_iter_create;
+ nft_counter_list_iter_next;
+ nft_counter_list_iter_destroy;
+
} LIBNFTNL_1.1;
--
1.7.10.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [nft v2 1/2] src: Add the accounter support
[not found] ` <cover.1422299750.git.ana@soleta.eu>
@ 2015-01-26 19:43 ` Ana Rey Botello
2015-01-26 19:43 ` [nft v2 2/2] tests: regression: Add counters support Ana Rey Botello
1 sibling, 0 replies; 7+ messages in thread
From: Ana Rey Botello @ 2015-01-26 19:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Ana Rey
From: Ana Rey <ana@soleta.eu>
This adds userspace support to accounting objects and expression.
Example of use in nft:
# nft add counter ip filter http-traffic
# nft add counter ip filter https-traffic
# nft add rule ip filter output tcp dport 80 counter name http-traffic
# nft add rule ip filter output tcp dport 443 counter name https-traffic
# nft delete counter ip filter https-traffic
Generate Some traffic:
# nft list table ip test
table ip filter {
counter http-traffic { pkts 779 bytes 99495}
counter https-traffic { pkts 189 bytes 37824}
chain output {
type filter hook output priority 0;
tcp dport http counter http-traffic
tcp dport https counter https-traffic
}
}
The kernel support is added in the commit:
"netfilter: named counter: add support to counters in nftables"
The libnftnl support is added in the commit:
"src: Add accounters support"
Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
include/linux/netfilter/nf_tables.h | 32 +++++
include/mnl.h | 8 ++
include/netlink.h | 22 ++++
include/rule.h | 47 +++++++
include/statement.h | 1 +
src/evaluate.c | 13 +-
src/mnl.c | 119 ++++++++++++++++++
src/netlink.c | 235 +++++++++++++++++++++++++++++++++++
src/netlink_delinearize.c | 3 +
src/netlink_linearize.c | 4 +
src/parser_bison.y | 60 ++++++++-
src/rule.c | 139 +++++++++++++++++++++
src/scanner.l | 1 +
src/statement.c | 8 +-
14 files changed, 685 insertions(+), 7 deletions(-)
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 832bc46..b2d8028 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_NEWCOUNTER: create a new counter (enum nft_counter_attributes)
+ * @NFT_MSG_GETCOUNTER: get a counter (enum nft_counter_attributes)
+ * @NFT_MSG_GETCOUNTER_ZERO: get a reset counter (enum nft_counter_attributes)
+ * @NFT_MSG_DELCOUNTER: delete a counter (enum nft_counter_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_NEWCOUNTER,
+ NFT_MSG_GETCOUNTER,
+ NFT_MSG_GETCOUNTER_ZERO,
+ NFT_MSG_DELCOUNTER,
NFT_MSG_MAX,
};
@@ -695,16 +703,40 @@ enum nft_limit_attributes {
*
* @NFTA_COUNTER_BYTES: number of bytes (NLA_U64)
* @NFTA_COUNTER_PACKETS: number of packets (NLA_U64)
+ * @NFTA_COUNTER_NAME: name of the counter (NLA_STRING)
*/
enum nft_counter_attributes {
NFTA_COUNTER_UNSPEC,
NFTA_COUNTER_BYTES,
NFTA_COUNTER_PACKETS,
+ NFTA_COUNTER_NAME,
__NFTA_COUNTER_MAX
};
#define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1)
/**
+ * enum nft_named_counter_attributes - nf_tables named counter netlink attributes
+ *
+ * @NFTA_NAMED_CTR_NAME: named counter name (NLA_STRING)
+ * @NFTA_NAMED_CTR_TABLE: table name (NLA_STRING)
+ * @NFTA_NAMED_CTR_USE: number of references to this named counter (NLA_U32)
+ * @NFTA_NAMED_CTR_ID: uniquely identifies a named counter in a transaction (NLA_U32)
+ * @NFTA_NAMED_CTR_BYTES: number of bytes (NLA_U64)
+ * @NFTA_NAMED_CTR_PACKETS: number of packets (NLA_U64)
+ */
+enum nft_named_counter_attributes {
+ NFTA_NAMED_CTR_UNSPEC,
+ NFTA_NAMED_CTR_NAME,
+ NFTA_NAMED_CTR_TABLE,
+ NFTA_NAMED_CTR_USE,
+ NFTA_NAMED_CTR_ID,
+ NFTA_NAMED_CTR_BYTES,
+ NFTA_NAMED_CTR_PACKETS,
+ __NFTA_NAMED_CTR_MAX
+};
+#define NFTA_NAMED_CTR_MAX (__NFTA_NAMED_CTR_MAX - 1)
+
+/**
* enum nft_log_attributes - nf_tables log expression netlink attributes
*
* @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
diff --git a/include/mnl.h b/include/mnl.h
index a0dfa1b..b447d62 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -34,6 +34,14 @@ int mnl_nft_rule_delete(struct mnl_socket *nf_sock, struct nft_rule *r,
struct nft_rule_list *mnl_nft_rule_dump(struct mnl_socket *nf_sock,
int family);
+int mnl_nft_counter_batch_add(struct nft_counter *nlc,
+ unsigned int flags, uint32_t seq);
+int mnl_nft_counter_batch_del(struct nft_counter *nlc,
+ unsigned int flags, uint32_t seq);
+int mnl_nft_counter_get(struct mnl_socket *nf_sock, struct nft_counter *nlc);
+struct nft_counter_list *mnl_nft_counter_dump(struct mnl_socket *nf_sock,
+ int family, const char *table);
+
int mnl_nft_chain_add(struct mnl_socket *nf_sock, struct nft_chain *nlc,
unsigned int flags);
int mnl_nft_chain_batch_add(struct nft_chain *nlc,
diff --git a/include/netlink.h b/include/netlink.h
index 4f79470..962aa4a 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -21,6 +21,7 @@ extern const struct location netlink_location;
* @msgs: message queue
* @list: list of parsed rules/chains/tables
* @set: current set
+ * @counter: current counter
* @data: pointer to pass data to callback
* @seqnum: sequence number
*/
@@ -28,6 +29,7 @@ struct netlink_ctx {
struct list_head *msgs;
struct list_head list;
struct set *set;
+ struct counter *counter;
const void *data;
uint32_t seqnum;
bool batch_supported;
@@ -38,6 +40,7 @@ extern struct nft_chain *alloc_nft_chain(const struct handle *h);
extern struct nft_rule *alloc_nft_rule(const struct handle *h);
extern struct nft_rule_expr *alloc_nft_expr(const char *name);
extern struct nft_set *alloc_nft_set(const struct handle *h);
+extern struct nft_counter *alloc_nft_counter(const struct handle *h);
struct nft_data_linearize {
uint32_t len;
@@ -84,6 +87,24 @@ extern int netlink_del_rule_batch(struct netlink_ctx *ctx,
const struct handle *h,
const struct location *loc);
+extern int netlink_add_counter(struct netlink_ctx *ctx, const struct handle *h,
+ struct counter *counter);
+extern int netlink_rename_counter(struct netlink_ctx *ctx,
+ const struct handle *h,
+ const struct location *loc, const char *name);
+extern int netlink_delete_counter(struct netlink_ctx *ctx,
+ const struct handle *h,
+ const struct location *loc);
+extern int netlink_list_counters(struct netlink_ctx *ctx,
+ const struct handle *h,
+ const struct location *loc);
+extern int netlink_get_counter(struct netlink_ctx *ctx, const struct handle *h,
+ const struct location *loc);
+extern int netlink_flush_counter(struct netlink_ctx *ctx,
+ const struct handle *h,
+ const struct location *loc);
+
+
extern int netlink_add_chain(struct netlink_ctx *ctx, const struct handle *h,
const struct location *loc,
const struct chain *chain, bool excl);
@@ -135,6 +156,7 @@ extern void netlink_dump_chain(struct nft_chain *nlc);
extern void netlink_dump_rule(struct nft_rule *nlr);
extern void netlink_dump_expr(struct nft_rule_expr *nle);
extern void netlink_dump_set(struct nft_set *nls);
+extern void netlink_dump_counter(struct nft_counter *nlc);
extern int netlink_batch_send(struct list_head *err_list);
diff --git a/include/rule.h b/include/rule.h
index 491411e..23c5581 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -12,6 +12,7 @@
* @table: table name
* @chain: chain name (chains and rules only)
* @set: set name (sets only)
+ * @counter: counter name (counters only)
* @handle: rule handle (rules only)
* @position: rule position (rules only)
* @set_id: set ID (sets only)
@@ -22,6 +23,7 @@ struct handle {
const char *table;
const char *chain;
const char *set;
+ const char *counter;
uint64_t handle;
uint64_t position;
uint32_t set_id;
@@ -71,6 +73,7 @@ extern struct symbol *symbol_lookup(const struct scope *scope,
* @location: location the table was defined at
* @chains: chains contained in the table
* @sets: sets contained in the table
+ * @counters: counters contained in the table
*/
struct table {
struct list_head list;
@@ -79,6 +82,7 @@ struct table {
struct scope scope;
struct list_head chains;
struct list_head sets;
+ struct list_head counters;
};
extern struct table *table_alloc(void);
@@ -211,6 +215,41 @@ extern void set_print(const struct set *set);
extern void set_print_plain(const struct set *s);
/**
+ * struct counter - nftables counter
+ *
+ * @list: table counter list node
+ * @handle: counter handle
+ * @location: location the counter was defined/declared at
+ * @refcnt: reference count
+ * @flags: bitmask of counter flags
+ * @bytes: Total bytes
+ * @packets: Total packets
+
+ */
+struct counter {
+ struct list_head list;
+ struct handle handle;
+ struct location location;
+ unsigned int refcnt;
+ uint32_t flags;
+ uint64_t bytes;
+ uint64_t packets;
+};
+
+extern struct counter *counter_alloc(const struct location *loc);
+extern struct counter *counter_get(struct counter *counter);
+extern void counter_free(struct counter *counter);
+extern struct counter *counter_clone(const struct counter *counter);
+extern void counter_add_hash(struct counter *counter, struct table *table);
+extern struct counter *counter_lookup(const struct table *table,
+ const char *name);
+extern struct counter *counter_lookup_global(uint32_t family, const char *table,
+ const char *name);
+extern void counter_print(const struct counter *counter);
+
+
+
+/**
* enum cmd_ops - command operations
*
* @CMD_INVALID: invalid
@@ -253,6 +292,8 @@ enum cmd_ops {
* @CMD_OBJ_EXPR: expression
* @CMD_OBJ_MONITOR: monitor
* @CMD_OBJ_EXPORT: export
+ * @CMD_OBJ_COUNTER: counter
+ * @CMD_OBJ_COUNTERS: counters
*/
enum cmd_obj {
CMD_OBJ_INVALID,
@@ -266,6 +307,8 @@ enum cmd_obj {
CMD_OBJ_EXPR,
CMD_OBJ_MONITOR,
CMD_OBJ_EXPORT,
+ CMD_OBJ_COUNTER,
+ CMD_OBJ_COUNTERS,
};
struct export {
@@ -282,6 +325,7 @@ enum {
CMD_MONITOR_OBJ_RULES,
CMD_MONITOR_OBJ_SETS,
CMD_MONITOR_OBJ_ELEMS,
+ CMD_MONITOR_OBJ_COUNTERS,
CMD_MONITOR_OBJ_MAX
};
@@ -320,6 +364,7 @@ struct cmd {
void *data;
struct expr *expr;
struct set *set;
+ struct counter *counter;
struct rule *rule;
struct chain *chain;
struct table *table;
@@ -345,6 +390,7 @@ extern void cmd_free(struct cmd *cmd);
* @table: current table
* @rule: current rule
* @set: current set
+ * @counter: current counter
* @stmt: current statement
* @ectx: expression context
* @pctx: payload context
@@ -355,6 +401,7 @@ struct eval_ctx {
struct table *table;
struct rule *rule;
struct set *set;
+ struct counter *counter;
struct stmt *stmt;
struct expr_ctx ectx;
struct proto_ctx pctx;
diff --git a/include/statement.h b/include/statement.h
index d143121..5c65633 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -13,6 +13,7 @@ extern struct stmt *verdict_stmt_alloc(const struct location *loc,
struct counter_stmt {
uint64_t packets;
uint64_t bytes;
+ const char *name;
};
extern struct stmt *counter_stmt_alloc(const struct location *loc);
diff --git a/src/evaluate.c b/src/evaluate.c
index d24d4cc..e7d3e80 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1824,6 +1824,7 @@ static int table_evaluate(struct eval_ctx *ctx, struct table *table)
if (set_evaluate(ctx, set) < 0)
return -1;
}
+
list_for_each_entry(chain, &table->chains, list) {
handle_merge(&chain->handle, &table->handle);
if (chain_evaluate(ctx, chain) < 0)
@@ -1844,6 +1845,8 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_RULE:
handle_merge(&cmd->rule->handle, &cmd->handle);
return rule_evaluate(ctx, cmd->rule);
+ case CMD_OBJ_COUNTER:
+ return 0;
case CMD_OBJ_CHAIN:
if (cmd->data == NULL)
return 0;
@@ -1863,6 +1866,7 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SETELEM:
return setelem_evaluate(ctx, &cmd->expr);
case CMD_OBJ_SET:
+ case CMD_OBJ_COUNTER:
case CMD_OBJ_RULE:
case CMD_OBJ_CHAIN:
case CMD_OBJ_TABLE:
@@ -1892,13 +1896,16 @@ static uint32_t monitor_flags[CMD_MONITOR_EVENT_MAX][CMD_MONITOR_OBJ_MAX] = {
(1 << NFT_MSG_DELSET),
[CMD_MONITOR_OBJ_ELEMS] = (1 << NFT_MSG_NEWSETELEM) |
(1 << NFT_MSG_DELSETELEM),
+ [CMD_MONITOR_OBJ_COUNTERS] = (1 << NFT_MSG_NEWCOUNTER) |
+ (1 << NFT_MSG_DELCOUNTER),
},
[CMD_MONITOR_EVENT_NEW] = {
[CMD_MONITOR_OBJ_ANY] = (1 << NFT_MSG_NEWTABLE) |
(1 << NFT_MSG_NEWCHAIN) |
(1 << NFT_MSG_NEWRULE) |
(1 << NFT_MSG_NEWSET) |
- (1 << NFT_MSG_NEWSETELEM),
+ (1 << NFT_MSG_NEWSETELEM)|
+ (1 << NFT_MSG_NEWCOUNTER),
[CMD_MONITOR_OBJ_TABLES] = (1 << NFT_MSG_NEWTABLE),
[CMD_MONITOR_OBJ_CHAINS] = (1 << NFT_MSG_NEWCHAIN),
[CMD_MONITOR_OBJ_RULES] = (1 << NFT_MSG_NEWRULE),
@@ -1910,12 +1917,14 @@ static uint32_t monitor_flags[CMD_MONITOR_EVENT_MAX][CMD_MONITOR_OBJ_MAX] = {
(1 << NFT_MSG_DELCHAIN) |
(1 << NFT_MSG_DELRULE) |
(1 << NFT_MSG_DELSET) |
- (1 << NFT_MSG_DELSETELEM),
+ (1 << NFT_MSG_DELSETELEM)|
+ (1 << NFT_MSG_DELCOUNTER),
[CMD_MONITOR_OBJ_TABLES] = (1 << NFT_MSG_DELTABLE),
[CMD_MONITOR_OBJ_CHAINS] = (1 << NFT_MSG_DELCHAIN),
[CMD_MONITOR_OBJ_RULES] = (1 << NFT_MSG_DELRULE),
[CMD_MONITOR_OBJ_SETS] = (1 << NFT_MSG_DELSET),
[CMD_MONITOR_OBJ_ELEMS] = (1 << NFT_MSG_DELSETELEM),
+ [CMD_MONITOR_OBJ_COUNTERS] = (1 << NFT_MSG_DELCOUNTER),
},
};
diff --git a/src/mnl.c b/src/mnl.c
index f48ead5..47b91b2 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -16,6 +16,7 @@
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include <libnftnl/set.h>
+#include <libnftnl/counter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
@@ -711,6 +712,124 @@ int mnl_nft_table_get(struct mnl_socket *nf_sock, struct nft_table *nlt,
}
/*
+ * Named counter
+ */
+int mnl_nft_counter_batch_add(struct nft_counter *nls, unsigned int flags,
+ uint32_t seqnum)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_counter_nlmsg_build_hdr(nft_nlmsg_batch_current(),
+ NFT_MSG_NEWCOUNTER,
+ nft_counter_attr_get_u32(nls, NFT_COUNTER_ATTR_FAMILY),
+ NLM_F_CREATE | flags, seqnum);
+ nft_counter_nlmsg_build_payload(nlh, nls);
+ nft_batch_continue();
+
+ return 0;
+}
+
+int mnl_nft_counter_batch_del(struct nft_counter *nls, unsigned int flags,
+ uint32_t seqnum)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_counter_nlmsg_build_hdr(nft_nlmsg_batch_current(),
+ NFT_MSG_DELCOUNTER,
+ nft_counter_attr_get_u32(nls, NFT_COUNTER_ATTR_FAMILY),
+ flags, seqnum);
+ nft_counter_nlmsg_build_payload(nlh, nls);
+ nft_batch_continue();
+
+ return 0;
+}
+
+static int counter_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_counter_list *ctr_list = data;
+ struct nft_counter *c;
+
+ if (check_genid(nlh) < 0)
+ return MNL_CB_ERROR;
+
+ c = nft_counter_alloc();
+ if (c == NULL)
+ memory_allocation_error();
+
+ if (nft_counter_nlmsg_parse(nlh, c) < 0)
+ goto err_free;
+
+ nft_counter_list_add_tail(c, ctr_list);
+ return MNL_CB_OK;
+
+err_free:
+ nft_counter_free(c);
+ return MNL_CB_OK;
+}
+
+static int counter_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_counter *a = data;
+
+ nft_counter_nlmsg_parse(nlh, a);
+
+ return MNL_CB_OK;
+}
+
+int mnl_nft_counter_get(struct mnl_socket *nf_sock, struct nft_counter *counter)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_counter_nlmsg_build_hdr(buf, NFT_MSG_GETCOUNTER,
+ nft_counter_attr_get_u32(counter,
+ NFT_COUNTER_ATTR_FAMILY),
+ NLM_F_ACK, seq);
+ nft_counter_nlmsg_build_payload(nlh, counter);
+
+ return nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, counter_get_cb,
+ counter);
+}
+
+struct nft_counter_list *mnl_nft_counter_dump(struct mnl_socket *nf_sock,
+ int family,
+ const char *table)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_counter *counter;
+ struct nft_counter_list *ctr_list;
+ int ret;
+
+ counter = nft_counter_alloc();
+ if (counter == NULL)
+ memory_allocation_error();
+
+ nlh = nft_counter_nlmsg_build_hdr(buf, NFT_MSG_GETCOUNTER, family,
+ NLM_F_DUMP|NLM_F_ACK, seq);
+ if (table != NULL)
+ nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE,
+ table);
+ nft_counter_nlmsg_build_payload(nlh, counter);
+ nft_counter_free(counter);
+
+ ctr_list = nft_counter_list_alloc();
+ if (ctr_list == NULL)
+ memory_allocation_error();
+
+ ret = nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, counter_cb, ctr_list);
+
+ if (ret < 0)
+ goto err;
+
+ return ctr_list;
+err:
+ nft_counter_list_free(ctr_list);
+ return NULL;
+}
+
+
+/*
* Set
*/
static int set_add_cb(const struct nlmsghdr *nlh, void *data)
diff --git a/src/netlink.c b/src/netlink.c
index 84d9d27..6cd775b 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -21,6 +21,7 @@
#include <libnftnl/chain.h>
#include <libnftnl/expr.h>
#include <libnftnl/set.h>
+#include <libnftnl/counter.h>
#include <libnftnl/common.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
@@ -1434,6 +1435,176 @@ out:
return err;
}
+static struct counter *netlink_delinearize_counter(struct netlink_ctx *ctx,
+ struct nft_counter *nla)
+{
+ struct counter *counter;
+
+ counter = counter_alloc(&netlink_location);
+
+ if (counter == NULL)
+ return NULL;
+
+ counter->handle.family =
+ nft_counter_attr_get_u32(nla, NFT_COUNTER_ATTR_FAMILY);
+ counter->handle.counter =
+ xstrdup(nft_counter_attr_get_str(nla, NFT_COUNTER_ATTR_NAME));
+ counter->handle.table =
+ xstrdup(nft_counter_attr_get_str(nla, NFT_COUNTER_ATTR_TABLE));
+ counter->packets =
+ nft_counter_attr_get_u64(nla, NFT_COUNTER_ATTR_PKTS);
+ counter->bytes = nft_counter_attr_get_u64(nla, NFT_COUNTER_ATTR_BYTES);
+
+ return counter;
+}
+
+static int list_counter_cb(struct nft_counter *c, void *arg)
+{
+ struct netlink_ctx *ctx = arg;
+ struct counter *counter;
+
+ netlink_dump_counter(c);
+ counter = netlink_delinearize_counter(ctx, c);
+ if (counter == NULL)
+ return -1;
+
+ list_add_tail(&counter->list, &ctx->list);
+
+ return 0;
+}
+
+int netlink_list_counters(struct netlink_ctx *ctx, const struct handle *h,
+ const struct location *loc)
+{
+ struct nft_counter_list *ctr_list;
+ int err;
+
+ ctr_list = mnl_nft_counter_dump(nf_sock, h->family, h->table);
+ if (ctr_list == NULL) {
+ if (errno == EINTR)
+ return -1;
+
+ return netlink_io_error(ctx, loc,
+ "Could not receive counters from kernel: %s",
+ strerror(errno));
+ }
+
+ err = nft_counter_list_foreach(ctr_list, list_counter_cb, ctx);
+ nft_counter_list_free(ctr_list);
+
+ return err;
+}
+
+int netlink_get_counter(struct netlink_ctx *ctx, const struct handle *h,
+ const struct location *loc)
+{
+ struct nft_counter *nla;
+ struct counter *counter;
+ int err;
+
+ nla = alloc_nft_counter(h);
+ netlink_dump_counter(nla);
+ err = mnl_nft_counter_get(nf_sock, nla);
+ if (err < 0) {
+ nft_counter_free(nla);
+ return netlink_io_error(ctx, loc,
+ "Could not receive counter from kernel: %s",
+ strerror(errno));
+ }
+
+ counter = netlink_delinearize_counter(ctx, nla);
+ if (counter == NULL) {
+ nft_counter_free(nla);
+ return -1;
+ }
+
+ list_add_tail(&counter->list, &ctx->list);
+
+ return err;
+}
+
+void netlink_dump_counter(struct nft_counter *nla)
+{
+#ifdef DEBUG
+ char buf[4096];
+
+ if (!(debug_level & DEBUG_NETLINK))
+ return;
+
+ nft_counter_snprintf(buf, sizeof(buf), nla, 0, 0);
+ fprintf(stdout, "%s\n", buf);
+#endif
+}
+
+struct nft_counter *alloc_nft_counter(const struct handle *h)
+{
+ struct nft_counter *nla;
+
+ nla = nft_counter_alloc();
+ if (nla == NULL)
+ memory_allocation_error();
+
+ nft_counter_attr_set_u32(nla, NFT_COUNTER_ATTR_FAMILY, h->family);
+ nft_counter_attr_set_str(nla, NFT_COUNTER_ATTR_TABLE, h->table);
+ if (h->counter != NULL) {
+ nft_counter_attr_set_str(nla,
+ NFT_COUNTER_ATTR_NAME, h->counter);
+ }
+
+ return nla;
+}
+
+static int netlink_add_counter_batch(struct netlink_ctx *ctx,
+ const struct handle *h,
+ struct counter *counter)
+{
+ struct nft_counter *nla;
+ int err;
+
+ nla = alloc_nft_counter(h);
+
+ netlink_dump_counter(nla);
+
+ err = mnl_nft_counter_batch_add(nla, NLM_F_EXCL, ctx->seqnum);
+ if (err < 0) {
+ netlink_io_error(ctx, &counter->location,
+ "Could not add counter: %s",
+ strerror(errno));
+ }
+ nft_counter_free(nla);
+
+ return err;
+}
+
+int netlink_add_counter(struct netlink_ctx *ctx, const struct handle *h,
+ struct counter *counter)
+{
+ return netlink_add_counter_batch(ctx, h, counter);
+}
+
+static int netlink_del_counter_batch(struct netlink_ctx *ctx,
+ const struct handle *h,
+ const struct location *loc)
+{
+ struct nft_counter *nla;
+ int err;
+
+ nla = alloc_nft_counter(h);
+ err = mnl_nft_counter_batch_del(nla, 0, ctx->seqnum);
+ nft_counter_free(nla);
+
+ if (err < 0)
+ netlink_io_error(ctx, loc, "Could not delete counter: %s",
+ strerror(errno));
+ return err;
+}
+
+int netlink_delete_counter(struct netlink_ctx *ctx, const struct handle *h,
+ const struct location *loc)
+{
+ return netlink_del_counter_batch(ctx, h, loc);
+}
+
int netlink_batch_send(struct list_head *err_list)
{
return mnl_batch_talk(nf_sock, err_list);
@@ -1515,6 +1686,19 @@ static struct nft_set *netlink_set_alloc(const struct nlmsghdr *nlh)
return nls;
}
+static struct nft_counter *netlink_counter_alloc(const struct nlmsghdr *nlh)
+{
+ struct nft_counter *nla = nft_counter_alloc();
+
+ if (nla == NULL)
+ memory_allocation_error();
+
+ if (nft_counter_nlmsg_parse(nlh, nla) < 0)
+ netlink_abi_error();
+
+ return nla;
+}
+
static struct nft_set *netlink_setelem_alloc(const struct nlmsghdr *nlh)
{
struct nft_set *nls;
@@ -1549,12 +1733,14 @@ static uint32_t netlink_msg2nftnl_of(uint32_t msg)
case NFT_MSG_NEWSET:
case NFT_MSG_NEWSETELEM:
case NFT_MSG_NEWRULE:
+ case NFT_MSG_NEWCOUNTER:
return NFT_OF_EVENT_NEW;
case NFT_MSG_DELTABLE:
case NFT_MSG_DELCHAIN:
case NFT_MSG_DELSET:
case NFT_MSG_DELSETELEM:
case NFT_MSG_DELRULE:
+ case NFT_MSG_DELCOUNTER:
return NFT_OF_EVENT_DEL;
}
@@ -1806,6 +1992,51 @@ out:
return MNL_CB_OK;
}
+static int netlink_events_counter_cb(const struct nlmsghdr *nlh, int type,
+ struct netlink_mon_handler *monh)
+{
+ struct counter *counter;
+ uint32_t family;
+ struct nft_counter *nla = netlink_counter_alloc(nlh);
+
+ switch (monh->format) {
+ case NFT_OUTPUT_DEFAULT:
+ switch (type) {
+ case NFT_MSG_NEWCOUNTER:
+ printf("add ");
+ counter = netlink_delinearize_counter(monh->ctx, nla);
+ if (counter == NULL)
+ return MNL_CB_ERROR;
+ counter_print(counter);
+ counter_free(counter);
+ printf("\n");
+ break;
+ case NFT_MSG_DELCOUNTER:
+ family = nft_counter_attr_get_u32(nla,
+ NFT_COUNTER_ATTR_FAMILY);
+ printf("delete counter %s %s %s\n",
+ family2str(family),
+ nft_counter_attr_get_str(nla,
+ NFT_COUNTER_ATTR_TABLE),
+ nft_counter_attr_get_str(nla,
+ NFT_COUNTER_ATTR_NAME));
+ break;
+ }
+ break;
+ case NFT_OUTPUT_XML:
+ case NFT_OUTPUT_JSON:
+ nft_counter_fprintf(stdout, nla, monh->format,
+ netlink_msg2nftnl_of(type));
+ fprintf(stdout, "\n");
+ break;
+ }
+
+ nft_counter_free(nla);
+
+ return MNL_CB_OK;
+
+}
+
static void rule_map_decompose_cb(struct set *s, void *data)
{
if (s->flags & NFT_SET_INTERVAL)
@@ -2038,6 +2269,10 @@ static int netlink_events_cb(const struct nlmsghdr *nlh, void *data)
case NFT_MSG_DELSETELEM: /* nft {add|delete} element */
ret = netlink_events_setelem_cb(nlh, type, monh);
break;
+ case NFT_MSG_NEWCOUNTER:
+ case NFT_MSG_DELCOUNTER: /* nft {add|delete} counter */
+ ret = netlink_events_counter_cb(nlh, type, monh);
+ break;
case NFT_MSG_NEWRULE:
case NFT_MSG_DELRULE:
ret = netlink_events_rule_cb(nlh, type, monh);
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 387bb67..5ccfa30 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -445,6 +445,7 @@ static void netlink_parse_ct(struct netlink_parse_ctx *ctx,
netlink_parse_ct_stmt(ctx, loc, nle);
}
+
static void netlink_parse_counter(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nft_rule_expr *nle)
@@ -456,6 +457,8 @@ static void netlink_parse_counter(struct netlink_parse_ctx *ctx,
nft_rule_expr_get_u64(nle, NFT_EXPR_CTR_PACKETS);
stmt->counter.bytes =
nft_rule_expr_get_u64(nle, NFT_EXPR_CTR_BYTES);
+ stmt->counter.name =
+ nft_rule_expr_get_str(nle, NFT_EXPR_CTR_NAME);
list_add_tail(&stmt->list, &ctx->rule->stmts);
}
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 9bef67b..3a098eb 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -553,6 +553,10 @@ static void netlink_gen_counter_stmt(struct netlink_linearize_ctx *ctx,
nft_rule_expr_set_u64(nle, NFT_EXPR_CTR_BYTES,
stmt->counter.bytes);
}
+ if (stmt->counter.name) {
+ nft_rule_expr_set_str(nle, NFT_EXPR_CTR_NAME,
+ stmt->counter.name);
+ }
nft_rule_add_expr(ctx->nlr, nle);
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index fd2407c..833e848 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -133,6 +133,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
struct expr *expr;
struct set *set;
const struct datatype *datatype;
+ struct counter *counter;
}
%token TOKEN_EOF 0 "end of file"
@@ -341,6 +342,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%token COUNTER "counter"
%token PACKETS "packets"
%token BYTES "bytes"
+%token NAME "name"
%token LOG "log"
%token PREFIX "prefix"
@@ -405,8 +407,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <cmd> base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd
%destructor { cmd_free($$); } base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd
-%type <handle> table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec
-%destructor { handle_free(&$$); } table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec
+%type <handle> table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec counter_spec counter_identifier
+%destructor { handle_free(&$$); } table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec counter_spec counter_identifier
%type <handle> set_spec set_identifier
%destructor { handle_free(&$$); } set_spec set_identifier
%type <val> handle_spec family_spec family_spec_explicit position_spec
@@ -428,6 +430,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <set> map_block_alloc map_block
%destructor { set_free($$); } map_block_alloc
+%type <counter> counter_block_alloc
+%destructor { counter_free($$); } counter_block_alloc
+
%type <list> stmt_list
%destructor { stmt_list_free($$); xfree($$); } stmt_list
%type <stmt> stmt match_stmt verdict_stmt
@@ -680,6 +685,10 @@ add_cmd : TABLE table_spec
handle_merge(&$3->handle, &$2);
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, &@$, $5);
}
+ | COUNTER counter_spec
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
| MAP set_spec map_block_alloc
'{' map_block '}'
{
@@ -740,6 +749,10 @@ delete_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
}
+ | COUNTER counter_spec
+ {
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
| MAP set_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
@@ -770,6 +783,10 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, &@$, NULL);
}
+ | COUNTER counter_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
| RULESET ruleset_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);
@@ -788,6 +805,10 @@ flush_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_SET, &$2, &@$, NULL);
}
+ | COUNTER counter_spec
+ {
+ $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
| RULESET ruleset_spec
{
$$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_RULESET, &$2, &@$, NULL);
@@ -877,6 +898,16 @@ table_block : /* empty */ { $$ = $<table>-1; }
list_add_tail(&$4->list, &$1->sets);
$$ = $1;
}
+ | table_block COUNTER counter_identifier
+ counter_block_alloc
+ stmt_seperator
+ {
+ $4->location = @3;
+ handle_merge(&$4->handle, &$3);
+ handle_free(&$3);
+ list_add_tail(&$4->list, &$1->counters);
+ $$ = $1;
+ }
| table_block MAP set_identifier
map_block_alloc '{' map_block '}'
stmt_seperator
@@ -907,6 +938,12 @@ chain_block : /* empty */ { $$ = $<chain>-1; }
}
;
+counter_block_alloc : /* empty */
+ {
+ $$ = counter_alloc(NULL);
+ }
+ ;
+
set_block_alloc : /* empty */
{
$$ = set_alloc(NULL);
@@ -1112,6 +1149,13 @@ set_spec : table_spec identifier
}
;
+counter_spec : table_spec identifier
+ {
+ $$ = $1;
+ $$.counter = $2;
+ }
+ ;
+
set_identifier : identifier
{
memset(&$$, 0, sizeof($$));
@@ -1119,6 +1163,13 @@ set_identifier : identifier
}
;
+counter_identifier : identifier
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.counter = $1;
+ }
+ ;
+
handle_spec : /* empty */
{
$$ = 0;
@@ -1258,7 +1309,6 @@ verdict_map_list_member_expr: opt_newline map_lhs_expr COLON verdict_expr opt_ne
}
;
-
counter_stmt : counter_stmt_alloc
| counter_stmt_alloc counter_args
@@ -1283,6 +1333,10 @@ counter_arg : PACKETS NUM
{
$<stmt>0->counter.bytes = $2;
}
+ | NAME STRING
+ {
+ $<stmt>0->counter.name = $2;
+ }
;
log_stmt : log_stmt_alloc
diff --git a/src/rule.c b/src/rule.c
index feafe26..53538ae 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -32,6 +32,7 @@ void handle_free(struct handle *h)
xfree(h->table);
xfree(h->chain);
xfree(h->set);
+ xfree(h->counter);
xfree(h->comment);
}
@@ -45,6 +46,8 @@ void handle_merge(struct handle *dst, const struct handle *src)
dst->chain = xstrdup(src->chain);
if (dst->set == NULL && src->set != NULL)
dst->set = xstrdup(src->set);
+ if (dst->counter == NULL && src->counter != NULL)
+ dst->counter = xstrdup(src->counter);
if (dst->handle == 0)
dst->handle = src->handle;
if (dst->position == 0)
@@ -212,6 +215,70 @@ void set_print_plain(const struct set *s)
do_set_print(s, &opts);
}
+struct counter *counter_alloc(const struct location *loc)
+{
+ struct counter *counter;
+
+ counter = xzalloc(sizeof(*counter));
+ counter->refcnt = 1;
+
+ if (loc != NULL)
+ counter->location = *loc;
+
+ return counter;
+}
+
+struct counter *counter_get(struct counter *counter)
+{
+ counter->refcnt++;
+
+ return counter;
+}
+
+void counter_free(struct counter *counter)
+{
+ if (--counter->refcnt > 0)
+ return;
+ handle_free(&counter->handle);
+ xfree(counter);
+}
+
+struct counter *counter_lookup(const struct table *table, const char *name)
+{
+ struct counter *counter;
+
+ list_for_each_entry(counter, &table->counters, list) {
+ if (!strcmp(counter->handle.counter, name))
+ return counter;
+ }
+
+ return NULL;
+}
+
+struct counter *counter_lookup_global(uint32_t family, const char *table,
+ const char *name)
+{
+ struct handle h;
+ struct table *t;
+
+ h.family = family;
+ h.table = table;
+
+ t = table_lookup(&h);
+ if (t == NULL)
+ return NULL;
+
+ return counter_lookup(t, name);
+}
+
+void counter_print(const struct counter *counter)
+{
+ printf("\tcounter %s { ", counter->handle.counter);
+ printf("packets %"PRIu64" bytes %"PRIu64"", counter->packets,
+ counter->bytes);
+ printf("}\n");
+}
+
struct rule *rule_alloc(const struct location *loc, const struct handle *h)
{
struct rule *rule;
@@ -467,6 +534,7 @@ struct table *table_alloc(void)
table = xzalloc(sizeof(*table));
init_list_head(&table->chains);
init_list_head(&table->sets);
+ init_list_head(&table->counters);
init_list_head(&table->scope.symbols);
return table;
}
@@ -504,6 +572,7 @@ struct table *table_lookup(const struct handle *h)
static void table_print(const struct table *table)
{
struct chain *chain;
+ struct counter *counter;
struct set *set;
const char *delim = "";
const char *family = family2str(table->handle.family);
@@ -516,11 +585,21 @@ static void table_print(const struct table *table)
set_print(set);
delim = "\n";
}
+
+ if (!list_empty(&table->sets))
+ printf("\n");
+ list_for_each_entry(counter, &table->counters, list) {
+ counter_print(counter);
+ }
+ if (!list_empty(&table->chains))
+ printf("\n");
+
list_for_each_entry(chain, &table->chains, list) {
printf("%s", delim);
chain_print(chain);
delim = "\n";
}
+
printf("}\n");
}
@@ -602,6 +681,9 @@ void cmd_free(struct cmd *cmd)
case CMD_OBJ_EXPORT:
export_free(cmd->export);
break;
+ case CMD_OBJ_COUNTER:
+ counter_free(cmd->counter);
+ break;
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -625,6 +707,15 @@ static int do_add_chain(struct netlink_ctx *ctx, const struct handle *h,
return 0;
}
+static int do_add_counter(struct netlink_ctx *ctx, const struct handle *h,
+ struct counter *counter)
+{
+ if (netlink_add_counter(ctx, h, counter) < 0)
+ return -1;
+
+ return 0;
+}
+
static int do_add_setelems(struct netlink_ctx *ctx, const struct handle *h,
const struct expr *expr)
{
@@ -654,6 +745,7 @@ static int do_add_table(struct netlink_ctx *ctx, const struct handle *h,
{
struct chain *chain;
struct set *set;
+ struct counter *counter;
if (netlink_add_table(ctx, h, loc, table, excl) < 0)
return -1;
@@ -663,6 +755,11 @@ static int do_add_table(struct netlink_ctx *ctx, const struct handle *h,
if (do_add_set(ctx, &set->handle, set) < 0)
return -1;
}
+ list_for_each_entry(counter, &table->counters, list) {
+ handle_merge(&counter->handle, &table->handle);
+ if (do_add_counter(ctx, &counter->handle, counter) < 0)
+ return -1;
+ }
list_for_each_entry(chain, &table->chains, list) {
if (do_add_chain(ctx, &chain->handle, &chain->location,
chain, excl) < 0)
@@ -688,6 +785,8 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
return do_add_set(ctx, &cmd->handle, cmd->set);
case CMD_OBJ_SETELEM:
return do_add_setelems(ctx, &cmd->handle, cmd->expr);
+ case CMD_OBJ_COUNTER:
+ return do_add_counter(ctx, &cmd->handle, cmd->counter);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -720,6 +819,9 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
return netlink_delete_set(ctx, &cmd->handle, &cmd->location);
case CMD_OBJ_SETELEM:
return netlink_delete_setelems(ctx, &cmd->handle, cmd->expr);
+ case CMD_OBJ_COUNTER:
+ return netlink_delete_counter(ctx, &cmd->handle,
+ &cmd->location);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -741,6 +843,21 @@ static int do_list_sets(struct netlink_ctx *ctx, const struct location *loc,
return 0;
}
+static int do_list_counters(struct netlink_ctx *ctx, const struct location *loc,
+ struct table *table)
+{
+ struct counter *counter, *ncounter;
+
+ if (netlink_list_counters(ctx, &table->handle, loc) < 0)
+ return -1;
+
+ list_for_each_entry_safe(counter, ncounter, &ctx->list, list) {
+ list_move_tail(&counter->list, &table->counters);
+ }
+
+ return 0;
+}
+
static int do_command_export(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct nft_ruleset *rs = netlink_dump_ruleset(ctx, &cmd->handle,
@@ -760,6 +877,7 @@ static void table_cleanup(struct table *table)
{
struct chain *chain, *nchain;
struct set *set, *nset;
+ struct counter *counter, *ncounter;
list_for_each_entry_safe(chain, nchain, &table->chains, list) {
list_del(&chain->list);
@@ -770,6 +888,10 @@ static void table_cleanup(struct table *table)
list_del(&set->list);
set_free(set);
}
+ list_for_each_entry_safe(counter, ncounter, &table->counters, list) {
+ list_del(&counter->list);
+ counter_free(counter);
+ }
}
static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -780,6 +902,8 @@ static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd,
if (do_list_sets(ctx, &cmd->location, table) < 0)
goto err;
+ if (do_list_counters(ctx, &cmd->location, table) < 0)
+ goto err;
if (netlink_list_chains(ctx, &cmd->handle, &cmd->location) < 0)
goto err;
list_splice_tail_init(&ctx->list, &table->chains);
@@ -835,6 +959,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct table *table = NULL;
struct set *set;
+ struct counter *counter;
/* No need to allocate the table object when listing all tables */
if (cmd->handle.table != NULL) {
@@ -887,6 +1012,20 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
set_print(set);
}
return 0;
+ case CMD_OBJ_COUNTERS:
+ if (netlink_list_counters(ctx, &cmd->handle, &cmd->location) < 0)
+ goto err;
+ list_for_each_entry(counter, &ctx->list, list) {
+ counter_print(counter);
+ }
+ return 0;
+ case CMD_OBJ_COUNTER:
+ if (netlink_get_counter(ctx, &cmd->handle, &cmd->location) < 0)
+ goto err;
+ list_for_each_entry(counter, &ctx->list, list) {
+ counter_print(counter);
+ }
+ return 0;
case CMD_OBJ_RULESET:
return do_list_ruleset(ctx, cmd);
default:
diff --git a/src/scanner.l b/src/scanner.l
index 73c4f8b..1a191f1 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -279,6 +279,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"counter" { return COUNTER; }
"packets" { return PACKETS; }
"bytes" { return BYTES; }
+"name" { return NAME; }
"log" { return LOG; }
"prefix" { return PREFIX; }
diff --git a/src/statement.c b/src/statement.c
index d72c6e9..990aa0e 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -105,8 +105,12 @@ struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr)
static void counter_stmt_print(const struct stmt *stmt)
{
- printf("counter packets %" PRIu64 " bytes %" PRIu64,
- stmt->counter.packets, stmt->counter.bytes);
+ printf("counter ");
+ if (stmt->counter.name)
+ printf("%s", stmt->counter.name);
+ else
+ printf("packets %" PRIu64 " bytes %" PRIu64,
+ stmt->counter.packets, stmt->counter.bytes);
}
static const struct stmt_ops counter_stmt_ops = {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [nf v2 2/2] netfilter: named counter: add support to counters in nftables
2015-01-26 19:43 [nf v2 0/6] Accounting objects support in nft Ana Rey Botello
2015-01-26 19:43 ` [nf v2 1/2] netfilter: Rename from nft_counter to nft_counter_priv Ana Rey Botello
[not found] ` <cover.1422299750.git.ana@soleta.eu>
@ 2015-01-26 19:43 ` Ana Rey Botello
[not found] ` <cover.1422299705.git.ana@soleta.eu>
3 siblings, 0 replies; 7+ messages in thread
From: Ana Rey Botello @ 2015-01-26 19:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Ana Rey
From: Ana Rey <ana@soleta.eu>
This adds counter objects support to allow us to manipulate the nftables's
extended accounting intraestructure.
Example of use in nft:
# nft add counter ip filter http-traffic
# nft add counter ip filter https-traffic
# nft add rule ip filter output tcp dport 80 counter name http-traffic
# nft add rule ip filter output tcp dport 443 counter name https-traffic
# nft delete counter ip filter https-traffic
Generate Some traffic:
# nft list table ip test
table ip filter {
counter http-traffic { pkts 779 bytes 99495}
counter https-traffic { pkts 189 bytes 37824}
chain output {
type filter hook output priority 0;
tcp dport http counter http-traffic
tcp dport https counter https-traffic
}
}
Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
include/net/netfilter/nf_tables.h | 49 +++
include/uapi/linux/netfilter/nf_tables.h | 34 +++
net/netfilter/nf_tables_api.c | 486 +++++++++++++++++++++++++++++-
net/netfilter/nft_counter.c | 128 ++++++--
4 files changed, 676 insertions(+), 21 deletions(-)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 9eaaa78..9a1acfe 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -408,6 +408,16 @@ struct nft_trans {
char data[0];
};
+struct nft_trans_counter {
+ struct nft_counter *counter;
+ u32 counter_id;
+};
+
+#define nft_trans_counter(trans) \
+ (((struct nft_trans_counter *)trans->data)->counter)
+#define nft_trans_counter_id(trans) \
+ (((struct nft_trans_counter *)trans->data)->counter_id)
+
struct nft_trans_rule {
struct nft_rule *rule;
};
@@ -572,6 +582,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt,
* @list: used internally
* @chains: chains in the table
* @sets: sets in the table
+ * @counters: counters in the table
* @hgenerator: handle generator state
* @use: number of chain references to this table
* @flags: table flag (see enum nft_table_flags)
@@ -581,6 +592,7 @@ struct nft_table {
struct list_head list;
struct list_head chains;
struct list_head sets;
+ struct list_head counters;
u64 hgenerator;
u32 use;
u16 flags;
@@ -639,6 +651,43 @@ void nft_unregister_chain_type(const struct nf_chain_type *);
int nft_register_expr(struct nft_expr_type *);
void nft_unregister_expr(struct nft_expr_type *);
+/**
+ * struct nft_counter_priv - nf_tables counter_priv instance
+ *
+ * @pkts: number of packets
+ * @bytes: number of bytes
+ */
+struct nft_counter_priv {
+ seqlock_t lock;
+ u64 bytes;
+ u64 packets;
+};
+
+/**
+ * struct nft_counter - nf_tables counter instance
+ *
+ * @list: table counter list node
+ * @name: name of the counter
+ * @counter: nft_priv_counter
+ * @bytes: number of bytes
+ * @use: number of rule references to this counter
+ * @flags: counter flags
+ */
+struct nft_counter {
+ struct list_head list;
+ char name[NFT_CTR_MAXNAMELEN];
+ struct nft_counter_priv counter;
+ u32 use:31,
+ flags:1;
+};
+
+struct nft_counter *nft_counter_lookup(const struct nft_ctx *ctx,
+ const char *counter_name);
+struct nft_counter *nf_tables_counter_lookup(const struct nft_table *table,
+ const struct nlattr *nla);
+void nft_counter_put(struct nft_counter *counter);
+int nft_counter_get(struct nft_counter *counter);
+
#define nft_dereference(p) \
nfnl_dereference(p, NFNL_SUBSYS_NFTABLES)
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 832bc46..aadb762 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -2,6 +2,7 @@
#define _LINUX_NF_TABLES_H
#define NFT_CHAIN_MAXNAMELEN 32
+#define NFT_CTR_MAXNAMELEN 16
#define NFT_USERDATA_MAXLEN 256
enum nft_registers {
@@ -53,6 +54,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_NEWCOUNTER: create a new counter (enum nft_counter_attributes)
+ * @NFT_MSG_GETCOUNTER: get a counter (enum nft_counter_attributes)
+ * @NFT_MSG_GETCOUNTER_ZERO: get a reset counter (enum nft_counter_attributes)
+ * @NFT_MSG_DELCOUNTER: delete a counter (enum nft_counter_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -72,6 +77,10 @@ enum nf_tables_msg_types {
NFT_MSG_DELSETELEM,
NFT_MSG_NEWGEN,
NFT_MSG_GETGEN,
+ NFT_MSG_NEWCOUNTER,
+ NFT_MSG_GETCOUNTER,
+ NFT_MSG_GETCOUNTER_ZERO,
+ NFT_MSG_DELCOUNTER,
NFT_MSG_MAX,
};
@@ -695,16 +704,40 @@ enum nft_limit_attributes {
*
* @NFTA_COUNTER_BYTES: number of bytes (NLA_U64)
* @NFTA_COUNTER_PACKETS: number of packets (NLA_U64)
+ * @NFTA_COUNTER_NAME: name of the counter (NLA_STRING)
*/
enum nft_counter_attributes {
NFTA_COUNTER_UNSPEC,
NFTA_COUNTER_BYTES,
NFTA_COUNTER_PACKETS,
+ NFTA_COUNTER_NAME,
__NFTA_COUNTER_MAX
};
#define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1)
/**
+ * enum nft_named_counter_attributes - nf_tables named counter netlink attributes
+ *
+ * @NFTA_NAMED_CTR_NAME: named counter name (NLA_STRING)
+ * @NFTA_NAMED_CTR_TABLE: table name (NLA_STRING)
+ * @NFTA_NAMED_CTR_USE: number of references to this named counter (NLA_U32)
+ * @NFTA_NAMED_CTR_ID: uniquely identifies a named counter in a transaction (NLA_U32)
+ * @NFTA_NAMED_CTR_BYTES: number of bytes (NLA_U64)
+ * @NFTA_NAMED_CTR_PACKETS: number of packets (NLA_U64)
+ */
+enum nft_named_counter_attributes {
+ NFTA_NAMED_CTR_UNSPEC,
+ NFTA_NAMED_CTR_NAME,
+ NFTA_NAMED_CTR_TABLE,
+ NFTA_NAMED_CTR_USE,
+ NFTA_NAMED_CTR_ID,
+ NFTA_NAMED_CTR_BYTES,
+ NFTA_NAMED_CTR_PACKETS,
+ __NFTA_NAMED_CTR_MAX
+};
+#define NFTA_NAMED_CTR_MAX (__NFTA_NAMED_CTR_MAX - 1)
+
+/**
* enum nft_log_attributes - nf_tables log expression netlink attributes
*
* @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
@@ -867,4 +900,5 @@ enum nft_gen_attributes {
};
#define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1)
+
#endif /* _LINUX_NF_TABLES_H */
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 7e68694..587af0e 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -239,6 +239,7 @@ nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule)
ctx->chain->use--;
return 0;
}
+
return -ENOENT;
}
@@ -271,7 +272,6 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
nft_trans_destroy(trans);
return err;
}
-
return 0;
}
@@ -325,6 +325,40 @@ static int nft_delset(struct nft_ctx *ctx, struct nft_set *set)
return err;
}
+static int nft_trans_counter_add(struct nft_ctx *ctx, int msg_type,
+ struct nft_counter *counter)
+{
+ struct nft_trans *trans;
+
+ trans = nft_trans_alloc(ctx, msg_type,
+ sizeof(struct nft_trans_counter));
+ if (!trans)
+ return -ENOMEM;
+
+ if (msg_type == NFT_MSG_NEWCOUNTER && ctx->nla[NFTA_NAMED_CTR_ID]) {
+ nft_trans_counter_id(trans) =
+ ntohl(nla_get_be32(ctx->nla[NFTA_NAMED_CTR_ID]));
+ }
+ nft_trans_counter(trans) = counter;
+ list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+
+ return 0;
+}
+
+static int nft_delcounter(struct nft_ctx *ctx, struct nft_counter *counter)
+{
+ int err;
+
+ err = nft_trans_counter_add(ctx, NFT_MSG_DELCOUNTER, counter);
+ if (err < 0)
+ return err;
+
+ list_del_rcu(&counter->list);
+ ctx->table->use--;
+
+ return err;
+}
+
/*
* Tables
*/
@@ -694,6 +728,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
nla_strlcpy(table->name, name, nla_len(name));
INIT_LIST_HEAD(&table->chains);
INIT_LIST_HEAD(&table->sets);
+ INIT_LIST_HEAD(&table->counters);
table->flags = flags;
nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
@@ -712,6 +747,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
int err;
struct nft_chain *chain, *nc;
struct nft_set *set, *ns;
+ struct nft_counter *counter, *na;
list_for_each_entry(chain, &ctx->table->chains, list) {
ctx->chain = chain;
@@ -730,7 +766,11 @@ static int nft_flush_table(struct nft_ctx *ctx)
if (err < 0)
goto out;
}
-
+ list_for_each_entry_safe(counter, na, &ctx->table->counters, list) {
+ err = nft_delcounter(ctx, counter);
+ if (err < 0)
+ goto out;
+ }
list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) {
ctx->chain = chain;
@@ -1108,7 +1148,7 @@ err:
return err;
}
-static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
+static const struct nla_policy nft_counter_priv_policy[NFTA_COUNTER_MAX + 1] = {
[NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
};
@@ -1120,7 +1160,8 @@ static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
struct nft_stats *stats;
int err;
- err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
+ err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr,
+ nft_counter_priv_policy);
if (err < 0)
return ERR_PTR(err);
@@ -3386,6 +3427,394 @@ err:
return err;
}
+static const struct nla_policy nft_counter_policy[NFTA_NAMED_CTR_MAX + 1] = {
+ [NFTA_NAMED_CTR_NAME] = { .type = NLA_NUL_STRING,
+ .len = NFT_CTR_MAXNAMELEN - 1 },
+ [NFTA_NAMED_CTR_BYTES] = { .type = NLA_U64 },
+ [NFTA_NAMED_CTR_PACKETS] = { .type = NLA_U64 },
+ [NFTA_NAMED_CTR_ID] = { .type = NLA_U32 },
+};
+
+struct nft_counter *nf_tables_counter_lookup(const struct nft_table *table,
+ const struct nlattr *nla)
+{
+ struct nft_counter *counter;
+
+ if (!nla)
+ return ERR_PTR(-EINVAL);
+ list_for_each_entry(counter, &table->counters, list) {
+ if (!nla_strcmp(nla, counter->name))
+ return counter;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+struct nft_counter *nft_counter_lookup(const struct nft_ctx *ctx,
+ const char *counter_name)
+{
+ struct nft_counter *cur, *counter = NULL;
+ struct nft_table *table = ctx->table;
+
+ list_for_each_entry(cur, &table->counters, list) {
+ if (strncmp(cur->name, counter_name, NFT_CTR_MAXNAMELEN) == 0)
+ return cur;
+ }
+
+ return counter;
+}
+EXPORT_SYMBOL_GPL(nft_counter_lookup);
+
+static int nft_ctx_init_from_counter(struct nft_ctx *ctx,
+ const struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[])
+{
+ struct net *net = sock_net(skb->sk);
+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+ struct nft_af_info *afi = NULL;
+ struct nft_table *table = NULL;
+
+ if (nfmsg->nfgen_family != NFPROTO_UNSPEC) {
+ afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
+ if (IS_ERR(afi))
+ return PTR_ERR(afi);
+ }
+
+ if (nla[NFTA_NAMED_CTR_TABLE]) {
+ if (!afi)
+ return -EAFNOSUPPORT;
+
+ table = nf_tables_table_lookup(afi, nla[NFTA_NAMED_CTR_TABLE]);
+ if (IS_ERR(table))
+ return PTR_ERR(table);
+ if (table->flags & NFT_TABLE_INACTIVE)
+ return -ENOENT;
+ }
+
+ nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
+
+ return 0;
+}
+
+static int nf_tables_newcounter(struct sock *nlsk, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[])
+{
+ struct nft_ctx ctx;
+ const struct nlattr *name;
+ struct nft_counter *counter, *matching;
+ int err;
+
+ if (!nla[NFTA_NAMED_CTR_NAME] || !nla[NFTA_NAMED_CTR_TABLE])
+ return -EINVAL;
+
+ err = nft_ctx_init_from_counter(&ctx, skb, nlh, nla);
+ if (err < 0)
+ return err;
+
+ matching = nf_tables_counter_lookup(ctx.table,
+ nla[NFTA_NAMED_CTR_NAME]);
+ if (!IS_ERR(matching)) {
+ if (nlh->nlmsg_flags & NLM_F_EXCL)
+ return -EEXIST;
+ if (nlh->nlmsg_flags & NLM_F_REPLACE)
+ return -EOPNOTSUPP;
+ else
+ return -EOPNOTSUPP;
+ }
+
+ if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+ return -ENOENT;
+
+ counter = kzalloc(sizeof(*counter), GFP_KERNEL);
+ if (!counter)
+ return -ENOMEM;
+
+ name = nla[NFTA_NAMED_CTR_NAME];
+ nla_strlcpy(counter->name, name, NFT_CTR_MAXNAMELEN);
+
+ if (nla[NFTA_NAMED_CTR_BYTES]) {
+ counter->counter.bytes =
+ be64_to_cpu(nla_get_be64(nla[NFTA_NAMED_CTR_BYTES]));
+ }
+
+ if (nla[NFTA_NAMED_CTR_PACKETS]) {
+ counter->counter.packets =
+ be64_to_cpu(nla_get_be64(nla[NFTA_NAMED_CTR_PACKETS]));
+ }
+
+ err = nft_trans_counter_add(&ctx, NFT_MSG_NEWCOUNTER, counter);
+ if (err < 0)
+ goto err;
+
+ list_add_tail_rcu(&counter->list, &ctx.table->counters);
+ ctx.table->use++;
+
+ return 0;
+err:
+ kfree(counter);
+ return err;
+}
+
+static int nf_tables_fill_counter(struct sk_buff *skb,
+ const struct nft_ctx *ctx,
+ struct nft_counter *counter,
+ u16 event, u16 flags, u32 type)
+{
+ struct nfgenmsg *nfmsg;
+ struct nlmsghdr *nlh;
+ u32 portid = ctx->portid;
+ u32 seq = ctx->seq;
+ u64 pkts, bytes;
+
+ event |= NFNL_SUBSYS_NFTABLES << 8;
+ nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
+ flags);
+ if (!nlh)
+ goto nla_put_failure;
+
+ nfmsg = nlmsg_data(nlh);
+ nfmsg->nfgen_family = ctx->afi->family;
+ nfmsg->version = NFNETLINK_V0;
+ nfmsg->res_id = htons(ctx->net->nft.base_seq & 0xffff);
+
+ if (type == NFT_MSG_GETCOUNTER_ZERO) {
+ write_seqlock_bh(&counter->counter.lock);
+ pkts = counter->counter.packets;
+ bytes = counter->counter.bytes;
+ counter->counter.bytes = 0;
+ counter->counter.packets = 0;
+ write_sequnlock_bh(&counter->counter.lock);
+ } else {
+ do {
+ seq = read_seqbegin(&counter->counter.lock);
+ bytes = counter->counter.bytes;
+ pkts = counter->counter.packets;
+ } while (read_seqretry(&counter->counter.lock, seq));
+ }
+
+ if (nla_put_string(skb, NFTA_NAMED_CTR_TABLE, ctx->table->name) ||
+ nla_put_string(skb, NFTA_NAMED_CTR_NAME, counter->name) ||
+ nla_put_be64(skb, NFTA_NAMED_CTR_PACKETS, cpu_to_be64(pkts)) ||
+ nla_put_be64(skb, NFTA_NAMED_CTR_BYTES, cpu_to_be64(bytes)) ||
+ nla_put_be32(skb, NFTA_NAMED_CTR_USE, htonl(counter->use)))
+ goto nla_put_failure;
+
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_trim(skb, nlh);
+ return -1;
+}
+
+static int nf_tables_counter_notify(const struct nft_ctx *ctx,
+ struct nft_counter *counter,
+ int event, gfp_t gfp_flags)
+{
+ struct sk_buff *skb;
+ u32 portid = ctx->portid;
+ int err;
+
+ if (!ctx->report &&
+ !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
+ return 0;
+
+ err = -ENOBUFS;
+ skb = nlmsg_new(NLMSG_GOODSIZE, gfp_flags);
+ if (!skb)
+ goto err;
+
+ err = nf_tables_fill_counter(skb, ctx, counter, event, 0, 0);
+ if (err < 0) {
+ kfree_skb(skb);
+ goto err;
+ }
+
+ err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES,
+ ctx->report, gfp_flags);
+err:
+ if (err < 0)
+ nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
+ return err;
+}
+
+static int nf_tables_delcounter(struct sock *nlsk, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[])
+{
+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+ struct nft_counter *counter;
+ struct nft_ctx ctx;
+ int err;
+
+ if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
+ return -EAFNOSUPPORT;
+ if (!nla[NFTA_NAMED_CTR_TABLE])
+ return -EINVAL;
+
+ err = nft_ctx_init_from_counter(&ctx, skb, nlh, nla);
+ if (err < 0)
+ return err;
+
+ counter = nf_tables_counter_lookup(ctx.table, nla[NFTA_NAMED_CTR_NAME]);
+ if (IS_ERR(counter))
+ return PTR_ERR(counter);
+
+ if (counter->use > 0)
+ return -EBUSY;
+
+ return nft_delcounter(&ctx, counter);
+}
+
+static int nf_tables_dump_counter(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct nft_counter *counter;
+ unsigned int idx, s_idx = cb->args[0];
+ struct nft_af_info *afi;
+ struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
+ struct net *net = sock_net(skb->sk);
+ int cur_family = cb->args[3];
+ struct nft_ctx *ctx = cb->data, ctx_counter;
+
+ if (cb->args[1])
+ return skb->len;
+
+ rcu_read_lock();
+ cb->seq = net->nft.base_seq;
+ list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
+ if (ctx->afi && ctx->afi != afi)
+ continue;
+
+ if (cur_family) {
+ if (afi->family != cur_family)
+ continue;
+
+ cur_family = 0;
+ }
+ list_for_each_entry_rcu(table, &afi->tables, list) {
+ if (ctx->table && ctx->table != table)
+ continue;
+
+ if (cur_table) {
+ if (cur_table != table)
+ continue;
+
+ cur_table = NULL;
+ }
+ idx = 0;
+ list_for_each_entry_rcu(counter, &table->counters,
+ list) {
+ if (idx < s_idx)
+ goto cont;
+
+ ctx_counter = *ctx;
+ ctx_counter.table = table;
+ ctx_counter.afi = afi;
+ if (nf_tables_fill_counter(skb, &ctx_counter,
+ counter,
+ NFT_MSG_NEWCOUNTER,
+ NLM_F_MULTI,
+ 0) < 0) {
+ cb->args[0] = idx;
+ cb->args[2] = (unsigned long)table;
+ cb->args[3] = afi->family;
+ goto done;
+ }
+ nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+cont:
+ idx++;
+ }
+ if (s_idx)
+ s_idx = 0;
+ }
+ }
+ cb->args[1] = 1;
+done:
+ rcu_read_unlock();
+ return skb->len;
+}
+
+static int nf_tables_dump_counter_done(struct netlink_callback *cb)
+{
+ kfree(cb->data);
+
+ return 0;
+}
+
+static int nf_tables_getcounter(struct sock *nlsk, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[])
+{
+ struct nft_ctx ctx;
+ struct sk_buff *skb2;
+ int err, ret;
+ struct nft_counter *counter;
+
+ /* Verify existence before starting dump */
+ err = nft_ctx_init_from_counter(&ctx, skb, nlh, nla);
+ if (err < 0)
+ return err;
+
+ if (nlh->nlmsg_flags & NLM_F_DUMP) {
+ struct netlink_dump_control c = {
+ .dump = nf_tables_dump_counter,
+ .done = nf_tables_dump_counter_done,
+ };
+ struct nft_ctx *ctx_dump;
+
+ ctx_dump = kmalloc(sizeof(*ctx_dump), GFP_KERNEL);
+ if (!ctx_dump)
+ return -ENOMEM;
+
+ *ctx_dump = ctx;
+ c.data = ctx_dump;
+
+ return netlink_dump_start(nlsk, skb, nlh, &c);
+ }
+
+ counter = nf_tables_counter_lookup(ctx.table, nla[NFTA_NAMED_CTR_NAME]);
+ if (IS_ERR(counter))
+ return PTR_ERR(counter);
+
+ skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb2)
+ return -ENOMEM;
+
+ err = nf_tables_fill_counter(skb2, &ctx, counter, NFT_MSG_NEWCOUNTER, 0,
+ NFNL_MSG_TYPE(nlh->nlmsg_type));
+ if (err < 0)
+ goto err;
+
+ ret = nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
+
+ /* this avoids a loop in nfnetlink. */
+ return ret == -EAGAIN ? -ENOBUFS : ret;
+
+err:
+ kfree_skb(skb2);
+ return err;
+}
+
+void nft_counter_put(struct nft_counter *counter)
+{
+ counter->use--;
+ module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL_GPL(nft_counter_put);
+
+int nft_counter_get(struct nft_counter *counter)
+{
+ if (!try_module_get(THIS_MODULE))
+ return -ENOENT;
+
+ counter->use++;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nft_counter_get);
+
static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
[NFT_MSG_NEWTABLE] = {
.call_batch = nf_tables_newtable,
@@ -3465,6 +3894,26 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
[NFT_MSG_GETGEN] = {
.call = nf_tables_getgen,
},
+ [NFT_MSG_NEWCOUNTER] = {
+ .call_batch = nf_tables_newcounter,
+ .attr_count = NFTA_NAMED_CTR_MAX,
+ .policy = nft_counter_policy,
+ },
+ [NFT_MSG_GETCOUNTER] = {
+ .call = nf_tables_getcounter,
+ .attr_count = NFTA_NAMED_CTR_MAX,
+ .policy = nft_counter_policy,
+ },
+ [NFT_MSG_GETCOUNTER_ZERO] = {
+ .call = nf_tables_getcounter,
+ .attr_count = NFTA_NAMED_CTR_MAX,
+ .policy = nft_counter_policy,
+ },
+ [NFT_MSG_DELCOUNTER] = {
+ .call_batch = nf_tables_delcounter,
+ .attr_count = NFTA_NAMED_CTR_MAX,
+ .policy = nft_counter_policy,
+ },
};
static void nft_chain_commit_update(struct nft_trans *trans)
@@ -3503,6 +3952,9 @@ static void nf_tables_commit_release(struct nft_trans *trans)
case NFT_MSG_DELSET:
nft_set_destroy(nft_trans_set(trans));
break;
+ case NFT_MSG_DELCOUNTER:
+ kfree(nft_trans_counter(trans));
+ break;
}
kfree(trans);
}
@@ -3608,6 +4060,19 @@ static int nf_tables_commit(struct sk_buff *skb)
}
nft_trans_destroy(trans);
break;
+ case NFT_MSG_NEWCOUNTER:
+ nf_tables_counter_notify(&trans->ctx,
+ nft_trans_counter(trans),
+ NFT_MSG_NEWCOUNTER,
+ GFP_KERNEL);
+ nft_trans_destroy(trans);
+ break;
+ case NFT_MSG_DELCOUNTER:
+ nf_tables_counter_notify(&trans->ctx,
+ nft_trans_counter(trans),
+ NFT_MSG_DELCOUNTER,
+ GFP_KERNEL);
+ break;
}
}
@@ -3638,6 +4103,9 @@ static void nf_tables_abort_release(struct nft_trans *trans)
case NFT_MSG_NEWSET:
nft_set_destroy(nft_trans_set(trans));
break;
+ case NFT_MSG_NEWCOUNTER:
+ kfree(nft_trans_counter(trans));
+ break;
}
kfree(trans);
}
@@ -3716,6 +4184,16 @@ static int nf_tables_abort(struct sk_buff *skb)
nft_trans_elem_set(trans)->nelems++;
nft_trans_destroy(trans);
break;
+ case NFT_MSG_NEWCOUNTER:
+ trans->ctx.table->use--;
+ list_del_rcu(&nft_trans_counter(trans)->list);
+ break;
+ case NFT_MSG_DELCOUNTER:
+ trans->ctx.table->use++;
+ list_add_tail_rcu(&nft_trans_counter(trans)->list,
+ &trans->ctx.table->counters);
+ nft_trans_destroy(trans);
+ break;
}
}
diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c
index 8815312..b76531c 100644
--- a/net/netfilter/nft_counter.c
+++ b/net/netfilter/nft_counter.c
@@ -17,15 +17,13 @@
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
-struct nft_counter_priv {
- seqlock_t lock;
- u64 bytes;
- u64 packets;
+struct nft_expr_counter {
+ struct nft_counter *counter;
};
-static void nft_counter_eval(const struct nft_expr *expr,
- struct nft_data data[NFT_REG_MAX + 1],
- const struct nft_pktinfo *pkt)
+static void nft_counter_priv_eval(const struct nft_expr *expr,
+ struct nft_data data[NFT_REG_MAX + 1],
+ const struct nft_pktinfo *pkt)
{
struct nft_counter_priv *priv = nft_expr_priv(expr);
@@ -35,7 +33,20 @@ static void nft_counter_eval(const struct nft_expr *expr,
write_sequnlock_bh(&priv->lock);
}
-static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
+static void nft_counter_eval(const struct nft_expr *expr,
+ struct nft_data data[NFT_REG_MAX + 1],
+ const struct nft_pktinfo *pkt)
+{
+ struct nft_expr_counter *priv = nft_expr_priv(expr);
+
+ write_seqlock_bh(&priv->counter->counter.lock);
+ priv->counter->counter.bytes += pkt->skb->len;
+ priv->counter->counter.packets++;
+ write_sequnlock_bh(&priv->counter->counter.lock);
+}
+
+static int nft_counter_priv_dump(struct sk_buff *skb,
+ const struct nft_expr *expr)
{
struct nft_counter_priv *priv = nft_expr_priv(expr);
unsigned int seq;
@@ -58,38 +69,121 @@ nla_put_failure:
return -1;
}
-static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
+static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ struct nft_expr_counter *priv = nft_expr_priv(expr);
+ unsigned int seq;
+ u64 bytes;
+ u64 packets;
+
+ if (nla_put_string(skb, NFTA_COUNTER_NAME, priv->counter->name))
+ goto nla_put_failure;
+
+ do {
+ seq = read_seqbegin(&priv->counter->counter.lock);
+ bytes = priv->counter->counter.bytes;
+ packets = priv->counter->counter.packets;
+ } while (read_seqretry(&priv->counter->counter.lock, seq));
+
+ if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(bytes)))
+ goto nla_put_failure;
+ if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(packets)))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static const struct nla_policy nft_counter_priv_policy[NFTA_COUNTER_MAX + 1] = {
[NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
};
+static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
+ [NFTA_COUNTER_NAME] = { .type = NLA_STRING },
+};
+
+static int nft_counter_priv_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_counter_priv *priv = nft_expr_priv(expr);
+
+ if (tb[NFTA_COUNTER_PACKETS]) {
+ priv->packets =
+ be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
+ }
+ if (tb[NFTA_COUNTER_BYTES]) {
+ priv->bytes =
+ be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
+ }
+ seqlock_init(&priv->lock);
+ return 0;
+}
+
static int nft_counter_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
- struct nft_counter_priv *priv = nft_expr_priv(expr);
+ struct nft_expr_counter *priv = nft_expr_priv(expr);
+ struct nft_counter *tmp;
- if (tb[NFTA_COUNTER_PACKETS])
- priv->packets = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
- if (tb[NFTA_COUNTER_BYTES])
- priv->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
+ if (!tb[NFTA_COUNTER_NAME])
+ return -EINVAL;
+
+ tmp = nft_counter_lookup(ctx, nla_data(tb[NFTA_COUNTER_NAME]));
+ if (!tmp)
+ return -ENOENT;
+
+ seqlock_init(&tmp->counter.lock);
+ priv->counter = tmp;
+
+ nft_counter_get(priv->counter);
- seqlock_init(&priv->lock);
return 0;
}
+static void nft_counter_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+{
+ struct nft_expr_counter *priv = nft_expr_priv(expr);
+
+ nft_counter_put(priv->counter);
+}
+
static struct nft_expr_type nft_counter_type;
-static const struct nft_expr_ops nft_counter_ops = {
+static const struct nft_expr_ops nft_counter_priv_ops = {
.type = &nft_counter_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_counter_priv)),
+ .eval = nft_counter_priv_eval,
+ .init = nft_counter_priv_init,
+ .dump = nft_counter_priv_dump,
+};
+
+static const struct nft_expr_ops nft_counter_ops = {
+ .type = &nft_counter_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_counter)),
.eval = nft_counter_eval,
.init = nft_counter_init,
.dump = nft_counter_dump,
+ .destroy = nft_counter_destroy,
};
+static const struct nft_expr_ops *
+nft_counter_select_ops(const struct nft_ctx *ctx,
+ const struct nlattr * const tb[])
+{
+ if (tb[NFTA_COUNTER_NAME])
+ return &nft_counter_ops;
+
+ return &nft_counter_priv_ops;
+}
+
static struct nft_expr_type nft_counter_type __read_mostly = {
.name = "counter",
- .ops = &nft_counter_ops,
+ .select_ops = &nft_counter_select_ops,
.policy = nft_counter_policy,
.maxattr = NFTA_COUNTER_MAX,
.owner = THIS_MODULE,
--
1.7.10.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [libnftnl v2 2/2] tests: add unit tests for counters
[not found] ` <cover.1422299705.git.ana@soleta.eu>
2015-01-26 19:43 ` [libnftnl v2 1/2] src: Add counters support Ana Rey Botello
@ 2015-01-26 19:43 ` Ana Rey Botello
1 sibling, 0 replies; 7+ messages in thread
From: Ana Rey Botello @ 2015-01-26 19:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Ana Rey Botello
This adds unit test for counters and updates the expr-counter test.
Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
tests/Makefile.am | 4 ++
tests/nft-counter-test.c | 86 +++++++++++++++++++++++++++++++++++++++++
tests/nft-expr_counter-test.c | 4 ++
3 files changed, 94 insertions(+)
create mode 100644 tests/nft-counter-test.c
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c0356f1..471cda5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -9,6 +9,7 @@ check_PROGRAMS = nft-parsing-test \
nft-chain-test \
nft-rule-test \
nft-set-test \
+ nft-counter-test \
nft-expr_bitwise-test \
nft-expr_byteorder-test \
nft-expr_counter-test \
@@ -44,6 +45,9 @@ nft_rule_test_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
nft_set_test_SOURCES = nft-set-test.c
nft_set_test_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+nft_counter_test_SOURCES = nft-counter-test.c
+nft_counter_test_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+
nft_expr_bitwise_test_SOURCES = nft-expr_bitwise-test.c
nft_expr_bitwise_test_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
diff --git a/tests/nft-counter-test.c b/tests/nft-counter-test.c
new file mode 100644
index 0000000..6a96ab5
--- /dev/null
+++ b/tests/nft-counter-test.c
@@ -0,0 +1,86 @@
+/*
+ * (C) 2015 by Ana Rey <ana@soleta.eu>
+ * (C) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2014 Intra2net AG <http://www.intra2net.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/counter.h>
+
+static int test_ok = 1;
+
+static void print_err(const char *msg)
+{
+ test_ok = 0;
+ printf("\033[31mERROR:\e[0m %s\n", msg);
+}
+
+static void cmp_nft_counter(struct nft_counter *a, struct nft_counter *b)
+{
+ if (strcmp(nft_counter_attr_get_str(a, NFT_COUNTER_ATTR_TABLE),
+ nft_counter_attr_get_str(b, NFT_COUNTER_ATTR_TABLE)) != 0)
+ print_err("Counter table mismatches");
+ if (strcmp(nft_counter_attr_get_str(a, NFT_COUNTER_ATTR_NAME),
+ nft_counter_attr_get_str(b, NFT_COUNTER_ATTR_NAME)) != 0)
+ print_err("Counter name mismatches");
+ if (nft_counter_attr_get_u32(a, NFT_COUNTER_ATTR_FAMILY) !=
+ nft_counter_attr_get_u32(b, NFT_COUNTER_ATTR_FAMILY))
+ print_err("Counter family mismatches");
+ if (nft_counter_attr_get_u32(a, NFT_COUNTER_ATTR_USE) !=
+ nft_counter_attr_get_u32(b, NFT_COUNTER_ATTR_USE))
+ print_err("Counter use mismatches");
+ if (nft_counter_attr_get_u64(a, NFT_COUNTER_ATTR_PKTS) !=
+ nft_counter_attr_get_u64(b, NFT_COUNTER_ATTR_PKTS))
+ print_err("Counter packets mismatches");
+ if (nft_counter_attr_get_u64(a, NFT_COUNTER_ATTR_BYTES) !=
+ nft_counter_attr_get_u64(b, NFT_COUNTER_ATTR_BYTES))
+ print_err("Counter bytes mismatches");
+}
+
+int main(int argc, char *argv[])
+{
+ struct nft_counter *a, *b = NULL;
+ char buf[4096];
+ struct nlmsghdr *nlh;
+
+ a = nft_counter_alloc();
+ b = nft_counter_alloc();
+ if (a == NULL || b == NULL)
+ print_err("OOM");
+
+ nft_counter_attr_set_str(a, NFT_COUNTER_ATTR_TABLE, "test-table");
+ nft_counter_attr_set_str(a, NFT_COUNTER_ATTR_NAME, "counter-name");
+ nft_counter_attr_set_u32(a, NFT_COUNTER_ATTR_FAMILY, 0x12345678);
+ nft_counter_attr_set_u32(a, NFT_COUNTER_ATTR_USE, 0x12345678);
+ nft_counter_attr_set_u64(a, NFT_COUNTER_ATTR_PKTS, 0x123456789abcdef0);
+ nft_counter_attr_set_u64(a, NFT_COUNTER_ATTR_BYTES, 0x123456789abcdef0);
+
+ /* cmd extracted from include/linux/netfilter/nf_tables.h */
+ nlh = nft_counter_nlmsg_build_hdr(buf, NFT_MSG_NEWCOUNTER, AF_INET,
+ 0, 1234);
+ nft_counter_nlmsg_build_payload(nlh, a);
+
+ if (nft_counter_nlmsg_parse(nlh, b) < 0)
+ print_err("parsing problems");
+
+ cmp_nft_counter(a, b);
+
+ nft_counter_free(a); nft_counter_free(b);
+
+ if (!test_ok)
+ exit(EXIT_FAILURE);
+
+ printf("%s: \033[32mOK\e[0m\n", argv[0]);
+ return EXIT_SUCCESS;
+}
diff --git a/tests/nft-expr_counter-test.c b/tests/nft-expr_counter-test.c
index e27d20a..a961fa7 100644
--- a/tests/nft-expr_counter-test.c
+++ b/tests/nft-expr_counter-test.c
@@ -36,6 +36,9 @@ static void cmp_nft_rule_expr(struct nft_rule_expr *rule_a,
if (nft_rule_expr_get_u64(rule_a, NFT_EXPR_CTR_PACKETS) !=
nft_rule_expr_get_u64(rule_b, NFT_EXPR_CTR_PACKETS))
print_err("Expr NFT_EXPR_CTR_PACKETS mismatches");
+ if (strcmp(nft_rule_expr_get_str(rule_a, NFT_EXPR_CTR_NAME),
+ nft_rule_expr_get_str(rule_b, NFT_EXPR_CTR_NAME)) != 0)
+ print_err("Expr NFT_EXPR_CTR_NAME mismatches");
}
int main(int argc, char *argv[])
@@ -56,6 +59,7 @@ int main(int argc, char *argv[])
if (ex == NULL)
print_err("OOM");
+ nft_rule_expr_set_str(ex, NFT_EXPR_CTR_NAME, "counter-name");
nft_rule_expr_set_u64(ex, NFT_EXPR_CTR_BYTES, 0x123456789abcdef0);
nft_rule_expr_set_u64(ex, NFT_EXPR_CTR_PACKETS, 0x123456789abcdef0);
nft_rule_add_expr(a, ex);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [nft v2 2/2] tests: regression: Add counters support
[not found] ` <cover.1422299750.git.ana@soleta.eu>
2015-01-26 19:43 ` [nft v2 1/2] src: Add the accounter support Ana Rey Botello
@ 2015-01-26 19:43 ` Ana Rey Botello
1 sibling, 0 replies; 7+ messages in thread
From: Ana Rey Botello @ 2015-01-26 19:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Ana Rey
From: Ana Rey <ana@soleta.eu>
This adds the accounter support into this regression test and the test
file ip/counter.c.
Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
tests/regression/ip/counter.t | 15 ++++++
tests/regression/nft-test.py | 110 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 125 insertions(+)
create mode 100644 tests/regression/ip/counter.t
diff --git a/tests/regression/ip/counter.t b/tests/regression/ip/counter.t
new file mode 100644
index 0000000..783b433
--- /dev/null
+++ b/tests/regression/ip/counter.t
@@ -0,0 +1,15 @@
+*ip;test-ip4
+*inet;test-inet
+:input;type filter hook input priority 0
+
+> counter1;ok
+> counter2;ok
+> counter3;ok
+
+ip daddr 192.168.0.1 counter name counter1;ok;ip daddr 192.168.0.1 counter counter1
+ip daddr 192.168.0.2 counter name counter1;ok;ip daddr 192.168.0.2 counter counter1
+
+ip daddr 192.168.0.1 counter name counter2;ok;ip daddr 192.168.0.1 counter counter2
+ip daddr 192.168.0.2 counter name counter3;ok;ip daddr 192.168.0.2 counter counter3
+
+ip daddr 192.168.0.2 counter name not-counter;fail
diff --git a/tests/regression/nft-test.py b/tests/regression/nft-test.py
index 7e5b475..ab95c27 100755
--- a/tests/regression/nft-test.py
+++ b/tests/regression/nft-test.py
@@ -27,6 +27,7 @@ log_file = None
table_list = []
chain_list = []
all_set = dict()
+all_counter = dict()
signal_received = 0
@@ -404,6 +405,81 @@ def set_check_element(rule1, rule2):
return ret
+def counter_add(counter_info, table_list, filename, lineno):
+ '''
+ Adds an counter.
+ '''
+
+ if not table_list:
+ reason = "Missing table to add rule"
+ print_error(reason, filename, lineno)
+ return -1
+
+ for table in table_list:
+ if counter_exist(counter_info[0], table, filename, lineno):
+ reason = "This counter " + counter_info + " exists in " + \
+ table[1] + ". I cannot add it again"
+ print_error(reason, filename, lineno)
+ return -1
+
+ table_info = " " + table[0] + " " + table[1] + " "
+ counter_text = " " + counter_info[0]
+ cmd = "nft add counter" + table_info + counter_text
+ ret = execute_cmd(cmd, filename, lineno)
+
+ if (ret == 0 and counter_info[1].rstrip() == "fail") or \
+ (ret != 0 and counter_info[1].rstrip() == "ok"):
+ reason = cmd + ": " + "I cannot add the counter " + \
+ counter_info[0]
+ print_error(reason, filename, lineno)
+ return -1
+
+ if not counter_exist(counter_info[0], table, filename, lineno):
+ reason = "I have just added the counter " + counter_info[0] + \
+ " to the table " + table[1] + " but it does not exist"
+ print_error(reason, filename, lineno)
+ return -1
+
+ return 0
+
+
+def counter_delete(all_counter, table, filename=None, lineno=None):
+ '''
+ Deletes counter and its content.
+ '''
+
+ for counter_name in all_counter.keys():
+ # Check if exists the counter
+ if not counter_exist(counter_name, table, filename, lineno):
+ reason = "The counter " + counter_name + \
+ " does not exist, I cannot delete it"
+ print_error(reason, filename, lineno)
+ return -1
+
+ # We delete the counter.
+ table_info = " " + table[0] + " " + table[1] + " "
+ cmd = "nft delete counter " + table_info + " " + counter_name
+ ret = execute_cmd(cmd, filename, lineno)
+ # Check if the counter still exists after I deleted it.
+ if ret != 0 or counter_exist(counter_name, table, filename, lineno):
+ reason = "Cannot remove the counter " + counter_name
+ print_error(reason, filename, lineno)
+ return -1
+
+ return 0
+
+
+def counter_exist(counter_name, table, filename, lineno):
+ '''
+ Check if the counter exists.
+ '''
+ table_info = " " + table[0] + " " + table[1] + " "
+ cmd = "nft list counter" + table_info + counter_name
+ ret = execute_cmd(cmd, filename, lineno)
+
+ return True if (ret == 0) else False
+
+
def output_clean(pre_output, chain):
pos_chain = pre_output[0].find(chain)
if pos_chain == -1:
@@ -527,6 +603,8 @@ def cleanup_on_exit():
ret = chain_delete(chain, table, "", "")
if all_set:
ret = set_delete(all_set, table)
+ if all_counter:
+ ret = counter_delete(all_counter, table)
ret = table_delete(table)
@@ -619,6 +697,19 @@ def set_element_process(element_line, filename, lineno):
table_list, filename, lineno)
+def counter_process(counter_line, filename, lineno):
+ counter_info = []
+ counter_name = counter_line.split(";")[0]
+ counter_info.append(counter_name)
+ counter_state = counter_line.split(";")[1] # ok or fail
+ counter_info.append(counter_state)
+ ret = counter_add(counter_info, table_list, filename, lineno)
+ if ret == 0:
+ all_counter[counter_name] = set()
+
+ return ret
+
+
def run_test_file(filename, force_all_family_option, specific_file):
'''
Runs a test file
@@ -674,6 +765,16 @@ def run_test_file(filename, force_all_family_option, specific_file):
passed += 1
continue
+ if line[0] == ">": # Adds this counter
+ counter_line = line[1:].strip()
+ ret = counter_process(counter_line, filename, lineno)
+ tests += 1
+ if ret == -1:
+ total_test_passed = False
+ continue
+ passed += 1
+ continue
+
if line[0] == "?": # Adds elements in a set
element_line = line.rstrip()[1:].split(";")
ret = set_element_process(element_line, filename, lineno)
@@ -729,6 +830,14 @@ def run_test_file(filename, force_all_family_option, specific_file):
reason = "There is a problem when we delete a set"
print_error(reason, filename, lineno)
+ # We delete counter.
+ if all_counter:
+ ret = counter_delete(all_counter, table, filename, lineno)
+ if ret != 0:
+ total_test_passed = False
+ reason = "There is a problem when we delete an counter"
+ print_error(reason, filename, lineno)
+
# We delete tables.
ret = table_delete(table, filename, lineno)
@@ -749,6 +858,7 @@ def run_test_file(filename, force_all_family_option, specific_file):
del table_list[:]
del chain_list[:]
all_set.clear()
+ all_counter.clear()
return [tests, passed, total_warning, total_error, total_unit_run]
--
1.7.10.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2015-01-26 19:42 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-01-26 19:43 [nf v2 0/6] Accounting objects support in nft Ana Rey Botello
2015-01-26 19:43 ` [nf v2 1/2] netfilter: Rename from nft_counter to nft_counter_priv Ana Rey Botello
[not found] ` <cover.1422299750.git.ana@soleta.eu>
2015-01-26 19:43 ` [nft v2 1/2] src: Add the accounter support Ana Rey Botello
2015-01-26 19:43 ` [nft v2 2/2] tests: regression: Add counters support Ana Rey Botello
2015-01-26 19:43 ` [nf v2 2/2] netfilter: named counter: add support to counters in nftables Ana Rey Botello
[not found] ` <cover.1422299705.git.ana@soleta.eu>
2015-01-26 19:43 ` [libnftnl v2 1/2] src: Add counters support Ana Rey Botello
2015-01-26 19:43 ` [libnftnl v2 2/2] tests: add unit tests for counters Ana Rey Botello
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).