All of lore.kernel.org
 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
  2015-01-26 19:43 ` [nf v2 2/2] netfilter: named counter: add support to counters in nftables Ana Rey Botello
                   ` (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
@ 2015-01-26 19:43 ` Ana Rey Botello
       [not found] ` <cover.1422299705.git.ana@soleta.eu>
       [not found] ` <cover.1422299750.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
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
     [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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.