netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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).