From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Ana Rey Botello <ana@soleta.eu>
Cc: netfilter-devel@vger.kernel.org
Subject: Re: [v3 nf] netfilter: add counters support
Date: Sun, 1 Feb 2015 21:37:34 +0100 [thread overview]
Message-ID: <20150201203733.GA16315@salvia> (raw)
In-Reply-To: <609d6e474895ec4038e1e7d70f510535eba0564b.1422704916.git.ana@soleta.eu>
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@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
prev parent reply other threads:[~2015-02-01 20:34 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-01-31 11:51 [v3 nf] netfilter: add counters support Ana Rey Botello
2015-02-01 20:37 ` Pablo Neira Ayuso [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150201203733.GA16315@salvia \
--to=pablo@netfilter.org \
--cc=ana@soleta.eu \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.