Hi Ana, First off, I'd suggest "netfilter: add named counter support" as title since we already have nameless counters. More comments below: On Sat, Jan 31, 2015 at 12:51:41PM +0100, Ana Rey Botello wrote: > diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h > index 9eaaa78..c400f06 100644 > --- a/include/net/netfilter/nf_tables.h > +++ b/include/net/netfilter/nf_tables.h > @@ -408,6 +408,16 @@ struct nft_trans { > char data[0]; > }; > > +struct nft_trans_counter { > + struct nft_named_ctr *counter; > + u32 counter_id; > +}; > + > +#define nft_trans_counter(trans) \ > + (((struct nft_trans_counter *)trans->data)->counter) > +#define nft_trans_counter_id(trans) \ > + (((struct nft_trans_counter *)trans->data)->counter_id) > + > struct nft_trans_rule { > struct nft_rule *rule; > }; > @@ -572,6 +582,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt, > * @list: used internally > * @chains: chains in the table > * @sets: sets in the table > + * @counters: counters in the table > * @hgenerator: handle generator state > * @use: number of chain references to this table > * @flags: table flag (see enum nft_table_flags) > @@ -581,6 +592,7 @@ struct nft_table { > struct list_head list; > struct list_head chains; > struct list_head sets; > + struct list_head counters; > u64 hgenerator; > u32 use; > u16 flags; > @@ -639,6 +651,41 @@ void nft_unregister_chain_type(const struct nf_chain_type *); > int nft_register_expr(struct nft_expr_type *); > void nft_unregister_expr(struct nft_expr_type *); > > +/** > + * struct nft_counter - nf_tables counter_priv instance > + * > + * @pkts: number of packets > + * @bytes: number of bytes > + */ > +struct nft_counter { > + seqlock_t lock; > + u64 bytes; > + u64 packets; > +}; > + > +/** > + * struct nft_named_ctr - nf_tables counter instance > + * > + * @list: table counter list node > + * @name: name of the counter > + * @counter: nft_priv_counter > + * @bytes: number of bytes > + * @use: number of rule references to this counter > + * @flags: counter flags > + */ > +struct nft_named_ctr { > + struct list_head list; > + char name[NFT_CTR_MAXNAMELEN]; > + struct nft_counter counter; > + u32 use:31, > + flags:1; > +}; > + > +struct nft_named_ctr *nft_counter_lookup(const struct nft_ctx *ctx, > + const char *counter_name); > +void nft_counter_put(struct nft_named_ctr *counter); > +int nft_counter_get(struct nft_named_ctr *counter); > + > #define nft_dereference(p) \ > nfnl_dereference(p, NFNL_SUBSYS_NFTABLES) > > diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h > index 832bc46..127a8bd 100644 > --- a/include/uapi/linux/netfilter/nf_tables.h > +++ b/include/uapi/linux/netfilter/nf_tables.h > @@ -2,6 +2,7 @@ > #define _LINUX_NF_TABLES_H > > #define NFT_CHAIN_MAXNAMELEN 32 > +#define NFT_CTR_MAXNAMELEN 16 > #define NFT_USERDATA_MAXLEN 256 > > enum nft_registers { > @@ -53,6 +54,10 @@ enum nft_verdicts { > * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes) > * @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes) > * @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes) > + * @NFT_MSG_NEWCOUNTER: create a new counter (enum nft_named_ctr_attributes) > + * @NFT_MSG_GETCOUNTER: get a counter (enum nft_named_ctr_attributes) > + * @NFT_MSG_GETCOUNTER_ZERO: get a reset counter (enum nft_named_ctr_attributes) > + * @NFT_MSG_DELCOUNTER: delete a counter (enum nft_named_ctr_attributes) > */ > enum nf_tables_msg_types { > NFT_MSG_NEWTABLE, > @@ -72,6 +77,10 @@ enum nf_tables_msg_types { > NFT_MSG_DELSETELEM, > NFT_MSG_NEWGEN, > NFT_MSG_GETGEN, > + NFT_MSG_NEWCOUNTER, > + NFT_MSG_GETCOUNTER, > + NFT_MSG_GETCOUNTER_ZERO, > + NFT_MSG_DELCOUNTER, > NFT_MSG_MAX, > }; > > @@ -695,16 +704,40 @@ enum nft_limit_attributes { > * > * @NFTA_COUNTER_BYTES: number of bytes (NLA_U64) > * @NFTA_COUNTER_PACKETS: number of packets (NLA_U64) > + * @NFTA_COUNTER_NAME: name of the counter (NLA_STRING) > */ > enum nft_counter_attributes { > NFTA_COUNTER_UNSPEC, > NFTA_COUNTER_BYTES, > NFTA_COUNTER_PACKETS, > + NFTA_COUNTER_NAME, > __NFTA_COUNTER_MAX > }; > #define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1) > > /** > + * enum nft_named_ctr_attributes - nf_tables named counter netlink attributes > + * > + * @NFTA_NAMED_CTR_NAME: named counter name (NLA_STRING) > + * @NFTA_NAMED_CTR_TABLE: table name (NLA_STRING) > + * @NFTA_NAMED_CTR_USE: number of references to this named counter (NLA_U32) > + * @NFTA_NAMED_CTR_ID: uniquely identifies a named counter in a transaction (NLA_U32) > + * @NFTA_NAMED_CTR_BYTES: number of bytes (NLA_U64) > + * @NFTA_NAMED_CTR_PACKETS: number of packets (NLA_U64) > + */ > +enum nft_named_ctr_attributes { > + NFTA_NAMED_CTR_UNSPEC, > + NFTA_NAMED_CTR_NAME, > + NFTA_NAMED_CTR_TABLE, > + NFTA_NAMED_CTR_USE, > + NFTA_NAMED_CTR_ID, You can safely remove NFTA_NAMED_CTR_ID and any code that is related. In sets, this makes sense because we have anonymous sets that have no name yet assigned so the ID provides a way to link sets without named to the rule that uses this for first time. Things are different with named counters since they always have to come with a name. > + NFTA_NAMED_CTR_BYTES, > + NFTA_NAMED_CTR_PACKETS, > + __NFTA_NAMED_CTR_MAX > +}; > +#define NFTA_NAMED_CTR_MAX (__NFTA_NAMED_CTR_MAX - 1) > + > +/** > * enum nft_log_attributes - nf_tables log expression netlink attributes > * > * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32) > @@ -867,4 +900,5 @@ enum nft_gen_attributes { > }; > #define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1) > > + > #endif /* _LINUX_NF_TABLES_H */ Remove this new empty line. > diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c > index 7e68694..fde06ac 100644 > --- a/net/netfilter/nf_tables_api.c > +++ b/net/netfilter/nf_tables_api.c > @@ -325,6 +325,43 @@ static int nft_delset(struct nft_ctx *ctx, struct nft_set *set) > return err; > } > > +#define NFT_NAMED_CTR_INACTIVE (1 << 0) > + > +static int nft_trans_counter_add(struct nft_ctx *ctx, int msg_type, > + struct nft_named_ctr *counter) > +{ > + struct nft_trans *trans; > + > + trans = nft_trans_alloc(ctx, msg_type, > + sizeof(struct nft_trans_counter)); > + if (!trans) > + return -ENOMEM; > + > + if (msg_type == NFT_MSG_NEWCOUNTER && ctx->nla[NFTA_NAMED_CTR_ID]) { > + nft_trans_counter_id(trans) = > + ntohl(nla_get_be32(ctx->nla[NFTA_NAMED_CTR_ID])); > + counter->flags |= NFT_NAMED_CTR_INACTIVE; > + } > + nft_trans_counter(trans) = counter; > + list_add_tail(&trans->list, &ctx->net->nft.commit_list); > + > + return 0; > +} > + > +static int nft_delcounter(struct nft_ctx *ctx, struct nft_named_ctr *counter) > +{ > + int err; > + > + err = nft_trans_counter_add(ctx, NFT_MSG_DELCOUNTER, counter); > + if (err < 0) > + return err; > + > + list_del_rcu(&counter->list); > + ctx->table->use--; > + > + return err; > +} > + > /* > * Tables > */ > @@ -694,6 +731,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, > nla_strlcpy(table->name, name, nla_len(name)); > INIT_LIST_HEAD(&table->chains); > INIT_LIST_HEAD(&table->sets); > + INIT_LIST_HEAD(&table->counters); > table->flags = flags; > > nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); > @@ -712,6 +750,7 @@ static int nft_flush_table(struct nft_ctx *ctx) > int err; > struct nft_chain *chain, *nc; > struct nft_set *set, *ns; > + struct nft_named_ctr *counter, *na; > > list_for_each_entry(chain, &ctx->table->chains, list) { > ctx->chain = chain; > @@ -730,7 +769,11 @@ static int nft_flush_table(struct nft_ctx *ctx) > if (err < 0) > goto out; > } > - > + list_for_each_entry_safe(counter, na, &ctx->table->counters, list) { > + err = nft_delcounter(ctx, counter); > + if (err < 0) > + goto out; > + } > list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) { > ctx->chain = chain; > > @@ -3386,6 +3429,404 @@ err: > return err; > } > > +static const struct nla_policy nft_named_ctr_policy[NFTA_NAMED_CTR_MAX + 1] = { > + [NFTA_NAMED_CTR_NAME] = { .type = NLA_NUL_STRING, > + .len = NFT_CTR_MAXNAMELEN - 1 }, > + [NFTA_NAMED_CTR_BYTES] = { .type = NLA_U64 }, > + [NFTA_NAMED_CTR_PACKETS] = { .type = NLA_U64 }, > + [NFTA_NAMED_CTR_ID] = { .type = NLA_U32 }, > +}; > + > +static struct > +nft_named_ctr *nf_tables_counter_lookup(const struct nft_table *table, > + const struct nlattr *nla) > +{ > + struct nft_named_ctr *counter; > + > + if (!nla) > + return ERR_PTR(-EINVAL); > + list_for_each_entry(counter, &table->counters, list) { > + if (!nla_strcmp(nla, counter->name)) > + return counter; > + } > + > + return ERR_PTR(-ENOENT); > +} > + > +struct nft_named_ctr *nft_counter_lookup(const struct nft_ctx *ctx, > + const char *counter_name) > +{ > + struct nft_named_ctr *cur, *counter = NULL; > + struct nft_table *table = ctx->table; > + > + list_for_each_entry(cur, &table->counters, list) { > + if (strncmp(cur->name, counter_name, NFT_CTR_MAXNAMELEN) == 0) > + return cur; > + } > + > + return counter; > +} > +EXPORT_SYMBOL_GPL(nft_counter_lookup); You can remove this nft_counter_lookup(). Instead, you can export nf_tables_counter_lookup() and use it from nft_counter.c, that change should be easy. > +static int nft_ctx_init_from_counter(struct nft_ctx *ctx, > + const struct sk_buff *skb, > + const struct nlmsghdr *nlh, > + const struct nlattr * const nla[]) > +{ > + struct net *net = sock_net(skb->sk); > + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); > + struct nft_af_info *afi = NULL; > + struct nft_table *table = NULL; > + > + if (nfmsg->nfgen_family != NFPROTO_UNSPEC) { > + afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); > + if (IS_ERR(afi)) > + return PTR_ERR(afi); > + } > + > + if (nla[NFTA_NAMED_CTR_TABLE]) { > + if (!afi) > + return -EAFNOSUPPORT; > + > + table = nf_tables_table_lookup(afi, nla[NFTA_NAMED_CTR_TABLE]); > + if (IS_ERR(table)) > + return PTR_ERR(table); > + if (table->flags & NFT_TABLE_INACTIVE) > + return -ENOENT; > + } > + > + nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); > + > + return 0; > +} > + > +static int nf_tables_newcounter(struct sock *nlsk, struct sk_buff *skb, > + const struct nlmsghdr *nlh, > + const struct nlattr * const nla[]) > +{ > + struct nft_ctx ctx; > + const struct nlattr *name; > + struct nft_named_ctr *counter, *matching; > + int err; > + > + if (!nla[NFTA_NAMED_CTR_NAME] || !nla[NFTA_NAMED_CTR_TABLE]) > + return -EINVAL; > + > + err = nft_ctx_init_from_counter(&ctx, skb, nlh, nla); > + if (err < 0) > + return err; You have to add here: if (table->use == UINT_MAX) return -EOVERFLOW; > + matching = nf_tables_counter_lookup(ctx.table, > + nla[NFTA_NAMED_CTR_NAME]); > + if (!IS_ERR(matching)) { > + if (nlh->nlmsg_flags & NLM_F_EXCL) > + return -EEXIST; > + if (nlh->nlmsg_flags & NLM_F_REPLACE) > + return -EOPNOTSUPP; > + else > + return -EOPNOTSUPP; You can simply return -EOPNOTSUPP, no need to check for the replace flag. > + } > + > + if (!(nlh->nlmsg_flags & NLM_F_CREATE)) > + return -ENOENT; > + > + counter = kzalloc(sizeof(*counter), GFP_KERNEL); > + if (!counter) > + return -ENOMEM; > + > + name = nla[NFTA_NAMED_CTR_NAME]; > + nla_strlcpy(counter->name, name, NFT_CTR_MAXNAMELEN); > + > + if (nla[NFTA_NAMED_CTR_BYTES]) { > + counter->counter.bytes = > + be64_to_cpu(nla_get_be64(nla[NFTA_NAMED_CTR_BYTES])); > + } > + > + if (nla[NFTA_NAMED_CTR_PACKETS]) { > + counter->counter.packets = > + be64_to_cpu(nla_get_be64(nla[NFTA_NAMED_CTR_PACKETS])); > + } > + > + seqlock_init(&counter->counter.lock); > + > + err = nft_trans_counter_add(&ctx, NFT_MSG_NEWCOUNTER, counter); > + if (err < 0) > + goto err; > + > + list_add_tail_rcu(&counter->list, &ctx.table->counters); > + ctx.table->use++; > + > + return 0; > +err: > + kfree(counter); > + return err; > +} > + > +static int nf_tables_fill_counter(struct sk_buff *skb, > + const struct nft_ctx *ctx, > + struct nft_named_ctr *counter, > + u16 event, u16 flags, u32 type) > +{ > + struct nfgenmsg *nfmsg; > + struct nlmsghdr *nlh; > + u32 portid = ctx->portid; > + u32 seq = ctx->seq; > + u64 pkts, bytes; > + > + event |= NFNL_SUBSYS_NFTABLES << 8; > + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), > + flags); > + if (!nlh) > + goto nla_put_failure; > + > + nfmsg = nlmsg_data(nlh); > + nfmsg->nfgen_family = ctx->afi->family; > + nfmsg->version = NFNETLINK_V0; > + nfmsg->res_id = htons(ctx->net->nft.base_seq & 0xffff); > + > + if (type == NFT_MSG_GETCOUNTER_ZERO) { > + write_seqlock_bh(&counter->counter.lock); Add this missing code here: pkts = counter->counter.packets; bytes = counter->counter.bytes; > + counter->counter.bytes = 0; > + counter->counter.packets = 0; > + write_sequnlock_bh(&counter->counter.lock); > + } else { > + do { > + seq = read_seqbegin(&counter->counter.lock); > + bytes = counter->counter.bytes; > + pkts = counter->counter.packets; > + } while (read_seqretry(&counter->counter.lock, seq)); > + } > + > + if (nla_put_string(skb, NFTA_NAMED_CTR_TABLE, ctx->table->name) || > + nla_put_string(skb, NFTA_NAMED_CTR_NAME, counter->name) || > + nla_put_be64(skb, NFTA_NAMED_CTR_PACKETS, cpu_to_be64(pkts)) || > + nla_put_be64(skb, NFTA_NAMED_CTR_BYTES, cpu_to_be64(bytes)) || > + nla_put_be32(skb, NFTA_NAMED_CTR_USE, htonl(counter->use))) > + goto nla_put_failure; > + > + return nlmsg_end(skb, nlh); > + > +nla_put_failure: > + nlmsg_trim(skb, nlh); > + return -1; > +} > + > +static int nf_tables_counter_notify(const struct nft_ctx *ctx, > + struct nft_named_ctr *counter, > + int event, gfp_t gfp_flags) > +{ > + struct sk_buff *skb; > + u32 portid = ctx->portid; > + int err; > + > + if (!ctx->report && > + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) > + return 0; > + > + err = -ENOBUFS; > + skb = nlmsg_new(NLMSG_GOODSIZE, gfp_flags); > + if (!skb) > + goto err; > + > + err = nf_tables_fill_counter(skb, ctx, counter, event, 0, 0); > + if (err < 0) { > + kfree_skb(skb); > + goto err; > + } > + > + err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, > + ctx->report, gfp_flags); > +err: > + if (err < 0) > + nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err); > + return err; > +} > + > +static int nf_tables_delcounter(struct sock *nlsk, struct sk_buff *skb, > + const struct nlmsghdr *nlh, > + const struct nlattr * const nla[]) > +{ > + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); > + struct nft_named_ctr *counter; > + struct nft_ctx ctx; > + int err; > + > + if (nfmsg->nfgen_family == NFPROTO_UNSPEC) > + return -EAFNOSUPPORT; > + if (!nla[NFTA_NAMED_CTR_TABLE]) > + return -EINVAL; > + > + err = nft_ctx_init_from_counter(&ctx, skb, nlh, nla); > + if (err < 0) > + return err; > + > + counter = nf_tables_counter_lookup(ctx.table, nla[NFTA_NAMED_CTR_NAME]); > + if (IS_ERR(counter)) > + return PTR_ERR(counter); > + if (counter->flags & NFT_NAMED_CTR_INACTIVE) > + return -ENOENT; > + if (counter->use > 0) > + return -EBUSY; > + > + return nft_delcounter(&ctx, counter); > +} > + > +static int nf_tables_dump_counter(struct sk_buff *skb, > + struct netlink_callback *cb) > +{ > + struct nft_named_ctr *counter; > + unsigned int idx, s_idx = cb->args[0]; > + struct nft_af_info *afi; > + struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; > + struct net *net = sock_net(skb->sk); > + int cur_family = cb->args[3]; > + struct nft_ctx *ctx = cb->data, ctx_counter; > + > + if (cb->args[1]) > + return skb->len; > + > + rcu_read_lock(); > + cb->seq = net->nft.base_seq; > + list_for_each_entry_rcu(afi, &net->nft.af_info, list) { > + if (ctx->afi && ctx->afi != afi) > + continue; > + > + if (cur_family) { > + if (afi->family != cur_family) > + continue; > + > + cur_family = 0; > + } > + list_for_each_entry_rcu(table, &afi->tables, list) { > + if (ctx->table && ctx->table != table) > + continue; > + > + if (cur_table) { > + if (cur_table != table) > + continue; > + > + cur_table = NULL; > + } > + idx = 0; > + list_for_each_entry_rcu(counter, &table->counters, > + list) { > + if (idx < s_idx) > + goto cont; > + > + ctx_counter = *ctx; > + ctx_counter.table = table; > + ctx_counter.afi = afi; > + if (nf_tables_fill_counter(skb, &ctx_counter, > + counter, > + NFT_MSG_NEWCOUNTER, > + NLM_F_MULTI, > + 0) < 0) { > + cb->args[0] = idx; > + cb->args[2] = (unsigned long)table; > + cb->args[3] = afi->family; > + goto done; > + } > + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); > +cont: > + idx++; > + } > + if (s_idx) > + s_idx = 0; > + } > + } > + cb->args[1] = 1; > +done: > + rcu_read_unlock(); > + return skb->len; > +} > + > +static int nf_tables_dump_counter_done(struct netlink_callback *cb) > +{ > + kfree(cb->data); > + > + return 0; > +} > + > +static int nf_tables_getcounter(struct sock *nlsk, struct sk_buff *skb, > + const struct nlmsghdr *nlh, > + const struct nlattr * const nla[]) > +{ > + struct nft_ctx ctx; > + struct sk_buff *skb2; > + int err, ret; > + struct nft_named_ctr *counter; > + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); > + > + /* Verify existence before starting dump */ > + err = nft_ctx_init_from_counter(&ctx, skb, nlh, nla); > + if (err < 0) > + return err; > + > + if (nlh->nlmsg_flags & NLM_F_DUMP) { > + struct netlink_dump_control c = { > + .dump = nf_tables_dump_counter, > + .done = nf_tables_dump_counter_done, > + }; > + struct nft_ctx *ctx_dump; > + > + ctx_dump = kmalloc(sizeof(*ctx_dump), GFP_KERNEL); > + if (!ctx_dump) > + return -ENOMEM; > + > + *ctx_dump = ctx; > + c.data = ctx_dump; > + > + return netlink_dump_start(nlsk, skb, nlh, &c); > + } > + > + /* Only accept unspec with dump */ > + if (nfmsg->nfgen_family == NFPROTO_UNSPEC) > + return -EAFNOSUPPORT; > + > + counter = nf_tables_counter_lookup(ctx.table, nla[NFTA_NAMED_CTR_NAME]); > + if (IS_ERR(counter)) > + return PTR_ERR(counter); > + > + if (counter->flags & NFT_NAMED_CTR_INACTIVE) > + return -ENOENT; > + > + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); > + if (!skb2) > + return -ENOMEM; > + > + err = nf_tables_fill_counter(skb2, &ctx, counter, NFT_MSG_NEWCOUNTER, 0, > + NFNL_MSG_TYPE(nlh->nlmsg_type)); > + if (err < 0) > + goto err; > + > + ret = nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); > + > + /* this avoids a loop in nfnetlink. */ > + return ret == -EAGAIN ? -ENOBUFS : ret; > + > +err: > + kfree_skb(skb2); > + return err; > +} > + > +void nft_counter_put(struct nft_named_ctr *counter) > +{ > + counter->use--; > + module_put(THIS_MODULE); > +} > +EXPORT_SYMBOL_GPL(nft_counter_put); > + > +int nft_counter_get(struct nft_named_ctr *counter) > +{ > + if (!try_module_get(THIS_MODULE)) > + return -ENOENT; > + Add here: if (counter->use == UINT_MAX) return -EOVERFLOW; > + counter->use++; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(nft_counter_get); > > static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { > [NFT_MSG_NEWTABLE] = { > .call_batch = nf_tables_newtable, > @@ -3465,6 +3906,26 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { > [NFT_MSG_GETGEN] = { > .call = nf_tables_getgen, > }, > + [NFT_MSG_NEWCOUNTER] = { > + .call_batch = nf_tables_newcounter, > + .attr_count = NFTA_NAMED_CTR_MAX, > + .policy = nft_named_ctr_policy, > + }, > + [NFT_MSG_GETCOUNTER] = { > + .call = nf_tables_getcounter, > + .attr_count = NFTA_NAMED_CTR_MAX, > + .policy = nft_named_ctr_policy, > + }, > + [NFT_MSG_GETCOUNTER_ZERO] = { > + .call = nf_tables_getcounter, > + .attr_count = NFTA_NAMED_CTR_MAX, > + .policy = nft_named_ctr_policy, > + }, > + [NFT_MSG_DELCOUNTER] = { > + .call_batch = nf_tables_delcounter, > + .attr_count = NFTA_NAMED_CTR_MAX, > + .policy = nft_named_ctr_policy, > + }, > }; > > static void nft_chain_commit_update(struct nft_trans *trans) > @@ -3503,6 +3964,9 @@ static void nf_tables_commit_release(struct nft_trans *trans) > case NFT_MSG_DELSET: > nft_set_destroy(nft_trans_set(trans)); > break; > + case NFT_MSG_DELCOUNTER: > + kfree(nft_trans_counter(trans)); > + break; > } > kfree(trans); > } > @@ -3608,6 +4072,20 @@ static int nf_tables_commit(struct sk_buff *skb) > } > nft_trans_destroy(trans); > break; > + case NFT_MSG_NEWCOUNTER: > + nft_trans_counter(trans)->flags &= ~NFT_NAMED_CTR_INACTIVE; > + nf_tables_counter_notify(&trans->ctx, > + nft_trans_counter(trans), > + NFT_MSG_NEWCOUNTER, > + GFP_KERNEL); > + nft_trans_destroy(trans); > + break; > + case NFT_MSG_DELCOUNTER: > + nf_tables_counter_notify(&trans->ctx, > + nft_trans_counter(trans), > + NFT_MSG_DELCOUNTER, > + GFP_KERNEL); > + break; > } > } > > @@ -3638,6 +4116,9 @@ static void nf_tables_abort_release(struct nft_trans *trans) > case NFT_MSG_NEWSET: > nft_set_destroy(nft_trans_set(trans)); > break; > + case NFT_MSG_NEWCOUNTER: > + kfree(nft_trans_counter(trans)); > + break; > } > kfree(trans); > } > @@ -3716,6 +4197,16 @@ static int nf_tables_abort(struct sk_buff *skb) > nft_trans_elem_set(trans)->nelems++; > nft_trans_destroy(trans); > break; > + case NFT_MSG_NEWCOUNTER: > + trans->ctx.table->use--; > + list_del_rcu(&nft_trans_counter(trans)->list); > + break; > + case NFT_MSG_DELCOUNTER: > + trans->ctx.table->use++; > + list_add_tail_rcu(&nft_trans_counter(trans)->list, > + &trans->ctx.table->counters); > + nft_trans_destroy(trans); > + break; > } > } > > diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c > index c89ee48..41a4cdf 100644 > --- a/net/netfilter/nft_counter.c > +++ b/net/netfilter/nft_counter.c > @@ -17,10 +17,8 @@ > #include <linux/netfilter/nf_tables.h> > #include <net/netfilter/nf_tables.h> > > -struct nft_counter { > - seqlock_t lock; > - u64 bytes; > - u64 packets; > +struct nft_named_ctr_expr { > + struct nft_named_ctr *counter; > }; > > static void nft_counter_eval(const struct nft_expr *expr, > @@ -35,6 +33,18 @@ static void nft_counter_eval(const struct nft_expr *expr, > write_sequnlock_bh(&priv->lock); > } > > +static void nft_named_ctr_eval(const struct nft_expr *expr, > + struct nft_data data[NFT_REG_MAX + 1], > + const struct nft_pktinfo *pkt) > +{ > + struct nft_named_ctr_expr *priv = nft_expr_priv(expr); > + > + write_seqlock_bh(&priv->counter->counter.lock); > + priv->counter->counter.bytes += pkt->skb->len; > + priv->counter->counter.packets++; > + write_sequnlock_bh(&priv->counter->counter.lock); > +} > + > static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) > { > struct nft_counter *priv = nft_expr_priv(expr); > @@ -58,11 +68,42 @@ nla_put_failure: > return -1; > } > > +static int nft_named_ctr_dump(struct sk_buff *skb, const struct nft_expr *expr) > +{ > + struct nft_named_ctr_expr *priv = nft_expr_priv(expr); > + unsigned int seq; > + u64 bytes; > + u64 packets; > + > + if (nla_put_string(skb, NFTA_COUNTER_NAME, priv->counter->name)) > + goto nla_put_failure; > + > + do { > + seq = read_seqbegin(&priv->counter->counter.lock); > + bytes = priv->counter->counter.bytes; > + packets = priv->counter->counter.packets; > + } while (read_seqretry(&priv->counter->counter.lock, seq)); > + > + if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(bytes))) > + goto nla_put_failure; > + if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(packets))) > + goto nla_put_failure; > + > + return 0; > + > +nla_put_failure: > + return -1; > +} > + > static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { > [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, > [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, > }; > > +static const struct nla_policy nft_named_ctr_policy[NFTA_COUNTER_MAX + 1] = { > + [NFTA_COUNTER_NAME] = { .type = NLA_STRING }, > +}; > + > static int nft_counter_init(const struct nft_ctx *ctx, > const struct nft_expr *expr, > const struct nlattr * const tb[]) > @@ -78,6 +119,35 @@ static int nft_counter_init(const struct nft_ctx *ctx, > return 0; > } > > +static int nft_named_ctr_init(const struct nft_ctx *ctx, > + const struct nft_expr *expr, > + const struct nlattr * const tb[]) > +{ > + struct nft_named_ctr_expr *priv = nft_expr_priv(expr); > + struct nft_named_ctr *counter; > + > + if (!tb[NFTA_COUNTER_NAME]) > + return -EINVAL; > + > + counter = nft_counter_lookup(ctx, nla_data(tb[NFTA_COUNTER_NAME])); > + if (!counter) > + return -ENOENT; > + > + priv->counter = counter; > + > + nft_counter_get(priv->counter); > + > + return 0; > +} > + > +static void nft_named_ctr_destroy(const struct nft_ctx *ctx, > + const struct nft_expr *expr) > +{ > + struct nft_named_ctr_expr *priv = nft_expr_priv(expr); > + > + nft_counter_put(priv->counter); > +} > + > static struct nft_expr_type nft_counter_type; > static const struct nft_expr_ops nft_counter_ops = { > .type = &nft_counter_type, > @@ -87,9 +157,28 @@ static const struct nft_expr_ops nft_counter_ops = { > .dump = nft_counter_dump, > }; > > +static const struct nft_expr_ops nft_named_ctr_ops = { > + .type = &nft_counter_type, > + .size = NFT_EXPR_SIZE(sizeof(struct nft_counter)), > + .eval = nft_named_ctr_eval, > + .init = nft_named_ctr_init, > + .dump = nft_named_ctr_dump, > + .destroy = nft_named_ctr_destroy, > +}; > + > +static const struct nft_expr_ops * > +nft_counter_select_ops(const struct nft_ctx *ctx, > + const struct nlattr * const tb[]) > +{ > + if (tb[NFTA_COUNTER_NAME]) > + return &nft_named_ctr_ops; > + > + return &nft_counter_ops; > +} > + > static struct nft_expr_type nft_counter_type __read_mostly = { > .name = "counter", > - .ops = &nft_counter_ops, > + .select_ops = &nft_counter_select_ops, > .policy = nft_counter_policy, > .maxattr = NFTA_COUNTER_MAX, > .owner = THIS_MODULE, > -- > 1.7.10.4 > > -- > To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html