netfilter: nf_tables: support for set flushing
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 5 Dec 2016 22:35:50 +0000 (23:35 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 7 Dec 2016 12:31:40 +0000 (13:31 +0100)
This patch adds support for set flushing, that consists of walking over
the set elements if the NFTA_SET_ELEM_LIST_ELEMENTS attribute is set.
This patch requires the following changes:

1) Add set->ops->deactivate_one() operation: This allows us to
   deactivate an element from the set element walk path, given we can
   skip the lookup that happens in ->deactivate().

2) Add a new nft_trans_alloc_gfp() function since we need to allocate
   transactions using GFP_ATOMIC given the set walk path happens with
   held rcu_read_lock.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_tables.h
net/netfilter/nf_tables_api.c
net/netfilter/nft_set_hash.c
net/netfilter/nft_set_rbtree.c

index 85f0f03f1e874971efbec7a0c856e4ada3f2b0af..924325c46aab2fafc10b3b92bccafd3e00c60c71 100644 (file)
@@ -259,7 +259,8 @@ struct nft_expr;
  *     @lookup: look up an element within the set
  *     @insert: insert new element into set
  *     @activate: activate new element in the next generation
- *     @deactivate: deactivate element in the next generation
+ *     @deactivate: lookup for element and deactivate it in the next generation
+ *     @deactivate_one: deactivate element in the next generation
  *     @remove: remove element from set
  *     @walk: iterate over all set elemeennts
  *     @privsize: function to return size of set private data
@@ -294,6 +295,9 @@ struct nft_set_ops {
        void *                          (*deactivate)(const struct net *net,
                                                      const struct nft_set *set,
                                                      const struct nft_set_elem *elem);
+       bool                            (*deactivate_one)(const struct net *net,
+                                                         const struct nft_set *set,
+                                                         void *priv);
        void                            (*remove)(const struct nft_set *set,
                                                  const struct nft_set_elem *elem);
        void                            (*walk)(const struct nft_ctx *ctx,
index b42059795819ab8ef6012992bfbe8d1700e45a76..a019a87e58ee8151620eb3d8500979d02dba54c1 100644 (file)
@@ -111,12 +111,12 @@ static void nft_ctx_init(struct nft_ctx *ctx,
        ctx->seq        = nlh->nlmsg_seq;
 }
 
-static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
-                                        int msg_type, u32 size)
+static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx,
+                                            int msg_type, u32 size, gfp_t gfp)
 {
        struct nft_trans *trans;
 
-       trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
+       trans = kzalloc(sizeof(struct nft_trans) + size, gfp);
        if (trans == NULL)
                return NULL;
 
@@ -126,6 +126,12 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
        return trans;
 }
 
+static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
+                                        int msg_type, u32 size)
+{
+       return nft_trans_alloc_gfp(ctx, msg_type, size, GFP_KERNEL);
+}
+
 static void nft_trans_destroy(struct nft_trans *trans)
 {
        list_del(&trans->list);
@@ -3876,6 +3882,34 @@ err1:
        return err;
 }
 
+static int nft_flush_set(const struct nft_ctx *ctx,
+                        const struct nft_set *set,
+                        const struct nft_set_iter *iter,
+                        const struct nft_set_elem *elem)
+{
+       struct nft_trans *trans;
+       int err;
+
+       trans = nft_trans_alloc_gfp(ctx, NFT_MSG_DELSETELEM,
+                                   sizeof(struct nft_trans_elem), GFP_ATOMIC);
+       if (!trans)
+               return -ENOMEM;
+
+       if (!set->ops->deactivate_one(ctx->net, set, elem->priv)) {
+               err = -ENOENT;
+               goto err1;
+       }
+
+       nft_trans_elem_set(trans) = (struct nft_set *)set;
+       nft_trans_elem(trans) = *((struct nft_set_elem *)elem);
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+
+       return 0;
+err1:
+       kfree(trans);
+       return err;
+}
+
 static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
                                struct sk_buff *skb, const struct nlmsghdr *nlh,
                                const struct nlattr * const nla[])
@@ -3886,9 +3920,6 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
        struct nft_ctx ctx;
        int rem, err = 0;
 
-       if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
-               return -EINVAL;
-
        err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, genmask);
        if (err < 0)
                return err;
@@ -3900,6 +3931,18 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
        if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
                return -EBUSY;
 
+       if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL) {
+               struct nft_set_dump_args args = {
+                       .iter   = {
+                               .genmask        = genmask,
+                               .fn             = nft_flush_set,
+                       },
+               };
+               set->ops->walk(&ctx, set, &args.iter);
+
+               return args.iter.err;
+       }
+
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
                err = nft_del_setelem(&ctx, set, attr);
                if (err < 0)
index 73f7687c5656012d0b77d41294f83c1145eb4e7f..1e20e2bbb6d924b5cdb331acf8610c8719763c7c 100644 (file)
@@ -397,6 +397,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
        .insert         = nft_hash_insert,
        .activate       = nft_hash_activate,
        .deactivate     = nft_hash_deactivate,
+       .deactivate_one = nft_hash_deactivate_one,
        .remove         = nft_hash_remove,
        .lookup         = nft_hash_lookup,
        .update         = nft_hash_update,
index 5580bb64dc0f629603792fdf12e923941b2c5601..08376e50f6cdc26d7864e846b5a07f2ca662207c 100644 (file)
@@ -304,6 +304,7 @@ static struct nft_set_ops nft_rbtree_ops __read_mostly = {
        .insert         = nft_rbtree_insert,
        .remove         = nft_rbtree_remove,
        .deactivate     = nft_rbtree_deactivate,
+       .deactivate_one = nft_rbtree_deactivate_one,
        .activate       = nft_rbtree_activate,
        .lookup         = nft_rbtree_lookup,
        .walk           = nft_rbtree_walk,