All of lore.kernel.org
 help / color / mirror / Atom feed
* Accounting objects support in nft
@ 2015-01-12 10:55 ana
       [not found] ` <cover.1421059771.git.ana@soleta.eu>
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: ana @ 2015-01-12 10:55 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Ana Rey Botello

From: Ana Rey Botello <ana@soleta.eu>

Hi,

With this patchset, we add accounting objects support to let us
manipulate extended accounting objects.

Example of use in nft:

 # nft add acct ip filter http-traffic
 # nft add acct ip filter https-traffic

 # nft add rule ip filter output tcp dport 80 acct http-traffic
 # nft add rule ip filter output tcp dport 443 acct https-traffic

 # nft delete acct ip filter https-traffic

 # nft list table ip test

table ip filter {
        acct http-traffic { pkts 779 bytes 99495}
        acct https-traffic { pkts 189 bytes 37824}

        chain output {
             type filter hook output priority 0;
             tcp dport http acct http-traffic
             tcp dport https acct 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.

Ana Rey (2):

  netfilter: acct: add support to accounters in nftables

 include/net/netfilter/nf_tables.h        |   41 +++
 include/uapi/linux/netfilter/nf_tables.h |   41 +++
 net/netfilter/Kconfig                    |    7 +
 net/netfilter/Makefile                   |    1 +
 net/netfilter/nf_tables_api.c            |  485 +++++++++++++++++++++++++++++-
 net/netfilter/nft_acct.c                 |  109 +++++++
 6 files changed, 679 insertions(+), 5 deletions(-)
 create mode 100644 net/netfilter/nft_acct.c

  src: Add accounters support

 examples/Makefile.am                |   23 +-
 examples/nft-acct-add.c             |  136 ++++++++
 examples/nft-acct-del.c             |  133 ++++++++
 examples/nft-acct-get.c             |  135 ++++++++
 examples/nft-acct-reset.c           |  121 +++++++
 examples/nft-rule-acct-add.c        |  220 +++++++++++++
 examples/nft-rule-get.c             |    1 +
 include/buffer.h                    |    1 +
 include/libnftnl/Makefile.am        |    3 +-
 include/libnftnl/acct.h             |   87 +++++
 include/libnftnl/expr.h             |    3 +
 include/linux/netfilter/nf_tables.h |   41 +++
 src/Makefile.am                     |    2 +
 src/acct.c                          |  612 +++++++++++++++++++++++++++++++++++
 src/expr/acct.c                     |  201 ++++++++++++
 src/libnftnl.map                    |   30 ++
 16 files changed, 1747 insertions(+), 2 deletions(-)
 create mode 100644 examples/nft-acct-add.c
 create mode 100644 examples/nft-acct-del.c
 create mode 100644 examples/nft-acct-get.c
 create mode 100644 examples/nft-acct-reset.c
 create mode 100644 examples/nft-rule-acct-add.c
 create mode 100644 include/libnftnl/acct.h
 create mode 100644 src/acct.c
 create mode 100644 src/expr/acct.c

  src: Add the accounter support
  tests: regression: Accounter support

 include/linux/netfilter/nf_tables.h |   41 +++++++
 include/mnl.h                       |    8 ++
 include/netlink.h                   |   18 +++
 include/rule.h                      |   46 +++++++
 include/statement.h                 |    9 ++
 src/evaluate.c                      |   14 ++-
 src/mnl.c                           |  117 ++++++++++++++++++
 src/netlink.c                       |  231 +++++++++++++++++++++++++++++++++++
 src/netlink_delinearize.c           |   14 +++
 src/netlink_linearize.c             |   16 +++
 src/parser_bison.y                  |   72 ++++++++++-
 src/rule.c                          |  137 +++++++++++++++++++++
 src/scanner.l                       |    2 +
 src/statement.c                     |   16 +++
 tests/regression/ip/acct.t          |   17 +++
 tests/regression/nft-test.py        |  112 +++++++++++++++++
 16 files changed, 866 insertions(+), 4 deletions(-)
 create mode 100644 tests/regression/ip/acct.t

-- 
1.7.10.4


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [nf-next] netfilter: acct: add support to accounters in nftables
       [not found] ` <cover.1421059771.git.ana@soleta.eu>
@ 2015-01-12 10:55   ` ana
  2015-01-12 11:31     ` Pablo Neira Ayuso
  2015-01-12 11:42     ` Patrick McHardy
  0 siblings, 2 replies; 20+ messages in thread
From: ana @ 2015-01-12 10:55 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Ana Rey

From: Ana Rey <ana@soleta.eu>

This adds accounting objects support to allow us to manipulate the nftables's
extended accounting intraestructure.

Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
 include/net/netfilter/nf_tables.h        |   41 +++
 include/uapi/linux/netfilter/nf_tables.h |   41 +++
 net/netfilter/Kconfig                    |    7 +
 net/netfilter/Makefile                   |    1 +
 net/netfilter/nf_tables_api.c            |  485 +++++++++++++++++++++++++++++-
 net/netfilter/nft_acct.c                 |  109 +++++++
 6 files changed, 679 insertions(+), 5 deletions(-)
 create mode 100644 net/netfilter/nft_acct.c

diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 3ae969e..96f5292 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -408,6 +408,17 @@ struct nft_trans {
 	char				data[0];
 };
 
+
+struct nft_trans_acct {
+	struct nft_acct	*acct;
+	u32		acct_id;
+};
+
+#define nft_trans_acct(trans)	\
+	(((struct nft_trans_acct *)trans->data)->acct)
+#define nft_trans_acct_id(trans)	\
+	(((struct nft_trans_acct *)trans->data)->acct_id)
+
 struct nft_trans_rule {
 	struct nft_rule			*rule;
 };
@@ -570,6 +581,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt,
  *	@list: used internally
  *	@chains: chains in the table
  *	@sets: sets in the table
+ *	@accts: accts in the table
  *	@hgenerator: handle generator state
  *	@use: number of chain references to this table
  *	@flags: table flag (see enum nft_table_flags)
@@ -579,6 +591,7 @@ struct nft_table {
 	struct list_head		list;
 	struct list_head		chains;
 	struct list_head		sets;
+	struct list_head		accts;
 	u64				hgenerator;
 	u32				use;
 	u16				flags;
@@ -637,6 +650,31 @@ 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_acct - nf_tables acct instance
+ *
+ * @list: table acct list node
+ * @name: name of the acct
+ * @pkts:  number of packets
+ * @bytes:  number of bytes
+ * use: number of rule references to this acct
+ */
+struct nft_acct {
+	struct list_head	list;
+	char                    name[NFT_ACCT_MAXNAMELEN];
+	atomic64_t		pkts;
+	atomic64_t		bytes;
+	u32			use;
+};
+
+struct nft_acct *nf_tables_acct_lookup(const struct nft_table *table,
+				       const struct nlattr *nla);
+struct nft_acct *nft_acct_find_get(const struct nft_ctx *ctx,
+				   const char *acct_name);
+void nft_acct_update(const struct sk_buff *skb, struct nft_acct *acct);
+void nft_acct_put(struct nft_acct *acct);
+int nft_acct_get(struct nft_acct *acct);
+
 #define nft_dereference(p)					\
 	nfnl_dereference(p, NFNL_SUBSYS_NFTABLES)
 
@@ -655,4 +693,7 @@ void nft_unregister_expr(struct nft_expr_type *);
 #define MODULE_ALIAS_NFT_SET() \
 	MODULE_ALIAS("nft-set")
 
+#define MODULE_ALIAS_NFT_ACCT() \
+	MODULE_ALIAS("nft-acct")
+
 #endif /* _NET_NF_TABLES_H */
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 832bc46..b5e17a0 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_ACCT_MAXNAMELEN	32
 #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_NEWACCT: create a new account (enum nft_acct_attributes)
+ * @NFT_MSG_GETACCT: get a account (enum nft_acct_attributes)
+ * @NFT_MSG_GETACCT_ZERO: get a reset accounter (enum nft_acct_attributes)
+ * @NFT_MSG_DELACCT: delete a account (enum nft_acct_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_NEWACCT,
+	NFT_MSG_GETACCT,
+	NFT_MSG_GETACCT_ZERO,
+	NFT_MSG_DELACCT,
 	NFT_MSG_MAX,
 };
 
@@ -867,4 +876,36 @@ enum nft_gen_attributes {
 };
 #define NFTA_GEN_MAX		(__NFTA_GEN_MAX - 1)
 
+/**
+ * enum nft_acct_attributes - nf_tables acct netlink attributes
+ *
+ * @NFTA_ACCT_NAME: name of the accounter (NLA_STRING)
+ * @NFTA_ACCT_TABLE: table name (NLA_STRING)
+ * @NFTA_ACCT_BYTES: number of bytes (NLA_U64)
+ * @NFTA_ACCT_PACKETS: number of packets (NLA_U64)
+ * @NFTA_ACCT_USE: number of rules using this account object (NLA_U32)
+ * @NFTA_ACCT_ID: uniquely identifies a acct in a transaction (NLA_U32)
+ */
+enum nft_acct_attributes {
+	NFTA_ACCT_UNSPEC,
+	NFTA_ACCT_NAME,
+	NFTA_ACCT_TABLE,
+	NFTA_ACCT_BYTES,
+	NFTA_ACCT_PACKETS,
+	NFTA_ACCT_USE,
+	NFTA_ACCT_ID,
+	__NFTA_ACCT_MAX
+};
+#define NFTA_ACCT_MAX		(__NFTA_ACCT_MAX - 1)
+
+enum nft_acct_expr_attr {
+	NFTA_ACCT_EXPR_UNSPEC,
+	NFTA_ACCT_EXPR_NAME,
+	__NFTA_ACCT_EXPR_MAX
+};
+#define NFTA_ACCT_EXPR_MAX        (__NFTA_ACCT_EXPR_MAX - 1)
+
+#ifndef NFTA_ACCT_NAME_MAX
+#define NFTA_ACCT_NAME_MAX	32
+#endif
 #endif /* _LINUX_NF_TABLES_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index b02660f..f0eeb89 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -446,6 +446,13 @@ config NF_TABLES_INET
 	help
 	  This option enables support for a mixed IPv4/IPv6 "inet" table.
 
+config NFT_ACCT
+	depends on NF_TABLES
+	tristate "Netfilter nf_tables acct module"
+	help
+	  This option adds the "acct" expression that you can use to update
+	  packet accounting objects.
+
 config NFT_EXTHDR
 	depends on NF_TABLES
 	tristate "Netfilter nf_tables IPv6 exthdr module"
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 89f73a9..fcc483f 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -90,6 +90,7 @@ obj-$(CONFIG_NFT_COUNTER)	+= nft_counter.o
 obj-$(CONFIG_NFT_LOG)		+= nft_log.o
 obj-$(CONFIG_NFT_MASQ)		+= nft_masq.o
 obj-$(CONFIG_NFT_REDIR)		+= nft_redir.o
+obj-$(CONFIG_NFT_ACCT)          += nft_acct.o
 
 # generic X tables 
 obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 3b3ddb4..faf970a 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;
 }
 
@@ -325,6 +326,39 @@ static int nft_delset(struct nft_ctx *ctx, struct nft_set *set)
 	return err;
 }
 
+static int nft_trans_acct_add(struct nft_ctx *ctx, int msg_type,
+			      struct nft_acct *acct)
+{
+	struct nft_trans *trans;
+
+	trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_acct));
+	if (!trans)
+		return -ENOMEM;
+
+	if (msg_type == NFT_MSG_NEWACCT && ctx->nla[NFTA_ACCT_ID]) {
+		nft_trans_acct_id(trans) =
+			ntohl(nla_get_be32(ctx->nla[NFTA_ACCT_ID]));
+	}
+	nft_trans_acct(trans) = acct;
+	list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+
+	return 0;
+}
+
+static int nft_delacct(struct nft_ctx *ctx, struct nft_acct *acct)
+{
+	int err;
+
+	err = nft_trans_acct_add(ctx, NFT_MSG_DELACCT, acct);
+	if (err < 0)
+		return err;
+
+	list_del_rcu(&acct->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->accts);
 	table->flags = flags;
 
 	nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
@@ -712,13 +747,18 @@ static int nft_flush_table(struct nft_ctx *ctx)
 	int err;
 	struct nft_chain *chain, *nc;
 	struct nft_set *set, *ns;
+	struct nft_acct *acct, *na;
 
-	list_for_each_entry(chain, &ctx->table->chains, list) {
+	list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) {
 		ctx->chain = chain;
 
 		err = nft_delrule_by_chain(ctx);
 		if (err < 0)
 			goto out;
+
+		err = nft_delchain(ctx);
+		if (err < 0)
+			goto out;
 	}
 
 	list_for_each_entry_safe(set, ns, &ctx->table->sets, list) {
@@ -731,10 +771,8 @@ static int nft_flush_table(struct nft_ctx *ctx)
 			goto out;
 	}
 
-	list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) {
-		ctx->chain = chain;
-
-		err = nft_delchain(ctx);
+	list_for_each_entry_safe(acct, na, &ctx->table->accts, list) {
+		err = nft_delacct(ctx, acct);
 		if (err < 0)
 			goto out;
 	}
@@ -3386,6 +3424,396 @@ err:
 	return err;
 }
 
+static const struct nla_policy nft_acct_policy[NFTA_ACCT_MAX + 1] = {
+	[NFTA_ACCT_NAME]	= { .type = NLA_NUL_STRING,
+				   .len = NFTA_ACCT_NAME_MAX - 1 },
+	[NFTA_ACCT_BYTES]	= { .type = NLA_U64 },
+	[NFTA_ACCT_PACKETS]	= { .type = NLA_U64 },
+	[NFTA_ACCT_ID]		= { .type = NLA_U32 },
+};
+
+struct nft_acct *nf_tables_acct_lookup(const struct nft_table *table,
+				       const struct nlattr *nla)
+{
+	struct nft_acct *acct;
+
+	if (!nla)
+		return ERR_PTR(-EINVAL);
+
+	list_for_each_entry(acct, &table->accts, list) {
+		if (!nla_strcmp(nla, acct->name))
+			return acct;
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+
+struct nft_acct *nft_acct_find_get(const struct nft_ctx *ctx,
+				   const char *acct_name)
+{
+	struct nft_acct *cur, *acct = NULL;
+	struct nft_table *table = ctx->table;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(cur, &table->accts, list) {
+		if (strncmp(cur->name, acct_name, NFTA_ACCT_NAME_MAX) != 0)
+			continue;
+
+		acct = cur;
+		break;
+	}
+	rcu_read_unlock();
+
+	return acct;
+}
+EXPORT_SYMBOL_GPL(nft_acct_find_get);
+
+static int nft_ctx_init_from_acct(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_ACCT_TABLE]) {
+		if (!afi)
+			return -EAFNOSUPPORT;
+
+		table = nf_tables_table_lookup(afi, nla[NFTA_ACCT_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_newacct(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_acct *acct, *matching;
+	unsigned int size = 0;
+	int err;
+
+	if (!nla[NFTA_ACCT_NAME] || !nla[NFTA_ACCT_TABLE])
+		return -EINVAL;
+
+	err = nft_ctx_init_from_acct(&ctx, skb, nlh, nla);
+	if (err < 0)
+		return err;
+
+	matching = nf_tables_acct_lookup(ctx.table, nla[NFTA_ACCT_NAME]);
+
+	if (!IS_ERR(matching)) {
+		if (nlh->nlmsg_flags & NLM_F_EXCL)
+			return -EEXIST;
+		if (nlh->nlmsg_flags & NLM_F_REPLACE)
+			return 0;
+		else
+			return -EBUSY;
+	}
+
+	if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+		return -ENOENT;
+
+	acct = kzalloc(sizeof(*acct) + size, GFP_KERNEL);
+	if (!acct)
+		return -ENOMEM;
+
+	name = nla[NFTA_ACCT_NAME];
+	nla_strlcpy(acct->name, name, nla_len(name));
+
+	if (nla[NFTA_ACCT_BYTES]) {
+		atomic64_set(&acct->bytes,
+			     be64_to_cpu(nla_get_be64(nla[NFTA_ACCT_BYTES])));
+	}
+	if (nla[NFTA_ACCT_PACKETS]) {
+		atomic64_set(&acct->pkts,
+			     be64_to_cpu(nla_get_be64(nla[NFTA_ACCT_PACKETS])));
+	}
+
+	err = nft_trans_acct_add(&ctx, NFT_MSG_NEWACCT, acct);
+	if (err < 0)
+		goto err;
+
+	list_add_tail_rcu(&acct->list, &ctx.table->accts);
+	ctx.table->use++;
+
+	return 0;
+err:
+	kfree(acct);
+	return err;
+}
+
+static int nf_tables_fill_acct(struct sk_buff *skb, const struct nft_ctx *ctx,
+			       const struct nft_acct *acct,
+			       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 (nla_put_string(skb, NFTA_ACCT_TABLE, ctx->table->name))
+		goto nla_put_failure;
+	if (nla_put_string(skb, NFTA_ACCT_NAME, acct->name))
+		goto nla_put_failure;
+
+	if (type == NFT_MSG_GETACCT_ZERO) {
+		pkts = atomic64_xchg(&((struct nft_acct *)acct)->pkts, 0);
+		bytes = atomic64_xchg(&((struct nft_acct *)acct)->bytes, 0);
+	} else {
+		pkts = atomic64_read(&acct->pkts);
+		bytes = atomic64_read(&acct->bytes);
+	}
+
+	if (nla_put_be64(skb, NFTA_ACCT_PACKETS, cpu_to_be64(pkts)) ||
+	    nla_put_be64(skb, NFTA_ACCT_BYTES, cpu_to_be64(bytes)) ||
+	    nla_put_be32(skb, NFTA_ACCT_USE, htonl(acct->use)))
+		goto nla_put_failure;
+
+	nlmsg_end(skb, nlh);
+
+	return skb->len;
+
+nla_put_failure:
+	nlmsg_trim(skb, nlh);
+	return -1;
+}
+
+static int nf_tables_acct_notify(const struct nft_ctx *ctx,
+				 const struct nft_acct *acct,
+				 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_acct(skb, ctx, acct, 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_delacct(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_acct *acct;
+	struct nft_ctx ctx;
+	int err;
+
+	if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
+		return -EAFNOSUPPORT;
+	if (!nla[NFTA_ACCT_TABLE])
+		return -EINVAL;
+
+	err = nft_ctx_init_from_acct(&ctx, skb, nlh, nla);
+	if (err < 0)
+		return err;
+
+	acct = nf_tables_acct_lookup(ctx.table, nla[NFTA_ACCT_NAME]);
+	if (IS_ERR(acct))
+		return PTR_ERR(acct);
+
+	if (acct->use > 0)
+		return -EBUSY;
+
+	return nft_delacct(&ctx, acct);
+}
+
+static int nf_tables_dump_acct(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	const struct nft_acct *acct;
+	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_acct;
+	u32 type;
+
+	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(acct, &table->accts, list) {
+				if (idx < s_idx)
+					goto cont;
+
+				ctx_acct = *ctx;
+				ctx_acct.table = table;
+				ctx_acct.afi = afi;
+				type = NFNL_MSG_TYPE(cb->nlh->nlmsg_type);
+				if (nf_tables_fill_acct(skb, &ctx_acct, acct,
+							NFT_MSG_NEWACCT,
+							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_acct_done(struct netlink_callback *cb)
+{
+	kfree(cb->data);
+
+	return 0;
+}
+
+static int nf_tables_getacct(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_acct *acct;
+
+	/* Verify existence before starting dump */
+	err = nft_ctx_init_from_acct(&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_acct,
+			.done = nf_tables_dump_acct_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);
+	}
+
+	acct = nf_tables_acct_lookup(ctx.table, nla[NFTA_ACCT_NAME]);
+	if (IS_ERR(acct))
+		return PTR_ERR(acct);
+
+	skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb2)
+		return -ENOMEM;
+
+	err = nf_tables_fill_acct(skb2, &ctx, acct, NFT_MSG_NEWACCT, 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_acct_put(struct nft_acct *acct)
+{
+	acct->use--;
+	module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL_GPL(nft_acct_put);
+
+int nft_acct_get(struct nft_acct *acct)
+{
+	if (!try_module_get(THIS_MODULE))
+		return -ENOENT;
+
+	acct->use++;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nft_acct_get);
+
 static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
 	[NFT_MSG_NEWTABLE] = {
 		.call_batch	= nf_tables_newtable,
@@ -3465,6 +3893,26 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
 	[NFT_MSG_GETGEN] = {
 		.call		= nf_tables_getgen,
 	},
+	[NFT_MSG_NEWACCT] = {
+		.call_batch	= nf_tables_newacct,
+		.attr_count	= NFTA_ACCT_MAX,
+		.policy		= nft_acct_policy,
+	},
+	[NFT_MSG_GETACCT] = {
+		.call		= nf_tables_getacct,
+		.attr_count	= NFTA_ACCT_MAX,
+		.policy		= nft_acct_policy,
+	},
+	[NFT_MSG_GETACCT_ZERO] = {
+		.call		= nf_tables_getacct,
+		.attr_count	= NFTA_ACCT_MAX,
+		.policy		= nft_acct_policy,
+	},
+	[NFT_MSG_DELACCT] = {
+		.call_batch	= nf_tables_delacct,
+		.attr_count	= NFTA_ACCT_MAX,
+		.policy		= nft_acct_policy,
+	},
 };
 
 static void nft_chain_commit_update(struct nft_trans *trans)
@@ -3503,6 +3951,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_DELACCT:
+		kfree(nft_trans_acct(trans));
+		break;
 	}
 	kfree(trans);
 }
@@ -3608,6 +4059,17 @@ static int nf_tables_commit(struct sk_buff *skb)
 			}
 			nft_trans_destroy(trans);
 			break;
+		case NFT_MSG_NEWACCT:
+			nf_tables_acct_notify(&trans->ctx,
+					      nft_trans_acct(trans),
+					      NFT_MSG_NEWACCT, GFP_KERNEL);
+			nft_trans_destroy(trans);
+			break;
+		case NFT_MSG_DELACCT:
+			nf_tables_acct_notify(&trans->ctx,
+					      nft_trans_acct(trans),
+					      NFT_MSG_DELACCT, GFP_KERNEL);
+			break;
 		}
 	}
 
@@ -3638,6 +4100,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_NEWACCT:
+		kfree(nft_trans_acct(trans));
+		break;
 	}
 	kfree(trans);
 }
@@ -3716,6 +4181,16 @@ static int nf_tables_abort(struct sk_buff *skb)
 			nft_trans_elem_set(trans)->nelems++;
 			nft_trans_destroy(trans);
 			break;
+		case NFT_MSG_NEWACCT:
+			trans->ctx.table->use--;
+			list_del_rcu(&nft_trans_acct(trans)->list);
+			break;
+		case NFT_MSG_DELACCT:
+			trans->ctx.table->use++;
+			list_add_tail_rcu(&nft_trans_acct(trans)->list,
+					  &trans->ctx.table->accts);
+			nft_trans_destroy(trans);
+			break;
 		}
 	}
 
diff --git a/net/netfilter/nft_acct.c b/net/netfilter/nft_acct.c
new file mode 100644
index 0000000..06b443a
--- /dev/null
+++ b/net/netfilter/nft_acct.c
@@ -0,0 +1,109 @@
+/*
+ * (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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/seqlock.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_expr_acct {
+	struct nft_acct		*acct;
+};
+
+static void nft_acct_eval(const struct nft_expr *expr,
+			  struct nft_data data[NFT_REG_MAX + 1],
+			  const struct nft_pktinfo *pkt)
+{
+	struct nft_expr_acct *priv = nft_expr_priv(expr);
+
+	atomic64_inc(&priv->acct->pkts);
+	atomic64_add(pkt->skb->len, &priv->acct->bytes);
+}
+
+static int nft_acct_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+	struct nft_expr_acct *priv = nft_expr_priv(expr);
+
+	if (nla_put_string(skb, NFTA_ACCT_EXPR_NAME, priv->acct->name))
+		goto nla_put_failure;
+
+	return 0;
+
+nla_put_failure:
+	return -1;
+}
+
+static const struct nla_policy nft_acct_policy[NFTA_ACCT_EXPR_MAX + 1] = {
+	[NFTA_ACCT_EXPR_NAME]	= { .type = NLA_STRING },
+};
+
+static int nft_acct_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+			 const struct nlattr * const tb[])
+{
+	struct nft_expr_acct *priv = nft_expr_priv(expr);
+
+	if (!tb[NFTA_ACCT_EXPR_NAME])
+		return -EINVAL;
+
+	priv->acct = nft_acct_find_get(ctx, nla_data(tb[NFTA_ACCT_EXPR_NAME]));
+	if (!priv->acct)
+		return -ENOENT;
+
+	nft_acct_get(priv->acct);
+
+	return 0;
+}
+
+static void nft_acct_destroy(const struct nft_ctx *ctx,
+			     const struct nft_expr *expr)
+{
+	struct nft_expr_acct *priv = nft_expr_priv(expr);
+
+	nft_acct_put(priv->acct);
+}
+
+static struct nft_expr_type nft_acct_type;
+static const struct nft_expr_ops nft_acct_ops = {
+	.type		= &nft_acct_type,
+	.size		= NFT_EXPR_SIZE(sizeof(struct nft_acct)),
+	.eval		= nft_acct_eval,
+	.init		= nft_acct_init,
+	.dump		= nft_acct_dump,
+	.destroy	= nft_acct_destroy,
+};
+
+static struct nft_expr_type nft_acct_type __read_mostly = {
+	.name		= "acct",
+	.ops		= &nft_acct_ops,
+	.policy		= nft_acct_policy,
+	.maxattr	= NFTA_ACCT_EXPR_MAX,
+	.owner		= THIS_MODULE,
+};
+
+static int __init nft_acct_module_init(void)
+{
+	return nft_register_expr(&nft_acct_type);
+}
+
+static void __exit nft_acct_module_exit(void)
+{
+	nft_unregister_expr(&nft_acct_type);
+}
+
+module_init(nft_acct_module_init);
+module_exit(nft_acct_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ana Rey Botello <ana@soleta.eu>");
+MODULE_ALIAS_NFT_EXPR("acct");
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [libnftnl] src: Add accounters support
       [not found] ` <cover.1421059891.git.ana@soleta.eu>
@ 2015-01-12 10:55   ` ana
  0 siblings, 0 replies; 20+ messages in thread
From: ana @ 2015-01-12 10:55 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Ana Rey

From: Ana Rey <ana@soleta.eu>

This adds userspace support to acct: support to accounting objects and
acct expresion.

Moreover, this adds some examples to add/delete/get/reset the accounter
object and add rules using an existing acct.

Example of how to use those examples:

* Add a new acct:
 # ./examples/nft-acct-add ip test acct1

 # ./examples/nft-acct-get ip test acct1 default
 table test family ip acct acct1 packet 0 bytes 0

 * Delete an acct:
 # ./examples/nft-acct-del ip test acct1

 * Add a rule using the acct expresion.
 # ./examples/nft-rule-add ip test output

 * Reset the acct:
 # ./examples/nft-acct-reset ip test acct1

The kernel support is added in the commit:
netfilter: acct: add support to accounters in nftables

Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
 examples/Makefile.am                |   23 +-
 examples/nft-acct-add.c             |  136 ++++++++
 examples/nft-acct-del.c             |  133 ++++++++
 examples/nft-acct-get.c             |  135 ++++++++
 examples/nft-acct-reset.c           |  121 +++++++
 examples/nft-rule-acct-add.c        |  220 +++++++++++++
 examples/nft-rule-get.c             |    1 +
 include/buffer.h                    |    1 +
 include/libnftnl/Makefile.am        |    3 +-
 include/libnftnl/acct.h             |   87 +++++
 include/libnftnl/expr.h             |    3 +
 include/linux/netfilter/nf_tables.h |   41 +++
 src/Makefile.am                     |    2 +
 src/acct.c                          |  612 +++++++++++++++++++++++++++++++++++
 src/expr/acct.c                     |  201 ++++++++++++
 src/libnftnl.map                    |   30 ++
 16 files changed, 1747 insertions(+), 2 deletions(-)
 create mode 100644 examples/nft-acct-add.c
 create mode 100644 examples/nft-acct-del.c
 create mode 100644 examples/nft-acct-get.c
 create mode 100644 examples/nft-acct-reset.c
 create mode 100644 examples/nft-rule-acct-add.c
 create mode 100644 include/libnftnl/acct.h
 create mode 100644 src/acct.c
 create mode 100644 src/expr/acct.c

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


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [nft 1/2] src: Add the accounter support
  2015-01-12 10:55 Accounting objects support in nft ana
       [not found] ` <cover.1421059771.git.ana@soleta.eu>
       [not found] ` <cover.1421059891.git.ana@soleta.eu>
@ 2015-01-12 10:55 ` ana
  2015-01-12 10:55 ` [nft 2/2] tests: regression: Accounter support ana
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 20+ messages in thread
From: ana @ 2015-01-12 10:55 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Ana Rey

From: Ana Rey <ana@soleta.eu>

This adds userspace support to accounting objects and expresion.

Example of use in nft:
 # nft add acct ip filter http-traffic
 # nft add acct ip filter https-traffic
 # nft add rule ip filter output tcp dport 80 acct http-traffic
 # nft add rule ip filter output tcp dport 443 acct https-traffic
 # nft delete acct ip filter https-traffic

Generate Some traffic:

 # nft list table ip test

    table ip filter {
            acct http-traffic { pkts 779 bytes 99495}
            acct https-traffic { pkts 189 bytes 37824}

            chain output {
                     type filter hook output priority 0;
                     tcp dport http acct http-traffic
                     tcp dport https acct https-traffic
            }
    }

The kernel support is addedin the commit:
"netfilter: acct: add support to accounters in nftables"

The libnftnl support is add in the commit:
"src: Add accounters support"

Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
 include/linux/netfilter/nf_tables.h |   41 +++++++
 include/mnl.h                       |    8 ++
 include/netlink.h                   |   18 +++
 include/rule.h                      |   46 +++++++
 include/statement.h                 |    9 ++
 src/evaluate.c                      |   14 ++-
 src/mnl.c                           |  117 ++++++++++++++++++
 src/netlink.c                       |  231 +++++++++++++++++++++++++++++++++++
 src/netlink_delinearize.c           |   14 +++
 src/netlink_linearize.c             |   16 +++
 src/parser_bison.y                  |   72 ++++++++++-
 src/rule.c                          |  137 +++++++++++++++++++++
 src/scanner.l                       |    2 +
 src/statement.c                     |   16 +++
 14 files changed, 737 insertions(+), 4 deletions(-)

diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 832bc46..5278e0a 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -53,6 +53,10 @@ enum nft_verdicts {
  * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes)
  * @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes)
  * @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes)
+ * @NFT_MSG_NEWACCT: create a new account (enum nft_acct_attributes)
+ * @NFT_MSG_GETACCT: get a account (enum nft_acct_attributes)
+ * @NFT_MSG_GETACCT_ZERO: get a reset accounter (enum nft_acct_attributes)
+ * @NFT_MSG_DELACCT: delete a account (enum nft_acct_attributes)
  */
 enum nf_tables_msg_types {
 	NFT_MSG_NEWTABLE,
@@ -72,6 +76,10 @@ enum nf_tables_msg_types {
 	NFT_MSG_DELSETELEM,
 	NFT_MSG_NEWGEN,
 	NFT_MSG_GETGEN,
+	NFT_MSG_NEWACCT,
+	NFT_MSG_GETACCT,
+	NFT_MSG_GETACCT_ZERO,
+	NFT_MSG_DELACCT,
 	NFT_MSG_MAX,
 };
 
@@ -867,4 +875,37 @@ enum nft_gen_attributes {
 };
 #define NFTA_GEN_MAX		(__NFTA_GEN_MAX - 1)
 
+/**
+ * enum nft_acct_attributes - nf_tables acct netlink attributes
+ *
+ * @NFTA_ACCT_NAME: name of the accounter (NLA_STRING)
+ * @NFTA_ACCT_TABLE: table name (NLA_STRING)
+ * @NFTA_ACCT_BYTES: number of bytes (NLA_U64)
+ * @NFTA_ACCT_PACKETS: number of packets (NLA_U64)
+ * @NFTA_ACCT_USE: number of rule in this accts (NLA_U32)
+ * @NFTA_ACCT_ID: uniquely identifies a acct in a transaction (NLA_U32)
+ */
+enum nft_acct_attributes {
+	NFTA_ACCT_UNSPEC,
+	NFTA_ACCT_NAME,
+	NFTA_ACCT_TABLE,
+	NFTA_ACCT_BYTES,
+	NFTA_ACCT_PACKETS,
+	NFTA_ACCT_USE,
+	NFTA_ACCT_ID,
+	__NFTA_ACCT_MAX
+};
+#define NFTA_ACCT_MAX		(__NFTA_ACCT_MAX - 1)
+
+enum nft_acct_expr_attr {
+	NFTA_ACCT_EXPR_UNSPEC,
+	NFTA_ACCT_EXPR_NAME,
+	__NFTA_ACCT_EXPR_MAX
+};
+#define NFTA_ACCT_EXPR_MAX        (__NFTA_ACCT_EXPR_MAX - 1)
+
+#ifndef NFTA_ACCT_NAME_MAX
+#define NFTA_ACCT_NAME_MAX	32
+#endif
+
 #endif /* _LINUX_NF_TABLES_H */
diff --git a/include/mnl.h b/include/mnl.h
index a0dfa1b..c01e520 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_acct_batch_add(struct nft_acct *nlc,
+			   unsigned int flags, uint32_t seq);
+int mnl_nft_acct_batch_del(struct nft_acct *nlc,
+			   unsigned int flags, uint32_t seq);
+int mnl_nft_acct_get(struct mnl_socket *nf_sock, struct nft_acct *nlc);
+struct nft_acct_list *mnl_nft_acct_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..7da9cf4 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
+ * @acct:	current acct
  * @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 acct		*acct;
 	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_acct *alloc_nft_acct(const struct handle *h);
 
 struct nft_data_linearize {
 	uint32_t	len;
@@ -84,6 +87,20 @@ extern int netlink_del_rule_batch(struct netlink_ctx *ctx,
 				  const struct handle *h,
 				  const struct location *loc);
 
+extern int netlink_add_acct(struct netlink_ctx *ctx, const struct handle *h,
+			    struct acct *acct);
+extern int netlink_rename_acct(struct netlink_ctx *ctx, const struct handle *h,
+			       const struct location *loc, const char *name);
+extern int netlink_delete_acct(struct netlink_ctx *ctx, const struct handle *h,
+			       const struct location *loc);
+extern int netlink_list_accts(struct netlink_ctx *ctx, const struct handle *h,
+			      const struct location *loc);
+extern int netlink_get_acct(struct netlink_ctx *ctx, const struct handle *h,
+			    const struct location *loc);
+extern int netlink_flush_acct(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 +152,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_acct(struct nft_acct *nla);
 
 extern int netlink_batch_send(struct list_head *err_list);
 
diff --git a/include/rule.h b/include/rule.h
index 491411e..8a9e4fb 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)
+ * @acct:	acct name (accts 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		*acct;
 	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
+ * @accts:	accts 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	accts;
 };
 
 extern struct table *table_alloc(void);
@@ -211,6 +215,40 @@ extern void set_print(const struct set *set);
 extern void set_print_plain(const struct set *s);
 
 /**
+ * struct acct - nftables acct
+ *
+ * @list:	table acct list node
+ * @handle:	acct handle
+ * @location:	location the acct was defined/declared at
+ * @refcnt:	reference count
+ * @flags:	bitmask of acct flags
+ * @bytes:	Total bytes
+ * @packets:	Total packets
+
+ */
+struct acct {
+	struct list_head	list;
+	struct handle		handle;
+	struct location		location;
+	unsigned int		refcnt;
+	uint32_t		flags;
+	uint64_t		bytes;
+	uint64_t		packets;
+};
+
+extern struct acct *acct_alloc(const struct location *loc);
+extern struct acct *acct_get(struct acct *acct);
+extern void acct_free(struct acct *acct);
+extern struct acct *acct_clone(const struct acct *acct);
+extern void acct_add_hash(struct acct *acct, struct table *table);
+extern struct acct *acct_lookup(const struct table *table, const char *name);
+extern struct acct *acct_lookup_global(uint32_t family, const char *table,
+				       const char *name);
+extern void acct_print(const struct acct *acct);
+
+
+
+/**
  * enum cmd_ops - command operations
  *
  * @CMD_INVALID:	invalid
@@ -253,6 +291,8 @@ enum cmd_ops {
  * @CMD_OBJ_EXPR:	expression
  * @CMD_OBJ_MONITOR:	monitor
  * @CMD_OBJ_EXPORT:	export
+ * @CMD_OBJ_ACCT:	acct
+ * @CMD_OBJ_ACCTS:	accts
  */
 enum cmd_obj {
 	CMD_OBJ_INVALID,
@@ -266,6 +306,8 @@ enum cmd_obj {
 	CMD_OBJ_EXPR,
 	CMD_OBJ_MONITOR,
 	CMD_OBJ_EXPORT,
+	CMD_OBJ_ACCT,
+	CMD_OBJ_ACCTS,
 };
 
 struct export {
@@ -282,6 +324,7 @@ enum {
 	CMD_MONITOR_OBJ_RULES,
 	CMD_MONITOR_OBJ_SETS,
 	CMD_MONITOR_OBJ_ELEMS,
+	CMD_MONITOR_OBJ_ACCTS,
 	CMD_MONITOR_OBJ_MAX
 };
 
@@ -320,6 +363,7 @@ struct cmd {
 		void		*data;
 		struct expr	*expr;
 		struct set	*set;
+		struct acct	*acct;
 		struct rule	*rule;
 		struct chain	*chain;
 		struct table	*table;
@@ -345,6 +389,7 @@ extern void cmd_free(struct cmd *cmd);
  * @table:	current table
  * @rule:	current rule
  * @set:	current set
+ * @acct:	current acct
  * @stmt:	current statement
  * @ectx:	expression context
  * @pctx:	payload context
@@ -355,6 +400,7 @@ struct eval_ctx {
 	struct table		*table;
 	struct rule		*rule;
 	struct set		*set;
+	struct acct		*acct;
 	struct stmt		*stmt;
 	struct expr_ctx		ectx;
 	struct proto_ctx	pctx;
diff --git a/include/statement.h b/include/statement.h
index d143121..53c8394 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -10,6 +10,12 @@ extern struct stmt *expr_stmt_alloc(const struct location *loc,
 extern struct stmt *verdict_stmt_alloc(const struct location *loc,
 				       struct expr *expr);
 
+struct acct_stmt {
+	const char		*name;
+};
+
+extern struct stmt *acct_stmt_alloc(const struct location *loc);
+
 struct counter_stmt {
 	uint64_t		packets;
 	uint64_t		bytes;
@@ -110,6 +116,7 @@ extern struct stmt *ct_stmt_alloc(const struct location *loc,
  * @STMT_INVALID:	uninitialised
  * @STMT_EXPRESSION:	expression statement (relational)
  * @STMT_VERDICT:	verdict statement
+ * @STMT_ACCT:		accts
  * @STMT_COUNTER:	counters
  * @STMT_META:		meta statement
  * @STMT_LIMIT:		limit statement
@@ -125,6 +132,7 @@ enum stmt_types {
 	STMT_INVALID,
 	STMT_EXPRESSION,
 	STMT_VERDICT,
+	STMT_ACCT,
 	STMT_COUNTER,
 	STMT_META,
 	STMT_LIMIT,
@@ -174,6 +182,7 @@ struct stmt {
 
 	union {
 		struct expr		*expr;
+		struct acct_stmt	acct;
 		struct counter_stmt	counter;
 		struct meta_stmt	meta;
 		struct log_stmt		log;
diff --git a/src/evaluate.c b/src/evaluate.c
index d24d4cc..75f007a 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1650,6 +1650,7 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 #endif
 
 	switch (stmt->ops->type) {
+	case STMT_ACCT:
 	case STMT_COUNTER:
 	case STMT_LIMIT:
 		return 0;
@@ -1824,6 +1825,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 +1846,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_ACCT:
+		return 0;
 	case CMD_OBJ_CHAIN:
 		if (cmd->data == NULL)
 			return 0;
@@ -1863,6 +1867,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_ACCT:
 	case CMD_OBJ_RULE:
 	case CMD_OBJ_CHAIN:
 	case CMD_OBJ_TABLE:
@@ -1892,13 +1897,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_ACCTS]	= (1 << NFT_MSG_NEWACCT) |
+						  (1 << NFT_MSG_DELACCT),
 	},
 	[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_NEWACCT),
 		[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 +1918,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_DELACCT),
 		[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_ACCTS]	= (1 << NFT_MSG_DELACCT),
 	},
 };
 
diff --git a/src/mnl.c b/src/mnl.c
index f48ead5..fc8ef41 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/acct.h>
 
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nf_tables.h>
@@ -711,6 +712,122 @@ int mnl_nft_table_get(struct mnl_socket *nf_sock, struct nft_table *nlt,
 }
 
 /*
+ * Acct
+ */
+int mnl_nft_acct_batch_add(struct nft_acct *nls, unsigned int flags,
+			  uint32_t seqnum)
+{
+	struct nlmsghdr *nlh;
+
+	nlh = nft_acct_nlmsg_build_hdr(nft_nlmsg_batch_current(),
+			NFT_MSG_NEWACCT,
+			nft_acct_attr_get_u32(nls, NFT_ACCT_ATTR_FAMILY),
+			NLM_F_CREATE | flags, seqnum);
+	nft_acct_nlmsg_build_payload(nlh, nls);
+	nft_batch_continue();
+
+	return 0;
+}
+
+int mnl_nft_acct_batch_del(struct nft_acct *nls, unsigned int flags,
+			  uint32_t seqnum)
+{
+	struct nlmsghdr *nlh;
+
+	nlh = nft_acct_nlmsg_build_hdr(nft_nlmsg_batch_current(),
+			NFT_MSG_DELACCT,
+			nft_acct_attr_get_u32(nls, NFT_ACCT_ATTR_FAMILY),
+			flags, seqnum);
+	nft_acct_nlmsg_build_payload(nlh, nls);
+	nft_batch_continue();
+
+	return 0;
+}
+
+static int acct_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nft_acct_list *nla_list = data;
+	struct nft_acct *acct;
+
+	if (check_genid(nlh) < 0)
+		return MNL_CB_ERROR;
+
+	acct = nft_acct_alloc();
+	if (acct == NULL)
+		memory_allocation_error();
+
+	if (nft_acct_nlmsg_parse(nlh, acct) < 0)
+		goto err_free;
+	nft_acct_list_add_tail(acct, nla_list);
+
+	return MNL_CB_OK;
+
+err_free:
+	nft_acct_free(acct);
+	return MNL_CB_OK;
+}
+
+static int acct_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nft_acct *a = data;
+
+	nft_acct_nlmsg_parse(nlh, a);
+
+	return MNL_CB_OK;
+}
+
+int mnl_nft_acct_get(struct mnl_socket *nf_sock, struct nft_acct *acct)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_acct_nlmsg_build_hdr(buf, NFT_MSG_GETACCT,
+			nft_acct_attr_get_u32(acct, NFT_ACCT_ATTR_FAMILY),
+			NLM_F_ACK, seq);
+	nft_acct_nlmsg_build_payload(nlh, acct);
+
+	return nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, acct_get_cb, acct);
+}
+
+struct nft_acct_list *mnl_nft_acct_dump(struct mnl_socket *nf_sock, int family,
+					const char *table)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	struct nft_acct *acct;
+	struct nft_acct_list *nla_list;
+	int ret;
+
+	acct = nft_acct_alloc();
+	if (acct == NULL)
+		memory_allocation_error();
+
+	nlh = nft_acct_nlmsg_build_hdr(buf, NFT_MSG_GETACCT, family,
+				       NLM_F_DUMP|NLM_F_ACK, seq);
+	if (table != NULL)
+		nft_acct_attr_set_str(acct, NFT_ACCT_ATTR_TABLE, table);
+	nft_acct_nlmsg_build_payload(nlh, acct);
+	nft_acct_free(acct);
+
+	nla_list = nft_acct_list_alloc();
+	if (nla_list == NULL)
+		memory_allocation_error();
+
+	ret = nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, acct_cb, nla_list);
+
+	if (ret < 0)
+		goto err;
+
+	return nla_list;
+err:
+	printf("Error en mnl_nft_acct_dump\n");
+	nft_acct_list_free(nla_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..33184c0 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/acct.h>
 #include <libnftnl/common.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nf_tables.h>
@@ -1434,6 +1435,174 @@ out:
 	return err;
 }
 
+static struct acct *netlink_delinearize_acct(struct netlink_ctx *ctx,
+					     struct nft_acct *nla)
+{
+	struct acct *acct;
+
+	acct = acct_alloc(&netlink_location);
+
+	if (acct == NULL)
+		return NULL;
+
+	acct->handle.family	=
+		 nft_acct_attr_get_u32(nla, NFT_ACCT_ATTR_FAMILY);
+	acct->handle.acct	=
+		 xstrdup(nft_acct_attr_get_str(nla, NFT_ACCT_ATTR_NAME));
+	acct->handle.table	=
+		 xstrdup(nft_acct_attr_get_str(nla, NFT_ACCT_ATTR_TABLE));
+	acct->flags	= nft_acct_attr_get_u32(nla, NFT_ACCT_ATTR_FLAGS);
+	acct->packets	= nft_acct_attr_get_u64(nla, NFT_ACCT_ATTR_PKTS);
+	acct->bytes	= nft_acct_attr_get_u64(nla, NFT_ACCT_ATTR_BYTES);
+
+	return acct;
+}
+
+static int list_acct_cb(struct nft_acct *nla, void *arg)
+{
+	struct netlink_ctx *ctx = arg;
+	struct acct *acct;
+
+	netlink_dump_acct(nla);
+	acct = netlink_delinearize_acct(ctx, nla);
+
+	if (acct == NULL)
+		return -1;
+
+	list_add_tail(&acct->list, &ctx->list);
+
+	return 0;
+}
+
+int netlink_list_accts(struct netlink_ctx *ctx, const struct handle *h,
+		       const struct location *loc)
+{
+	struct nft_acct_list *acct_cache;
+	int err;
+
+	acct_cache = mnl_nft_acct_dump(nf_sock, h->family, h->table);
+	if (acct_cache == NULL) {
+		if (errno == EINTR)
+			return -1;
+
+		return netlink_io_error(ctx, loc,
+					"Could not receive accts from kernel: %s",
+					strerror(errno));
+	}
+
+	err = nft_acct_list_foreach(acct_cache, list_acct_cb, ctx);
+
+	nft_acct_list_free(acct_cache);
+
+	return err;
+}
+
+int netlink_get_acct(struct netlink_ctx *ctx, const struct handle *h,
+		     const struct location *loc)
+{
+	struct nft_acct *nla;
+	struct acct *acct;
+	int err;
+
+	nla = alloc_nft_acct(h);
+	netlink_dump_acct(nla);
+	err = mnl_nft_acct_get(nf_sock, nla);
+	if (err < 0) {
+		nft_acct_free(nla);
+		return netlink_io_error(ctx, loc,
+					"Could not receive acct from kernel: %s",
+					strerror(errno));
+	}
+
+	acct = netlink_delinearize_acct(ctx, nla);
+	if (acct == NULL) {
+		nft_acct_free(nla);
+		return -1;
+	}
+
+	list_add_tail(&acct->list, &ctx->list);
+
+	return err;
+}
+
+void netlink_dump_acct(struct nft_acct *nla)
+{
+#ifdef DEBUG
+	char buf[4096];
+
+	if (!(debug_level & DEBUG_NETLINK))
+		return;
+
+	nft_acct_snprintf(buf, sizeof(buf), nla, 0, 0);
+	fprintf(stdout, "%s\n", buf);
+#endif
+}
+
+struct nft_acct *alloc_nft_acct(const struct handle *h)
+{
+	struct nft_acct *nla;
+
+	nla = nft_acct_alloc();
+	if (nla == NULL)
+		memory_allocation_error();
+
+	nft_acct_attr_set_u32(nla, NFT_ACCT_ATTR_FAMILY, h->family);
+	nft_acct_attr_set_str(nla, NFT_ACCT_ATTR_TABLE, h->table);
+	if (h->acct != NULL)
+		nft_acct_attr_set_str(nla, NFT_ACCT_ATTR_NAME, h->acct);
+
+	return nla;
+}
+
+static int netlink_add_acct_batch(struct netlink_ctx *ctx,
+				  const struct handle *h, struct acct *acct)
+{
+	struct nft_acct *nla;
+	int err;
+
+	nla = alloc_nft_acct(h);
+
+	netlink_dump_acct(nla);
+
+	err = mnl_nft_acct_batch_add(nla, NLM_F_EXCL, ctx->seqnum);
+	if (err < 0) {
+		netlink_io_error(ctx, &acct->location, "Could not add acct: %s",
+				 strerror(errno));
+	}
+	nft_acct_free(nla);
+
+	return err;
+}
+
+int netlink_add_acct(struct netlink_ctx *ctx, const struct handle *h,
+		     struct acct *acct)
+{
+	return netlink_add_acct_batch(ctx, h, acct);
+}
+
+static int netlink_del_acct_batch(struct netlink_ctx *ctx,
+				  const struct handle *h,
+				  const struct location *loc)
+{
+	struct nft_acct *nla;
+	int err;
+
+	nla = alloc_nft_acct(h);
+	err = mnl_nft_acct_batch_del(nla, 0, ctx->seqnum);
+	nft_acct_free(nla);
+
+	if (err < 0)
+		netlink_io_error(ctx, loc, "Could not delete acct: %s",
+				 strerror(errno));
+	return err;
+}
+
+int netlink_delete_acct(struct netlink_ctx *ctx, const struct handle *h,
+			const struct location *loc)
+{
+	return netlink_del_acct_batch(ctx, h, loc);
+}
+
 int netlink_batch_send(struct list_head *err_list)
 {
 	return mnl_batch_talk(nf_sock, err_list);
@@ -1515,6 +1684,19 @@ static struct nft_set *netlink_set_alloc(const struct nlmsghdr *nlh)
 	return nls;
 }
 
+static struct nft_acct *netlink_acct_alloc(const struct nlmsghdr *nlh)
+{
+	struct nft_acct *nla = nft_acct_alloc();
+
+	if (nla == NULL)
+		memory_allocation_error();
+
+	if (nft_acct_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 +1731,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_NEWACCT:
 		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_DELACCT:
 		return NFT_OF_EVENT_DEL;
 	}
 
@@ -1806,6 +1990,49 @@ out:
 	return MNL_CB_OK;
 }
 
+static int netlink_events_acct_cb(const struct nlmsghdr *nlh, int type,
+				  struct netlink_mon_handler *monh)
+{
+	struct acct *acct;
+	uint32_t family;
+	struct nft_acct *nla = netlink_acct_alloc(nlh);
+
+	switch (monh->format) {
+	case NFT_OUTPUT_DEFAULT:
+		switch (type) {
+		case NFT_MSG_NEWACCT:
+			printf("add ");
+			acct = netlink_delinearize_acct(monh->ctx, nla);
+			if (acct == NULL)
+				return MNL_CB_ERROR;
+			acct_print(acct);
+			acct_free(acct);
+			printf("\n");
+			break;
+		case NFT_MSG_DELACCT:
+			family = nft_acct_attr_get_u32(nla,
+						       NFT_ACCT_ATTR_FAMILY);
+			printf("delete acct %s %s %s\n",
+			       family2str(family),
+			       nft_acct_attr_get_str(nla, NFT_ACCT_ATTR_TABLE),
+			       nft_acct_attr_get_str(nla, NFT_ACCT_ATTR_NAME));
+			break;
+		}
+		break;
+	case NFT_OUTPUT_XML:
+	case NFT_OUTPUT_JSON:
+		nft_acct_fprintf(stdout, nla, monh->format,
+				 netlink_msg2nftnl_of(type));
+		fprintf(stdout, "\n");
+		break;
+	}
+
+	nft_acct_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 +2265,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_NEWACCT:
+	case NFT_MSG_DELACCT:		/* nft {add|delete} acct */
+		ret = netlink_events_acct_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..c6d6b82 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -445,6 +445,19 @@ static void netlink_parse_ct(struct netlink_parse_ctx *ctx,
 		netlink_parse_ct_stmt(ctx, loc, nle);
 }
 
+static void netlink_parse_acct(struct netlink_parse_ctx *ctx,
+			       const struct location *loc,
+			       const struct nft_rule_expr *nle)
+{
+	struct stmt *stmt;
+
+	stmt = acct_stmt_alloc(loc);
+	stmt->acct.name =
+		nft_rule_expr_get_str(nle, NFT_EXPR_ACCT_NAME);
+
+	list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
 static void netlink_parse_counter(struct netlink_parse_ctx *ctx,
 				  const struct location *loc,
 				  const struct nft_rule_expr *nle)
@@ -709,6 +722,7 @@ static const struct {
 	{ .name = "exthdr",	.parse = netlink_parse_exthdr },
 	{ .name = "meta",	.parse = netlink_parse_meta },
 	{ .name = "ct",		.parse = netlink_parse_ct },
+	{ .name = "acct",	.parse = netlink_parse_acct },
 	{ .name = "counter",	.parse = netlink_parse_counter },
 	{ .name = "log",	.parse = netlink_parse_log },
 	{ .name = "limit",	.parse = netlink_parse_limit },
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 9bef67b..d18e780 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -539,6 +539,20 @@ static void netlink_gen_verdict_stmt(struct netlink_linearize_ctx *ctx,
 	return netlink_gen_expr(ctx, stmt->expr, NFT_REG_VERDICT);
 }
 
+static void netlink_gen_acct_stmt(struct netlink_linearize_ctx *ctx,
+				 const struct stmt *stmt)
+{
+	struct nft_rule_expr *nle;
+
+	nle = alloc_nft_expr("acct");
+	if (stmt->acct.name) {
+		nft_rule_expr_set_str(nle, NFT_EXPR_ACCT_NAME,
+				      stmt->acct.name);
+	}
+
+	nft_rule_add_expr(ctx->nlr, nle);
+}
+
 static void netlink_gen_counter_stmt(struct netlink_linearize_ctx *ctx,
 				     const struct stmt *stmt)
 {
@@ -806,6 +820,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
 		return netlink_gen_expr(ctx, stmt->expr, NFT_REG_VERDICT);
 	case STMT_VERDICT:
 		return netlink_gen_verdict_stmt(ctx, stmt);
+	case STMT_ACCT:
+		return netlink_gen_acct_stmt(ctx, stmt);
 	case STMT_COUNTER:
 		return netlink_gen_counter_stmt(ctx, stmt);
 	case STMT_META:
diff --git a/src/parser_bison.y b/src/parser_bison.y
index fd2407c..415264d 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 acct		*acct;
 }
 
 %token TOKEN_EOF 0		"end of file"
@@ -177,6 +178,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token MAP			"map"
 %token HANDLE			"handle"
 %token RULESET			"ruleset"
+%token ACCT			"acct"
 
 %token INET			"inet"
 
@@ -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 acct_spec acct_identifier
+%destructor { handle_free(&$$); } table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec acct_spec acct_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,10 +430,15 @@ 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 <acct>			acct_block_alloc
+%destructor { acct_free($$); }	acct_block_alloc
+
 %type <list>			stmt_list
 %destructor { stmt_list_free($$); xfree($$); } stmt_list
 %type <stmt>			stmt match_stmt verdict_stmt
 %destructor { stmt_free($$); }	stmt match_stmt verdict_stmt
+%type <stmt>			acct_stmt acct_stmt_alloc
+%destructor { stmt_free($$); }	acct_stmt acct_stmt_alloc
 %type <stmt>			counter_stmt counter_stmt_alloc
 %destructor { stmt_free($$); }	counter_stmt counter_stmt_alloc
 %type <stmt>			ct_stmt
@@ -680,6 +687,10 @@ add_cmd			:	TABLE		table_spec
 				handle_merge(&$3->handle, &$2);
 				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, &@$, $5);
 			}
+			|	ACCT		acct_spec
+			{
+				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_ACCT, &$2, &@$, NULL);
+			}
 			|	MAP		set_spec	map_block_alloc
 						'{'	map_block	'}'
 			{
@@ -740,6 +751,10 @@ delete_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
 			}
+			|	ACCT		acct_spec
+			{
+				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_ACCT, &$2, &@$, NULL);
+			}
 			|	MAP		set_spec
 			{
 				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
@@ -770,6 +785,10 @@ list_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, &@$, NULL);
 			}
+			|	ACCT		acct_spec
+			{
+				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_ACCT, &$2, &@$, NULL);
+			}
 			|	RULESET		ruleset_spec
 			{
 				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);
@@ -788,6 +807,10 @@ flush_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_SET, &$2, &@$, NULL);
 			}
+			|	ACCT		acct_spec
+			{
+				$$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_ACCT, &$2, &@$, NULL);
+			}
 			|	RULESET		ruleset_spec
 			{
 				$$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_RULESET, &$2, &@$, NULL);
@@ -877,6 +900,16 @@ table_block		:	/* empty */	{ $$ = $<table>-1; }
 				list_add_tail(&$4->list, &$1->sets);
 				$$ = $1;
 			}
+			|	table_block	ACCT		acct_identifier
+					acct_block_alloc
+					stmt_seperator
+			{
+				$4->location = @3;
+				handle_merge(&$4->handle, &$3);
+				handle_free(&$3);
+				list_add_tail(&$4->list, &$1->accts);
+				$$ = $1;
+			}
 			|	table_block	MAP		set_identifier
 					map_block_alloc		'{'	map_block	'}'
 					stmt_seperator
@@ -907,6 +940,12 @@ chain_block		:	/* empty */	{ $$ = $<chain>-1; }
 			}
 			;
 
+acct_block_alloc	:	/* empty */
+			{
+				$$ = acct_alloc(NULL);
+			}
+			;
+
 set_block_alloc		:	/* empty */
 			{
 				$$ = set_alloc(NULL);
@@ -1112,6 +1151,13 @@ set_spec		:	table_spec	identifier
 			}
 			;
 
+acct_spec		:	table_spec	identifier
+			{
+				$$		= $1;
+				$$.acct	= $2;
+			}
+			;
+
 set_identifier		:	identifier
 			{
 				memset(&$$, 0, sizeof($$));
@@ -1119,6 +1165,13 @@ set_identifier		:	identifier
 			}
 			;
 
+acct_identifier		:	identifier
+			{
+				memset(&$$, 0, sizeof($$));
+				$$.acct		= $1;
+			}
+			;
+
 handle_spec		:	/* empty */
 			{
 				$$ = 0;
@@ -1198,6 +1251,7 @@ stmt_list		:	stmt
 stmt			:	verdict_stmt
 			|	match_stmt
 			|	counter_stmt
+			|	acct_stmt
 			|	meta_stmt
 			|	log_stmt
 			|	limit_stmt
@@ -1258,6 +1312,20 @@ verdict_map_list_member_expr:	opt_newline	map_lhs_expr	COLON	verdict_expr	opt_ne
 			}
 			;
 
+acct_stmt		:	acct_stmt_alloc	acct_arg
+
+acct_stmt_alloc		:	ACCT
+			{
+				$$ = acct_stmt_alloc(&@$);
+			}
+			;
+
+acct_arg		:	STRING
+			{
+				$<stmt>0->acct.name = $1;
+			}
+			;
+
 
 counter_stmt		:	counter_stmt_alloc
 			|	counter_stmt_alloc	counter_args
diff --git a/src/rule.c b/src/rule.c
index feafe26..b2981d4 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->acct);
 	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->acct == NULL && src->acct != NULL)
+		dst->acct = xstrdup(src->acct);
 	if (dst->handle == 0)
 		dst->handle = src->handle;
 	if (dst->position == 0)
@@ -212,6 +215,69 @@ void set_print_plain(const struct set *s)
 	do_set_print(s, &opts);
 }
 
+struct acct *acct_alloc(const struct location *loc)
+{
+	struct acct *acct;
+
+	acct = xzalloc(sizeof(*acct));
+	acct->refcnt = 1;
+
+	if (loc != NULL)
+		acct->location = *loc;
+
+	return acct;
+}
+
+struct acct *acct_get(struct acct *acct)
+{
+	acct->refcnt++;
+
+	return acct;
+}
+
+void acct_free(struct acct *acct)
+{
+	if (--acct->refcnt > 0)
+		return;
+	handle_free(&acct->handle);
+	xfree(acct);
+}
+
+struct acct *acct_lookup(const struct table *table, const char *name)
+{
+	struct acct *acct;
+
+	list_for_each_entry(acct, &table->accts, list) {
+		if (!strcmp(acct->handle.acct, name))
+			return acct;
+	}
+
+	return NULL;
+}
+
+struct acct *acct_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 acct_lookup(t, name);
+}
+
+void acct_print(const struct acct *acct)
+{
+	printf("\tacct %s { ", acct->handle.acct);
+	printf("pkts %"PRIu64" bytes %"PRIu64"", acct->packets, acct->bytes);
+	printf("}\n");
+}
+
 struct rule *rule_alloc(const struct location *loc, const struct handle *h)
 {
 	struct rule *rule;
@@ -467,6 +533,7 @@ struct table *table_alloc(void)
 	table = xzalloc(sizeof(*table));
 	init_list_head(&table->chains);
 	init_list_head(&table->sets);
+	init_list_head(&table->accts);
 	init_list_head(&table->scope.symbols);
 	return table;
 }
@@ -504,6 +571,7 @@ struct table *table_lookup(const struct handle *h)
 static void table_print(const struct table *table)
 {
 	struct chain *chain;
+	struct acct *acct;
 	struct set *set;
 	const char *delim = "";
 	const char *family = family2str(table->handle.family);
@@ -516,11 +584,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(acct, &table->accts, list) {
+		acct_print(acct);
+	}
+	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 +680,9 @@ void cmd_free(struct cmd *cmd)
 		case CMD_OBJ_EXPORT:
 			export_free(cmd->export);
 			break;
+		case CMD_OBJ_ACCT:
+			acct_free(cmd->acct);
+			break;
 		default:
 			BUG("invalid command object type %u\n", cmd->obj);
 		}
@@ -625,6 +706,15 @@ static int do_add_chain(struct netlink_ctx *ctx, const struct handle *h,
 	return 0;
 }
 
+static int do_add_acct(struct netlink_ctx *ctx, const struct handle *h,
+		       struct acct *acct)
+{
+	if (netlink_add_acct(ctx, h, acct) < 0)
+		return -1;
+
+	return 0;
+}
+
 static int do_add_setelems(struct netlink_ctx *ctx, const struct handle *h,
 			   const struct expr *expr)
 {
@@ -654,6 +744,7 @@ static int do_add_table(struct netlink_ctx *ctx, const struct handle *h,
 {
 	struct chain *chain;
 	struct set *set;
+	struct acct *acct;
 
 	if (netlink_add_table(ctx, h, loc, table, excl) < 0)
 		return -1;
@@ -663,6 +754,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(acct, &table->accts, list) {
+			handle_merge(&acct->handle, &table->handle);
+			if (do_add_acct(ctx, &acct->handle, acct) < 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 +784,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_ACCT:
+		return do_add_acct(ctx, &cmd->handle, cmd->acct);
 	default:
 		BUG("invalid command object type %u\n", cmd->obj);
 	}
@@ -720,6 +818,8 @@ 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_ACCT:
+		return netlink_delete_acct(ctx, &cmd->handle, &cmd->location);
 	default:
 		BUG("invalid command object type %u\n", cmd->obj);
 	}
@@ -741,6 +841,21 @@ static int do_list_sets(struct netlink_ctx *ctx, const struct location *loc,
 	return 0;
 }
 
+static int do_list_accts(struct netlink_ctx *ctx, const struct location *loc,
+			 struct table *table)
+{
+	struct acct *acct, *nacct;
+
+	if (netlink_list_accts(ctx, &table->handle, loc) < 0)
+		return -1;
+
+	list_for_each_entry_safe(acct, nacct, &ctx->list, list) {
+		list_move_tail(&acct->list, &table->accts);
+	}
+
+	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 +875,7 @@ static void table_cleanup(struct table *table)
 {
 	struct chain *chain, *nchain;
 	struct set *set, *nset;
+	struct acct *acct, *nacct;
 
 	list_for_each_entry_safe(chain, nchain, &table->chains, list) {
 		list_del(&chain->list);
@@ -770,6 +886,10 @@ static void table_cleanup(struct table *table)
 		list_del(&set->list);
 		set_free(set);
 	}
+	list_for_each_entry_safe(acct, nacct, &table->accts, list) {
+		list_del(&acct->list);
+		acct_free(acct);
+	}
 }
 
 static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -780,6 +900,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_accts(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 +957,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 {
 	struct table *table = NULL;
 	struct set *set;
+	struct acct *acct;
 
 	/* No need to allocate the table object when listing all tables */
 	if (cmd->handle.table != NULL) {
@@ -887,6 +1010,20 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 			set_print(set);
 		}
 		return 0;
+	case CMD_OBJ_ACCTS:
+		if (netlink_list_accts(ctx, &cmd->handle, &cmd->location) < 0)
+			goto err;
+		list_for_each_entry(acct, &ctx->list, list) {
+			acct_print(acct);
+		}
+		return 0;
+	case CMD_OBJ_ACCT:
+		if (netlink_get_acct(ctx, &cmd->handle, &cmd->location) < 0)
+			goto err;
+		list_for_each_entry(acct, &ctx->list, list) {
+			acct_print(acct);
+		}
+		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..eb69eb0 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -276,6 +276,8 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "performance"		{ return PERFORMANCE; }
 "memory"		{ return MEMORY; }
 
+"acct"			{ return ACCT; }
+
 "counter"		{ return COUNTER; }
 "packets"		{ return PACKETS; }
 "bytes"			{ return BYTES; }
diff --git a/src/statement.c b/src/statement.c
index d72c6e9..35d44c9 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -120,6 +120,22 @@ struct stmt *counter_stmt_alloc(const struct location *loc)
 	return stmt_alloc(loc, &counter_stmt_ops);
 }
 
+static void acct_stmt_print(const struct stmt *stmt)
+{
+	printf("acct %s", stmt->acct.name);
+}
+
+static const struct stmt_ops acct_stmt_ops = {
+	.type		= STMT_ACCT,
+	.name		= "acct",
+	.print		= acct_stmt_print,
+};
+
+struct stmt *acct_stmt_alloc(const struct location *loc)
+{
+	return stmt_alloc(loc, &acct_stmt_ops);
+}
+
 static const char *syslog_level[LOG_DEBUG + 1] = {
 	[LOG_EMERG]	= "emerg",
 	[LOG_ALERT]	= "alert",
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [nft 2/2] tests: regression: Accounter support
  2015-01-12 10:55 Accounting objects support in nft ana
                   ` (2 preceding siblings ...)
  2015-01-12 10:55 ` [nft 1/2] src: Add the accounter support ana
@ 2015-01-12 10:55 ` ana
  2015-01-12 11:39 ` Accounting objects support in nft Patrick McHardy
  2015-01-12 11:48 ` Arturo Borrero Gonzalez
  5 siblings, 0 replies; 20+ messages in thread
From: ana @ 2015-01-12 10:55 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/acct.c.

Signed-off-by: Ana Rey Botello <ana@soleta.eu>
---
 tests/regression/ip/acct.t   |   17 +++++++
 tests/regression/nft-test.py |  112 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 129 insertions(+)
 create mode 100644 tests/regression/ip/acct.t

diff --git a/tests/regression/ip/acct.t b/tests/regression/ip/acct.t
new file mode 100644
index 0000000..2193da2
--- /dev/null
+++ b/tests/regression/ip/acct.t
@@ -0,0 +1,17 @@
+*ip;test-ip4
+*inet;test-inet
+:input;type filter hook input priority 0
+
+> acct1;ok
+> acct2;ok
+> acct3;ok
+
+ip daddr 192.168.0.1 acct acct1;ok
+ip daddr 192.168.0.1 acct acct1;ok
+ip daddr 192.168.0.2 acct acct1;ok
+
+ip daddr 192.168.0.1 acct acct2;ok
+ip daddr 192.168.0.1 acct acct2;ok
+ip daddr 192.168.0.2 acct acct3;ok
+
+ip daddr 192.168.0.2 acct not-acct;fail
diff --git a/tests/regression/nft-test.py b/tests/regression/nft-test.py
index 7e5b475..4b2b1cb 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_acct = dict()
 signal_received = 0
 
 
@@ -403,6 +404,83 @@ def set_check_element(rule1, rule2):
             ret = 0
     return ret
 
+##
+def acct_add(acct_info, table_list, filename, lineno):
+    '''
+    Adds an acct.
+    '''
+
+    if not table_list:
+        reason = "Missing table to add rule"
+        print_error(reason, filename, lineno)
+        return -1
+
+    for table in table_list:
+        if acct_exist(acct_info[0], table, filename, lineno):
+            reason = "This acct " + acct_info + " exists in " + table[1] + \
+                ". I cannot add it again"
+            print_error(reason, filename, lineno)
+            return -1
+
+        table_info = " " + table[0] + " " + table[1] + " "
+        acct_text = " " + acct_info[0]
+        cmd = "nft add acct" + table_info + acct_text
+        ret = execute_cmd(cmd, filename, lineno)
+
+        if (ret == 0 and acct_info[1].rstrip() == "fail") or \
+           (ret != 0 and acct_info[1].rstrip() == "ok"):
+                reason = cmd + ": " + "I cannot add the acct " + acct_info[0]
+                print_error(reason, filename, lineno)
+                return -1
+
+        if not acct_exist(acct_info[0], table, filename, lineno):
+            reason = "I have just added the acct " + acct_info[0] + \
+                " to the table " + table[1] + " but it does not exist"
+            print_error(reason, filename, lineno)
+            return -1
+
+    return 0
+
+
+def acct_delete(all_acct, table, filename=None, lineno=None):
+    '''
+    Deletes acct and its content.
+    '''
+
+    for acct_name in all_acct.keys():
+        # Check if exists the acct
+        if not acct_exist(acct_name, table, filename, lineno):
+            reason = "The acct " + acct_name + \
+                " does not exist, I cannot delete it"
+            print_error(reason, filename, lineno)
+            return -1
+
+        # We delete the acct.
+        table_info = " " + table[0] + " " + table[1] + " "
+        cmd = "nft delete acct " + table_info + " " + acct_name
+        ret = execute_cmd(cmd, filename, lineno)
+        # Check if the acct still exists after I deleted it.
+        if ret != 0 or acct_exist(acct_name, table, filename, lineno):
+            reason = "Cannot remove the acct " + acct_name
+            print_error(reason, filename, lineno)
+            return -1
+
+    return 0
+
+
+def acct_exist(acct_name, table, filename, lineno):
+    '''
+    Check if the acct exists.
+    '''
+    table_info = " " + table[0] + " " + table[1] + " "
+    cmd = "nft list acct" + table_info + acct_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)
@@ -527,6 +605,8 @@ def cleanup_on_exit():
             ret = chain_delete(chain, table, "", "")
         if all_set:
             ret = set_delete(all_set, table)
+        if all_acct:
+            ret = acct_delete(all_acct, table)
         ret = table_delete(table)
 
 
@@ -619,6 +699,19 @@ def set_element_process(element_line, filename, lineno):
                             table_list, filename, lineno)
 
 
+def acct_process(acct_line, filename, lineno):
+    acct_info = []
+    acct_name = acct_line.split(";")[0]
+    acct_info.append(acct_name)
+    acct_state = acct_line.split(";")[1] # ok or fail
+    acct_info.append(acct_state)
+    ret = acct_add(acct_info, table_list, filename, lineno)
+    if ret == 0:
+        all_acct[acct_name] = set()
+
+    return ret
+
+
 def run_test_file(filename, force_all_family_option, specific_file):
     '''
     Runs a test file
@@ -674,6 +767,16 @@ def run_test_file(filename, force_all_family_option, specific_file):
             passed += 1
             continue
 
+        if line[0] == ">":  # Adds this acct
+            acct_line = line[1:].strip()
+            ret = acct_process(acct_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 +832,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 acct.
+        if all_acct:
+            ret = acct_delete(all_acct, table, filename, lineno)
+            if ret != 0:
+                total_test_passed = False
+                reason = "There is a problem when we delete an acct"
+                print_error(reason, filename, lineno)
+
         # We delete tables.
         ret = table_delete(table, filename, lineno)
 
@@ -749,6 +860,7 @@ def run_test_file(filename, force_all_family_option, specific_file):
     del table_list[:]
     del chain_list[:]
     all_set.clear()
+    all_acct.clear()
 
     return [tests, passed, total_warning, total_error, total_unit_run]
 
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [nf-next] netfilter: acct: add support to accounters in nftables
  2015-01-12 10:55   ` [nf-next] netfilter: acct: add support to accounters in nftables ana
@ 2015-01-12 11:31     ` Pablo Neira Ayuso
  2015-01-12 11:45       ` Patrick McHardy
  2015-01-12 11:42     ` Patrick McHardy
  1 sibling, 1 reply; 20+ messages in thread
From: Pablo Neira Ayuso @ 2015-01-12 11:31 UTC (permalink / raw)
  To: ana; +Cc: netfilter-devel

On Mon, Jan 12, 2015 at 11:55:51AM +0100, ana@soleta.eu wrote:
> From: Ana Rey <ana@soleta.eu>
> 
> This adds accounting objects support to allow us to manipulate the nftables's
> extended accounting intraestructure.

I really think it's worth to provide some examples in the patch
description so people understand better what this is about.

> Signed-off-by: Ana Rey Botello <ana@soleta.eu>
> ---
>  include/net/netfilter/nf_tables.h        |   41 +++
>  include/uapi/linux/netfilter/nf_tables.h |   41 +++
>  net/netfilter/Kconfig                    |    7 +
>  net/netfilter/Makefile                   |    1 +
>  net/netfilter/nf_tables_api.c            |  485 +++++++++++++++++++++++++++++-
>  net/netfilter/nft_acct.c                 |  109 +++++++
>  6 files changed, 679 insertions(+), 5 deletions(-)
>  create mode 100644 net/netfilter/nft_acct.c
> 
> diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
> index 3ae969e..96f5292 100644
> --- a/include/net/netfilter/nf_tables.h
> +++ b/include/net/netfilter/nf_tables.h
> @@ -408,6 +408,17 @@ struct nft_trans {
>  	char				data[0];
>  };
>  
> +

Remove extra line break, not needed.

> +struct nft_trans_acct {
> +	struct nft_acct	*acct;
> +	u32		acct_id;
> +};
> +
> +#define nft_trans_acct(trans)	\
> +	(((struct nft_trans_acct *)trans->data)->acct)
> +#define nft_trans_acct_id(trans)	\
> +	(((struct nft_trans_acct *)trans->data)->acct_id)
> +
>  struct nft_trans_rule {
>  	struct nft_rule			*rule;
>  };
> @@ -570,6 +581,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt,
>   *	@list: used internally
>   *	@chains: chains in the table
>   *	@sets: sets in the table
> + *	@accts: accts in the table
>   *	@hgenerator: handle generator state
>   *	@use: number of chain references to this table
>   *	@flags: table flag (see enum nft_table_flags)
> @@ -579,6 +591,7 @@ struct nft_table {
>  	struct list_head		list;
>  	struct list_head		chains;
>  	struct list_head		sets;
> +	struct list_head		accts;
>  	u64				hgenerator;
>  	u32				use;
>  	u16				flags;
> @@ -637,6 +650,31 @@ 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_acct - nf_tables acct instance
> + *
> + * @list: table acct list node
> + * @name: name of the acct
> + * @pkts:  number of packets
> + * @bytes:  number of bytes
> + * use: number of rule references to this acct
> + */
> +struct nft_acct {
> +	struct list_head	list;
> +	char                    name[NFT_ACCT_MAXNAMELEN];

NFT_ACCT_MAXNAMELEN needs to be limited to 16 bytes, so we can easily
use acct objects from mappings.

> +	atomic64_t		pkts;
> +	atomic64_t		bytes;
> +	u32			use;
> +};
> +
> +struct nft_acct *nf_tables_acct_lookup(const struct nft_table *table,
> +				       const struct nlattr *nla);
> +struct nft_acct *nft_acct_find_get(const struct nft_ctx *ctx,
> +				   const char *acct_name);
> +void nft_acct_update(const struct sk_buff *skb, struct nft_acct *acct);

This is a dead definition.

> +void nft_acct_put(struct nft_acct *acct);
> +int nft_acct_get(struct nft_acct *acct);
> +
>  #define nft_dereference(p)					\
>  	nfnl_dereference(p, NFNL_SUBSYS_NFTABLES)
>  
> @@ -655,4 +693,7 @@ void nft_unregister_expr(struct nft_expr_type *);
>  #define MODULE_ALIAS_NFT_SET() \
>  	MODULE_ALIAS("nft-set")
>  
> +#define MODULE_ALIAS_NFT_ACCT() \
> +	MODULE_ALIAS("nft-acct")
> +
>  #endif /* _NET_NF_TABLES_H */
> diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
> index 832bc46..b5e17a0 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_ACCT_MAXNAMELEN	32
>  #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_NEWACCT: create a new account (enum nft_acct_attributes)
> + * @NFT_MSG_GETACCT: get a account (enum nft_acct_attributes)
> + * @NFT_MSG_GETACCT_ZERO: get a reset accounter (enum nft_acct_attributes)
> + * @NFT_MSG_DELACCT: delete a account (enum nft_acct_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_NEWACCT,
> +	NFT_MSG_GETACCT,
> +	NFT_MSG_GETACCT_ZERO,
> +	NFT_MSG_DELACCT,
>  	NFT_MSG_MAX,
>  };
>  
> @@ -867,4 +876,36 @@ enum nft_gen_attributes {
>  };
>  #define NFTA_GEN_MAX		(__NFTA_GEN_MAX - 1)
>  
> +/**
> + * enum nft_acct_attributes - nf_tables acct netlink attributes
> + *
> + * @NFTA_ACCT_NAME: name of the accounter (NLA_STRING)
> + * @NFTA_ACCT_TABLE: table name (NLA_STRING)
> + * @NFTA_ACCT_BYTES: number of bytes (NLA_U64)
> + * @NFTA_ACCT_PACKETS: number of packets (NLA_U64)
> + * @NFTA_ACCT_USE: number of rules using this account object (NLA_U32)
> + * @NFTA_ACCT_ID: uniquely identifies a acct in a transaction (NLA_U32)
> + */
> +enum nft_acct_attributes {
> +	NFTA_ACCT_UNSPEC,
> +	NFTA_ACCT_NAME,
> +	NFTA_ACCT_TABLE,
> +	NFTA_ACCT_BYTES,
> +	NFTA_ACCT_PACKETS,
> +	NFTA_ACCT_USE,
> +	NFTA_ACCT_ID,
> +	__NFTA_ACCT_MAX
> +};
> +#define NFTA_ACCT_MAX		(__NFTA_ACCT_MAX - 1)
> +
> +enum nft_acct_expr_attr {
> +	NFTA_ACCT_EXPR_UNSPEC,
> +	NFTA_ACCT_EXPR_NAME,
> +	__NFTA_ACCT_EXPR_MAX
> +};
> +#define NFTA_ACCT_EXPR_MAX        (__NFTA_ACCT_EXPR_MAX - 1)
> +
> +#ifndef NFTA_ACCT_NAME_MAX
> +#define NFTA_ACCT_NAME_MAX	32
> +#endif
>  #endif /* _LINUX_NF_TABLES_H */
> diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
> index b02660f..f0eeb89 100644
> --- a/net/netfilter/Kconfig
> +++ b/net/netfilter/Kconfig
> @@ -446,6 +446,13 @@ config NF_TABLES_INET
>  	help
>  	  This option enables support for a mixed IPv4/IPv6 "inet" table.
>  
> +config NFT_ACCT
> +	depends on NF_TABLES
> +	tristate "Netfilter nf_tables acct module"
> +	help
> +	  This option adds the "acct" expression that you can use to update
> +	  packet accounting objects.
> +
>  config NFT_EXTHDR
>  	depends on NF_TABLES
>  	tristate "Netfilter nf_tables IPv6 exthdr module"
> diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
> index 89f73a9..fcc483f 100644
> --- a/net/netfilter/Makefile
> +++ b/net/netfilter/Makefile
> @@ -90,6 +90,7 @@ obj-$(CONFIG_NFT_COUNTER)	+= nft_counter.o
>  obj-$(CONFIG_NFT_LOG)		+= nft_log.o
>  obj-$(CONFIG_NFT_MASQ)		+= nft_masq.o
>  obj-$(CONFIG_NFT_REDIR)		+= nft_redir.o
> +obj-$(CONFIG_NFT_ACCT)          += nft_acct.o
                         ^--------^
                       I see spaces there, not indents.

>  
>  # generic X tables 
>  obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
> diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
> index 3b3ddb4..faf970a 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;
>  	}
> +

This change has nothing to do with this, please remove it.

>  	return -ENOENT;
>  }
>  
> @@ -325,6 +326,39 @@ static int nft_delset(struct nft_ctx *ctx, struct nft_set *set)
>  	return err;
>  }
>  
> +static int nft_trans_acct_add(struct nft_ctx *ctx, int msg_type,
> +			      struct nft_acct *acct)
> +{
> +	struct nft_trans *trans;
> +
> +	trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_acct));
> +	if (!trans)
> +		return -ENOMEM;
> +
> +	if (msg_type == NFT_MSG_NEWACCT && ctx->nla[NFTA_ACCT_ID]) {
> +		nft_trans_acct_id(trans) =
> +			ntohl(nla_get_be32(ctx->nla[NFTA_ACCT_ID]));
> +	}
> +	nft_trans_acct(trans) = acct;
> +	list_add_tail(&trans->list, &ctx->net->nft.commit_list);
> +
> +	return 0;
> +}
> +
> +static int nft_delacct(struct nft_ctx *ctx, struct nft_acct *acct)
> +{
> +	int err;
> +
> +	err = nft_trans_acct_add(ctx, NFT_MSG_DELACCT, acct);
> +	if (err < 0)
> +		return err;
> +
> +	list_del_rcu(&acct->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->accts);
>  	table->flags = flags;
>  
>  	nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
> @@ -712,13 +747,18 @@ static int nft_flush_table(struct nft_ctx *ctx)
>  	int err;
>  	struct nft_chain *chain, *nc;
>  	struct nft_set *set, *ns;
> +	struct nft_acct *acct, *na;
>  
> -	list_for_each_entry(chain, &ctx->table->chains, list) {
> +	list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) {

This changes has nothing to do with this patch.

>  		ctx->chain = chain;
>  
>  		err = nft_delrule_by_chain(ctx);
>  		if (err < 0)
>  			goto out;
> +
> +		err = nft_delchain(ctx);
> +		if (err < 0)
> +			goto out;

Same thing here, this chunks has nothing to do with it.

>  	}
>  
>  	list_for_each_entry_safe(set, ns, &ctx->table->sets, list) {
> @@ -731,10 +771,8 @@ static int nft_flush_table(struct nft_ctx *ctx)
>  			goto out;
>  	}
>  
> -	list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) {
> -		ctx->chain = chain;
> -
> -		err = nft_delchain(ctx);
> +	list_for_each_entry_safe(acct, na, &ctx->table->accts, list) {
> +		err = nft_delacct(ctx, acct);

This change doesn't make sense. You're removing chain handling and
replacing it by doing something with acct objects...

>  		if (err < 0)
>  			goto out;
>  	}
> @@ -3386,6 +3424,396 @@ err:
>  	return err;
>  }
>  
> +static const struct nla_policy nft_acct_policy[NFTA_ACCT_MAX + 1] = {
> +	[NFTA_ACCT_NAME]	= { .type = NLA_NUL_STRING,
> +				   .len = NFTA_ACCT_NAME_MAX - 1 },
> +	[NFTA_ACCT_BYTES]	= { .type = NLA_U64 },
> +	[NFTA_ACCT_PACKETS]	= { .type = NLA_U64 },
> +	[NFTA_ACCT_ID]		= { .type = NLA_U32 },
> +};
> +
> +struct nft_acct *nf_tables_acct_lookup(const struct nft_table *table,
> +				       const struct nlattr *nla)
> +{
> +	struct nft_acct *acct;
> +
> +	if (!nla)
> +		return ERR_PTR(-EINVAL);
> +
> +	list_for_each_entry(acct, &table->accts, list) {
> +		if (!nla_strcmp(nla, acct->name))
> +			return acct;
> +	}
> +
> +	return ERR_PTR(-ENOENT);
> +}
> +
> +struct nft_acct *nft_acct_find_get(const struct nft_ctx *ctx,
> +				   const char *acct_name)

You can kill this function a use acct_lookup all the time.

> +{
> +	struct nft_acct *cur, *acct = NULL;
> +	struct nft_table *table = ctx->table;
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(cur, &table->accts, list) {
> +		if (strncmp(cur->name, acct_name, NFTA_ACCT_NAME_MAX) != 0)
> +			continue;
> +
> +		acct = cur;
> +		break;
> +	}
> +	rcu_read_unlock();
> +
> +	return acct;
> +}
> +EXPORT_SYMBOL_GPL(nft_acct_find_get);
> +
> +static int nft_ctx_init_from_acct(struct nft_ctx *ctx,
> +				  const struct sk_buff *skb,

Please, align the parameters.

> +				  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_ACCT_TABLE]) {
> +		if (!afi)
> +			return -EAFNOSUPPORT;
> +
> +		table = nf_tables_table_lookup(afi, nla[NFTA_ACCT_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_newacct(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_acct *acct, *matching;
> +	unsigned int size = 0;

This is always zero, remove it.

> +	int err;
> +
> +	if (!nla[NFTA_ACCT_NAME] || !nla[NFTA_ACCT_TABLE])
> +		return -EINVAL;
> +
> +	err = nft_ctx_init_from_acct(&ctx, skb, nlh, nla);
> +	if (err < 0)
> +		return err;
> +
> +	matching = nf_tables_acct_lookup(ctx.table, nla[NFTA_ACCT_NAME]);
> +
> +	if (!IS_ERR(matching)) {
> +		if (nlh->nlmsg_flags & NLM_F_EXCL)
> +			return -EEXIST;
> +		if (nlh->nlmsg_flags & NLM_F_REPLACE)
> +			return 0;
> +		else
> +			return -EBUSY;
> +	}
> +
> +	if (!(nlh->nlmsg_flags & NLM_F_CREATE))
> +		return -ENOENT;
> +
> +	acct = kzalloc(sizeof(*acct) + size, GFP_KERNEL);
> +	if (!acct)
> +		return -ENOMEM;
> +
> +	name = nla[NFTA_ACCT_NAME];
> +	nla_strlcpy(acct->name, name, nla_len(name));

Use NFT_ACCT_NAMEMAX instead of nla_len(name).

> +
> +	if (nla[NFTA_ACCT_BYTES]) {
> +		atomic64_set(&acct->bytes,
> +			     be64_to_cpu(nla_get_be64(nla[NFTA_ACCT_BYTES])));
> +	}
> +	if (nla[NFTA_ACCT_PACKETS]) {
> +		atomic64_set(&acct->pkts,
> +			     be64_to_cpu(nla_get_be64(nla[NFTA_ACCT_PACKETS])));
> +	}
> +
> +	err = nft_trans_acct_add(&ctx, NFT_MSG_NEWACCT, acct);
> +	if (err < 0)
> +		goto err;
> +
> +	list_add_tail_rcu(&acct->list, &ctx.table->accts);
> +	ctx.table->use++;
> +
> +	return 0;
> +err:
> +	kfree(acct);
> +	return err;
> +}
> +
> +static int nf_tables_fill_acct(struct sk_buff *skb, const struct nft_ctx *ctx,
> +			       const struct nft_acct *acct,
> +			       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 (nla_put_string(skb, NFTA_ACCT_TABLE, ctx->table->name))
> +		goto nla_put_failure;
> +	if (nla_put_string(skb, NFTA_ACCT_NAME, acct->name))
> +		goto nla_put_failure;
> +
> +	if (type == NFT_MSG_GETACCT_ZERO) {
> +		pkts = atomic64_xchg(&((struct nft_acct *)acct)->pkts, 0);
> +		bytes = atomic64_xchg(&((struct nft_acct *)acct)->bytes, 0);
> +	} else {
> +		pkts = atomic64_read(&acct->pkts);
> +		bytes = atomic64_read(&acct->bytes);
> +	}
> +
> +	if (nla_put_be64(skb, NFTA_ACCT_PACKETS, cpu_to_be64(pkts)) ||
> +	    nla_put_be64(skb, NFTA_ACCT_BYTES, cpu_to_be64(bytes)) ||
> +	    nla_put_be32(skb, NFTA_ACCT_USE, htonl(acct->use)))
> +		goto nla_put_failure;
> +
> +	nlmsg_end(skb, nlh);
> +
> +	return skb->len;
> +
> +nla_put_failure:
> +	nlmsg_trim(skb, nlh);
> +	return -1;
> +}
> +
> +static int nf_tables_acct_notify(const struct nft_ctx *ctx,
> +				 const struct nft_acct *acct,
> +				 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_acct(skb, ctx, acct, 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_delacct(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_acct *acct;
> +	struct nft_ctx ctx;
> +	int err;
> +
> +	if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
> +		return -EAFNOSUPPORT;
> +	if (!nla[NFTA_ACCT_TABLE])
> +		return -EINVAL;
> +
> +	err = nft_ctx_init_from_acct(&ctx, skb, nlh, nla);
> +	if (err < 0)
> +		return err;
> +
> +	acct = nf_tables_acct_lookup(ctx.table, nla[NFTA_ACCT_NAME]);
> +	if (IS_ERR(acct))
> +		return PTR_ERR(acct);
> +
> +	if (acct->use > 0)
> +		return -EBUSY;
> +
> +	return nft_delacct(&ctx, acct);
> +}
> +
> +static int nf_tables_dump_acct(struct sk_buff *skb, struct netlink_callback *cb)
> +{
> +	const struct nft_acct *acct;
> +	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_acct;
> +	u32 type;
> +
> +	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(acct, &table->accts, list) {
> +				if (idx < s_idx)
> +					goto cont;
> +
> +				ctx_acct = *ctx;
> +				ctx_acct.table = table;
> +				ctx_acct.afi = afi;
> +				type = NFNL_MSG_TYPE(cb->nlh->nlmsg_type);
> +				if (nf_tables_fill_acct(skb, &ctx_acct, acct,
> +							NFT_MSG_NEWACCT,
> +							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_acct_done(struct netlink_callback *cb)
> +{
> +	kfree(cb->data);
> +
> +	return 0;
> +}
> +
> +static int nf_tables_getacct(struct sock *nlsk, struct sk_buff *skb,
> +			     const struct nlmsghdr *nlh,
> +			     const struct nlattr * const nla[])

fix alignment:

static int foo(...
               ...
               ...)
{
        ...
}

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Accounting objects support in nft
  2015-01-12 10:55 Accounting objects support in nft ana
                   ` (3 preceding siblings ...)
  2015-01-12 10:55 ` [nft 2/2] tests: regression: Accounter support ana
@ 2015-01-12 11:39 ` Patrick McHardy
  2015-01-12 12:19   ` Pablo Neira Ayuso
  2015-01-12 11:48 ` Arturo Borrero Gonzalez
  5 siblings, 1 reply; 20+ messages in thread
From: Patrick McHardy @ 2015-01-12 11:39 UTC (permalink / raw)
  To: ana; +Cc: netfilter-devel

On 12.01, ana@soleta.eu wrote:
> From: Ana Rey Botello <ana@soleta.eu>
> 
> Hi,
> 
> With this patchset, we add accounting objects support to let us
> manipulate extended accounting objects.
> 
> Example of use in nft:
> 
>  # nft add acct ip filter http-traffic
>  # nft add acct ip filter https-traffic
> 
>  # nft add rule ip filter output tcp dport 80 acct http-traffic
>  # nft add rule ip filter output tcp dport 443 acct https-traffic

I'm wondering whether we really should use a new keyword for this
or do something similar to sets where we have named and anonymous
variants.

An alternative would be:

nft add counter ip filter http-traffic
nft filter output tcp dport 80 counter http-traffic

It is after all just a named counter, so it seems clearer to me to
reuse the keyword.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [nf-next] netfilter: acct: add support to accounters in nftables
  2015-01-12 10:55   ` [nf-next] netfilter: acct: add support to accounters in nftables ana
  2015-01-12 11:31     ` Pablo Neira Ayuso
@ 2015-01-12 11:42     ` Patrick McHardy
  1 sibling, 0 replies; 20+ messages in thread
From: Patrick McHardy @ 2015-01-12 11:42 UTC (permalink / raw)
  To: ana; +Cc: netfilter-devel

On 12.01, ana@soleta.eu wrote:
> From: Ana Rey <ana@soleta.eu>
> 
> This adds accounting objects support to allow us to manipulate the nftables's
> extended accounting intraestructure.
> 
> +/**
> + * struct nft_acct - nf_tables acct instance
> + *
> + * @list: table acct list node
> + * @name: name of the acct
> + * @pkts:  number of packets
> + * @bytes:  number of bytes
> + * use: number of rule references to this acct
> + */
> +struct nft_acct {
> +	struct list_head	list;
> +	char                    name[NFT_ACCT_MAXNAMELEN];
> +	atomic64_t		pkts;
> +	atomic64_t		bytes;
> +	u32			use;
> +};

Also I think it would be nice to use the same accounting structure
for regular counters and these counters regarding the accounting
part. I'm not saying we should necessarily use the nft_counter
variant with a seqlock, but whatever reasoning we use to decide
for one variant should fully apply to the other as well.

The nft_acct would then simply be a container with a list_head,
a use counter and an embedded nft counter structure.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [nf-next] netfilter: acct: add support to accounters in nftables
  2015-01-12 11:31     ` Pablo Neira Ayuso
@ 2015-01-12 11:45       ` Patrick McHardy
  2015-01-12 12:27         ` Pablo Neira Ayuso
  0 siblings, 1 reply; 20+ messages in thread
From: Patrick McHardy @ 2015-01-12 11:45 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: ana, netfilter-devel

On 12.01, Pablo Neira Ayuso wrote:
> > +/**
> > + * struct nft_acct - nf_tables acct instance
> > + *
> > + * @list: table acct list node
> > + * @name: name of the acct
> > + * @pkts:  number of packets
> > + * @bytes:  number of bytes
> > + * use: number of rule references to this acct
> > + */
> > +struct nft_acct {
> > +	struct list_head	list;
> > +	char                    name[NFT_ACCT_MAXNAMELEN];
> 
> NFT_ACCT_MAXNAMELEN needs to be limited to 16 bytes, so we can easily
> use acct objects from mappings.

Not saying that we need more than 16 bytes, but we'd hopefully use
references instead of runtime resolved lookups in mappings :)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Accounting objects support in nft
  2015-01-12 10:55 Accounting objects support in nft ana
                   ` (4 preceding siblings ...)
  2015-01-12 11:39 ` Accounting objects support in nft Patrick McHardy
@ 2015-01-12 11:48 ` Arturo Borrero Gonzalez
  2015-01-12 12:35   ` Pablo Neira Ayuso
  5 siblings, 1 reply; 20+ messages in thread
From: Arturo Borrero Gonzalez @ 2015-01-12 11:48 UTC (permalink / raw)
  To: ana; +Cc: Netfilter Development Mailing list

On 12 January 2015 at 11:55,  <ana@soleta.eu> wrote:
>
> table ip filter {
>         acct http-traffic { pkts 779 bytes 99495}
>         acct https-traffic { pkts 189 bytes 37824}
>
>         chain output {
>              type filter hook output priority 0;
>              tcp dport http acct http-traffic
>              tcp dport https acct https-traffic
>         }
> }
>

Interesting, Ana!

I understand that acct objects are bounded to a table/family.
Why not make them globals? So we could increment same counters from
different families/tables.

best regards.

-- 
Arturo Borrero González
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Accounting objects support in nft
  2015-01-12 11:39 ` Accounting objects support in nft Patrick McHardy
@ 2015-01-12 12:19   ` Pablo Neira Ayuso
  0 siblings, 0 replies; 20+ messages in thread
From: Pablo Neira Ayuso @ 2015-01-12 12:19 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: ana, netfilter-devel

On Mon, Jan 12, 2015 at 11:39:27AM +0000, Patrick McHardy wrote:
> On 12.01, ana@soleta.eu wrote:
> > From: Ana Rey Botello <ana@soleta.eu>
> > 
> > Hi,
> > 
> > With this patchset, we add accounting objects support to let us
> > manipulate extended accounting objects.
> > 
> > Example of use in nft:
> > 
> >  # nft add acct ip filter http-traffic
> >  # nft add acct ip filter https-traffic
> > 
> >  # nft add rule ip filter output tcp dport 80 acct http-traffic
> >  # nft add rule ip filter output tcp dport 443 acct https-traffic
> 
> I'm wondering whether we really should use a new keyword for this
> or do something similar to sets where we have named and anonymous
> variants.
> 
> An alternative would be:
> 
> nft add counter ip filter http-traffic
> nft filter output tcp dport 80 counter http-traffic
> 
> It is after all just a named counter, so it seems clearer to me to
> reuse the keyword.

I like this named counters idea.

So Ana, respin to include the changes in nft_counter instead.

Also rename all _acct to _counter instead.

Thanks.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [nf-next] netfilter: acct: add support to accounters in nftables
  2015-01-12 11:45       ` Patrick McHardy
@ 2015-01-12 12:27         ` Pablo Neira Ayuso
  2015-01-12 12:33           ` Patrick McHardy
  0 siblings, 1 reply; 20+ messages in thread
From: Pablo Neira Ayuso @ 2015-01-12 12:27 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: ana, netfilter-devel

On Mon, Jan 12, 2015 at 11:45:39AM +0000, Patrick McHardy wrote:
> On 12.01, Pablo Neira Ayuso wrote:
> > > +/**
> > > + * struct nft_acct - nf_tables acct instance
> > > + *
> > > + * @list: table acct list node
> > > + * @name: name of the acct
> > > + * @pkts:  number of packets
> > > + * @bytes:  number of bytes
> > > + * use: number of rule references to this acct
> > > + */
> > > +struct nft_acct {
> > > +	struct list_head	list;
> > > +	char                    name[NFT_ACCT_MAXNAMELEN];
> > 
> > NFT_ACCT_MAXNAMELEN needs to be limited to 16 bytes, so we can easily
> > use acct objects from mappings.
> 
> Not saying that we need more than 16 bytes, but we'd hopefully use
> references instead of runtime resolved lookups in mappings :)

I just noticed Ana have to replace NFTA_ACCT_EXPR_NAME by
NFTA_ACCT_EXPR_SREG to support mappings.

What's your idea behind the references? Use some unique id to identify
the object instead of the name?

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [nf-next] netfilter: acct: add support to accounters in nftables
  2015-01-12 12:27         ` Pablo Neira Ayuso
@ 2015-01-12 12:33           ` Patrick McHardy
  2015-01-12 12:59             ` Patrick McHardy
  0 siblings, 1 reply; 20+ messages in thread
From: Patrick McHardy @ 2015-01-12 12:33 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: ana, netfilter-devel

On 12.01, Pablo Neira Ayuso wrote:
> On Mon, Jan 12, 2015 at 11:45:39AM +0000, Patrick McHardy wrote:
> > On 12.01, Pablo Neira Ayuso wrote:
> > > > +/**
> > > > + * struct nft_acct - nf_tables acct instance
> > > > + *
> > > > + * @list: table acct list node
> > > > + * @name: name of the acct
> > > > + * @pkts:  number of packets
> > > > + * @bytes:  number of bytes
> > > > + * use: number of rule references to this acct
> > > > + */
> > > > +struct nft_acct {
> > > > +	struct list_head	list;
> > > > +	char                    name[NFT_ACCT_MAXNAMELEN];
> > > 
> > > NFT_ACCT_MAXNAMELEN needs to be limited to 16 bytes, so we can easily
> > > use acct objects from mappings.
> > 
> > Not saying that we need more than 16 bytes, but we'd hopefully use
> > references instead of runtime resolved lookups in mappings :)
> 
> I just noticed Ana have to replace NFTA_ACCT_EXPR_NAME by
> NFTA_ACCT_EXPR_SREG to support mappings.
> 
> What's your idea behind the references? Use some unique id to identify
> the object instead of the name?

Similar to verdicts we'd include another data type which refers to
accounting objects. Those are resolved when the data is created and
we'd return them from the map lookup. Resolving can use the name,
just as we do for chains.

At runtime the map would either load the reference to a register and
pass it to the counter expression (might need a special register
similar to NFT_REG_VERDICT) or we just add a special type of the
counter expression that invokes the lookup itself.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Accounting objects support in nft
  2015-01-12 11:48 ` Arturo Borrero Gonzalez
@ 2015-01-12 12:35   ` Pablo Neira Ayuso
  2015-01-12 12:37     ` Patrick McHardy
  0 siblings, 1 reply; 20+ messages in thread
From: Pablo Neira Ayuso @ 2015-01-12 12:35 UTC (permalink / raw)
  To: Arturo Borrero Gonzalez; +Cc: ana, Netfilter Development Mailing list, kaber

On Mon, Jan 12, 2015 at 12:48:35PM +0100, Arturo Borrero Gonzalez wrote:
> On 12 January 2015 at 11:55,  <ana@soleta.eu> wrote:
> >
> > table ip filter {
> >         acct http-traffic { pkts 779 bytes 99495}
> >         acct https-traffic { pkts 189 bytes 37824}
> >
> >         chain output {
> >              type filter hook output priority 0;
> >              tcp dport http acct http-traffic
> >              tcp dport https acct https-traffic
> >         }
> > }
> >
>
> Interesting, Ana!
>
> I understand that acct objects are bounded to a table/family.
> Why not make them globals? So we could increment same counters from
> different families/tables.

Indeed. The existing binding between acct and tables is superfluous.
With sets, we need that to check for loops in verdict maps.

So counters can become also top-level identifier as it happens with
tables, ie.

counters {
        http-traffic { pkts 779 bytes 99495}
        acct https-traffic { pkts 189 bytes 37824}
}

table ip filter {
        chain output {
             type filter hook output priority 0;
             tcp dport http counter http-traffic
             tcp dport https counter https-traffic
        }
}

Patrick, any comment on that?

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Accounting objects support in nft
  2015-01-12 12:35   ` Pablo Neira Ayuso
@ 2015-01-12 12:37     ` Patrick McHardy
  2015-01-12 13:38       ` Pablo Neira Ayuso
  2015-01-12 20:43       ` Arturo Borrero Gonzalez
  0 siblings, 2 replies; 20+ messages in thread
From: Patrick McHardy @ 2015-01-12 12:37 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: Arturo Borrero Gonzalez, ana, Netfilter Development Mailing list

On 12.01, Pablo Neira Ayuso wrote:
> On Mon, Jan 12, 2015 at 12:48:35PM +0100, Arturo Borrero Gonzalez wrote:
> > On 12 January 2015 at 11:55,  <ana@soleta.eu> wrote:
> > >
> > > table ip filter {
> > >         acct http-traffic { pkts 779 bytes 99495}
> > >         acct https-traffic { pkts 189 bytes 37824}
> > >
> > >         chain output {
> > >              type filter hook output priority 0;
> > >              tcp dport http acct http-traffic
> > >              tcp dport https acct https-traffic
> > >         }
> > > }
> > >
> >
> > Interesting, Ana!
> >
> > I understand that acct objects are bounded to a table/family.
> > Why not make them globals? So we could increment same counters from
> > different families/tables.
> 
> Indeed. The existing binding between acct and tables is superfluous.
> With sets, we need that to check for loops in verdict maps.
> 
> So counters can become also top-level identifier as it happens with
> tables, ie.
> 
> counters {
>         http-traffic { pkts 779 bytes 99495}
>         acct https-traffic { pkts 189 bytes 37824}
> }
> 
> table ip filter {
>         chain output {
>              type filter hook output priority 0;
>              tcp dport http counter http-traffic
>              tcp dport https counter https-traffic
>         }
> }
> 
> Patrick, any comment on that?

I'm unsure, we don't have any global objects so far, this might open
another can of flushing/ordering etc problems. If it works without
problems, I can see both variants being useful. Given that we only
need a list to store them we might be able to support both by minor
adjustments to the lookup function.

If we do actually want to support both, I'd suggest to start using
just table scope and expand it later.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [nf-next] netfilter: acct: add support to accounters in nftables
  2015-01-12 12:33           ` Patrick McHardy
@ 2015-01-12 12:59             ` Patrick McHardy
  2015-01-13 18:01               ` Patrick McHardy
  0 siblings, 1 reply; 20+ messages in thread
From: Patrick McHardy @ 2015-01-12 12:59 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: ana, netfilter-devel

On 12.01, Patrick McHardy wrote:
> On 12.01, Pablo Neira Ayuso wrote:
> > On Mon, Jan 12, 2015 at 11:45:39AM +0000, Patrick McHardy wrote:
> > > > NFT_ACCT_MAXNAMELEN needs to be limited to 16 bytes, so we can easily
> > > > use acct objects from mappings.
> > > 
> > > Not saying that we need more than 16 bytes, but we'd hopefully use
> > > references instead of runtime resolved lookups in mappings :)
> > 
> > I just noticed Ana have to replace NFTA_ACCT_EXPR_NAME by
> > NFTA_ACCT_EXPR_SREG to support mappings.
> > 
> > What's your idea behind the references? Use some unique id to identify
> > the object instead of the name?
> 
> Similar to verdicts we'd include another data type which refers to
> accounting objects. Those are resolved when the data is created and
> we'd return them from the map lookup. Resolving can use the name,
> just as we do for chains.
> 
> At runtime the map would either load the reference to a register and
> pass it to the counter expression (might need a special register
> similar to NFT_REG_VERDICT) or we just add a special type of the
> counter expression that invokes the lookup itself.

Thinking a bit more about this, I think this is very much similar
to what we've discussed about having some generic state provider
for limit, quota etc.

In all these cases we need to provide some persistent storage to
the expressions to be used instead of their private data. For
hashlimit or hashquota it would probably be based on some generic
state provider that dynamically instantiates this, but its still
very similar to looking up an accounting object in a set and
passing that on.

So strike that "special type of counter expression", I think what
we need is either a special register to pass state between modules
or just some better validation for the existing registers so passing
pointers is safe.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Accounting objects support in nft
  2015-01-12 12:37     ` Patrick McHardy
@ 2015-01-12 13:38       ` Pablo Neira Ayuso
  2015-01-12 20:43       ` Arturo Borrero Gonzalez
  1 sibling, 0 replies; 20+ messages in thread
From: Pablo Neira Ayuso @ 2015-01-12 13:38 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Arturo Borrero Gonzalez, ana, Netfilter Development Mailing list

On Mon, Jan 12, 2015 at 12:37:11PM +0000, Patrick McHardy wrote:
> On 12.01, Pablo Neira Ayuso wrote:
> > On Mon, Jan 12, 2015 at 12:48:35PM +0100, Arturo Borrero Gonzalez wrote:
> > > On 12 January 2015 at 11:55,  <ana@soleta.eu> wrote:
> > > >
> > > > table ip filter {
> > > >         acct http-traffic { pkts 779 bytes 99495}
> > > >         acct https-traffic { pkts 189 bytes 37824}
> > > >
> > > >         chain output {
> > > >              type filter hook output priority 0;
> > > >              tcp dport http acct http-traffic
> > > >              tcp dport https acct https-traffic
> > > >         }
> > > > }
> > > >
> > >
> > > Interesting, Ana!
> > >
> > > I understand that acct objects are bounded to a table/family.
> > > Why not make them globals? So we could increment same counters from
> > > different families/tables.
> > 
> > Indeed. The existing binding between acct and tables is superfluous.
> > With sets, we need that to check for loops in verdict maps.
> > 
> > So counters can become also top-level identifier as it happens with
> > tables, ie.
> > 
> > counters {
> >         http-traffic { pkts 779 bytes 99495}
> >         acct https-traffic { pkts 189 bytes 37824}
> > }
> > 
> > table ip filter {
> >         chain output {
> >              type filter hook output priority 0;
> >              tcp dport http counter http-traffic
> >              tcp dport https counter https-traffic
> >         }
> > }
> > 
> > Patrick, any comment on that?
> 
> I'm unsure, we don't have any global objects so far, this might open
> another can of flushing/ordering etc problems. If it works without
> problems, I can see both variants being useful. Given that we only
> need a list to store them we might be able to support both by minor
> adjustments to the lookup function.
> 
> If we do actually want to support both, I'd suggest to start using
> just table scope and expand it later.

Agreed.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Accounting objects support in nft
  2015-01-12 12:37     ` Patrick McHardy
  2015-01-12 13:38       ` Pablo Neira Ayuso
@ 2015-01-12 20:43       ` Arturo Borrero Gonzalez
  2015-01-13  8:25         ` Ana Rey
  1 sibling, 1 reply; 20+ messages in thread
From: Arturo Borrero Gonzalez @ 2015-01-12 20:43 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Pablo Neira Ayuso, ana, Netfilter Development Mailing list

On 12 January 2015 at 13:37, Patrick McHardy <kaber@trash.net> wrote:
> On 12.01, Pablo Neira Ayuso wrote:
>> On Mon, Jan 12, 2015 at 12:48:35PM +0100, Arturo Borrero Gonzalez wrote:
>> > On 12 January 2015 at 11:55,  <ana@soleta.eu> wrote:
>> > >
>> > > table ip filter {
>> > >         acct http-traffic { pkts 779 bytes 99495}
>> > >         acct https-traffic { pkts 189 bytes 37824}
>> > >
>> > >         chain output {
>> > >              type filter hook output priority 0;
>> > >              tcp dport http acct http-traffic
>> > >              tcp dport https acct https-traffic
>> > >         }
>> > > }
>> > >
>> >
>> > Interesting, Ana!
>> >
>> > I understand that acct objects are bounded to a table/family.
>> > Why not make them globals? So we could increment same counters from
>> > different families/tables.
>>
>> Indeed. The existing binding between acct and tables is superfluous.
>> With sets, we need that to check for loops in verdict maps.
>>
>> So counters can become also top-level identifier as it happens with
>> tables, ie.
>>
>> counters {
>>         http-traffic { pkts 779 bytes 99495}
>>         acct https-traffic { pkts 189 bytes 37824}
>> }
>>
>> table ip filter {
>>         chain output {
>>              type filter hook output priority 0;
>>              tcp dport http counter http-traffic
>>              tcp dport https counter https-traffic
>>         }
>> }
>>
>> Patrick, any comment on that?
>
> I'm unsure, we don't have any global objects so far, this might open
> another can of flushing/ordering etc problems. If it works without
> problems, I can see both variants being useful. Given that we only
> need a list to store them we might be able to support both by minor
> adjustments to the lookup function.
>
> If we do actually want to support both, I'd suggest to start using
> just table scope and expand it later.

Ok :-)

-- 
Arturo Borrero González
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Accounting objects support in nft
  2015-01-12 20:43       ` Arturo Borrero Gonzalez
@ 2015-01-13  8:25         ` Ana Rey
  0 siblings, 0 replies; 20+ messages in thread
From: Ana Rey @ 2015-01-13  8:25 UTC (permalink / raw)
  To: Arturo Borrero Gonzalez, Patrick McHardy
  Cc: Pablo Neira Ayuso, Netfilter Development Mailing list



On 12/01/15 21:43, Arturo Borrero Gonzalez wrote:
> On 12 January 2015 at 13:37, Patrick McHardy <kaber@trash.net> wrote:
>> On 12.01, Pablo Neira Ayuso wrote:
>>> On Mon, Jan 12, 2015 at 12:48:35PM +0100, Arturo Borrero Gonzalez wrote:
>>>> On 12 January 2015 at 11:55,  <ana@soleta.eu> wrote:
>>>>>
>>>>> table ip filter {
>>>>>         acct http-traffic { pkts 779 bytes 99495}
>>>>>         acct https-traffic { pkts 189 bytes 37824}
>>>>>
>>>>>         chain output {
>>>>>              type filter hook output priority 0;
>>>>>              tcp dport http acct http-traffic
>>>>>              tcp dport https acct https-traffic
>>>>>         }
>>>>> }
>>>>>
>>>>
>>>> Interesting, Ana!
>>>>
>>>> I understand that acct objects are bounded to a table/family.
>>>> Why not make them globals? So we could increment same counters from
>>>> different families/tables.
>>>
>>> Indeed. The existing binding between acct and tables is superfluous.
>>> With sets, we need that to check for loops in verdict maps.
>>>
>>> So counters can become also top-level identifier as it happens with
>>> tables, ie.
>>>
>>> counters {
>>>         http-traffic { pkts 779 bytes 99495}
>>>         acct https-traffic { pkts 189 bytes 37824}
>>> }
>>>
>>> table ip filter {
>>>         chain output {
>>>              type filter hook output priority 0;
>>>              tcp dport http counter http-traffic
>>>              tcp dport https counter https-traffic
>>>         }
>>> }
>>>
>>> Patrick, any comment on that?
>>
>> I'm unsure, we don't have any global objects so far, this might open
>> another can of flushing/ordering etc problems. If it works without
>> problems, I can see both variants being useful. Given that we only
>> need a list to store them we might be able to support both by minor
>> adjustments to the lookup function.
>>
>> If we do actually want to support both, I'd suggest to start using
>> just table scope and expand it later.
> 
> Ok :-)

ok.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [nf-next] netfilter: acct: add support to accounters in nftables
  2015-01-12 12:59             ` Patrick McHardy
@ 2015-01-13 18:01               ` Patrick McHardy
  0 siblings, 0 replies; 20+ messages in thread
From: Patrick McHardy @ 2015-01-13 18:01 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: ana, netfilter-devel

On 12.01, Patrick McHardy wrote:
> On 12.01, Patrick McHardy wrote:
> > On 12.01, Pablo Neira Ayuso wrote:
> > > I just noticed Ana have to replace NFTA_ACCT_EXPR_NAME by
> > > NFTA_ACCT_EXPR_SREG to support mappings.
> > > 
> > > What's your idea behind the references? Use some unique id to identify
> > > the object instead of the name?
> > 
> > Similar to verdicts we'd include another data type which refers to
> > accounting objects. Those are resolved when the data is created and
> > we'd return them from the map lookup. Resolving can use the name,
> > just as we do for chains.
> > 
> > At runtime the map would either load the reference to a register and
> > pass it to the counter expression (might need a special register
> > similar to NFT_REG_VERDICT) or we just add a special type of the
> > counter expression that invokes the lookup itself.
> 
> Thinking a bit more about this, I think this is very much similar
> to what we've discussed about having some generic state provider
> for limit, quota etc.
> 
> In all these cases we need to provide some persistent storage to
> the expressions to be used instead of their private data. For
> hashlimit or hashquota it would probably be based on some generic
> state provider that dynamically instantiates this, but its still
> very similar to looking up an accounting object in a set and
> passing that on.
> 
> So strike that "special type of counter expression", I think what
> we need is either a special register to pass state between modules
> or just some better validation for the existing registers so passing
> pointers is safe.

For getting a better feeling of this I did some initial hacking on
the state module for limit/quota etc. What the module does is
basically maintaining a hash with opaque state objects, looking
them up based on a provided key and handing them to another module
to use instead of its private data. If the state doesn't exist
it is instantiated and added to the hash.

The use would be something like this:

nft filter input ... limit flow ip saddr . tcp dport rate 10/s
nft filter forward ... quota flow ether saddr 1gb

The expressions inside the kernel for the first example would look
like this:

ip saddr  => reg0
tcp dport => reg1
state reg0 len 8 => reg_state
limit priv = reg_state

For the state module, there are two different problems in the
interaction with the limit module:

- initializing a new state based on the parameters of the limit
  expression

- passing the state to the limit expression to be used instead of the
  private space

Initialization obviously requires the parameters used in the limit
expression, which are unknown to the generic state expression. So
we need to call into the limit module to perform this initialization
and pass it its configuration data, which is also unknown to the
state expression.

A simple solution to this is to limit the use of the state expression
to the expression following it directly. In that case we can simply
do something like

expr_next->ops->init_state(expr_next, obj)

The second question is how to pass the state to the limit expression.
The possibilities are:

- use registers
- invoke the limit expression directly and make the core skip it
- change the ->eval() functions to not take a pointer to the expression
  but to expr_priv(expr) and replace that pointer

Using registers is probably the worst solution since it requires
complicated validation and also special cased ->eval() functions.
Both other ways should be completely transparent.

Now why I'm describing this is because its very similar to what
the accounting could do using the named counters. Instead of the
state expression we'd have a different expression which passes
the data of the global accounting objects to the counter expression.

So what is missing for both cases is to get the state from sets.
This unfortunately mostly rules out not using registers since
direct ->eval invocation or replacement of argument pointers inside
the lookup expression would be quite an ugly hack.

We might be able to hide this behind some function so we don't
need different ->eval() functions. Not very important for the
counter case, but still desirable.

Any ideas/comments?

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2015-01-13 18:01 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-12 10:55 Accounting objects support in nft ana
     [not found] ` <cover.1421059771.git.ana@soleta.eu>
2015-01-12 10:55   ` [nf-next] netfilter: acct: add support to accounters in nftables ana
2015-01-12 11:31     ` Pablo Neira Ayuso
2015-01-12 11:45       ` Patrick McHardy
2015-01-12 12:27         ` Pablo Neira Ayuso
2015-01-12 12:33           ` Patrick McHardy
2015-01-12 12:59             ` Patrick McHardy
2015-01-13 18:01               ` Patrick McHardy
2015-01-12 11:42     ` Patrick McHardy
     [not found] ` <cover.1421059891.git.ana@soleta.eu>
2015-01-12 10:55   ` [libnftnl] src: Add accounters support ana
2015-01-12 10:55 ` [nft 1/2] src: Add the accounter support ana
2015-01-12 10:55 ` [nft 2/2] tests: regression: Accounter support ana
2015-01-12 11:39 ` Accounting objects support in nft Patrick McHardy
2015-01-12 12:19   ` Pablo Neira Ayuso
2015-01-12 11:48 ` Arturo Borrero Gonzalez
2015-01-12 12:35   ` Pablo Neira Ayuso
2015-01-12 12:37     ` Patrick McHardy
2015-01-12 13:38       ` Pablo Neira Ayuso
2015-01-12 20:43       ` Arturo Borrero Gonzalez
2015-01-13  8:25         ` Ana Rey

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.