netfilter: nf_tables: support for set flushing
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>
This commit is contained in:
parent
37df5301a3
commit
8411b6442e
|
@ -259,7 +259,8 @@ struct nft_expr;
|
||||||
* @lookup: look up an element within the set
|
* @lookup: look up an element within the set
|
||||||
* @insert: insert new element into set
|
* @insert: insert new element into set
|
||||||
* @activate: activate new element in the next generation
|
* @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
|
* @remove: remove element from set
|
||||||
* @walk: iterate over all set elemeennts
|
* @walk: iterate over all set elemeennts
|
||||||
* @privsize: function to return size of set private data
|
* @privsize: function to return size of set private data
|
||||||
|
@ -294,6 +295,9 @@ struct nft_set_ops {
|
||||||
void * (*deactivate)(const struct net *net,
|
void * (*deactivate)(const struct net *net,
|
||||||
const struct nft_set *set,
|
const struct nft_set *set,
|
||||||
const struct nft_set_elem *elem);
|
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,
|
void (*remove)(const struct nft_set *set,
|
||||||
const struct nft_set_elem *elem);
|
const struct nft_set_elem *elem);
|
||||||
void (*walk)(const struct nft_ctx *ctx,
|
void (*walk)(const struct nft_ctx *ctx,
|
||||||
|
|
|
@ -111,12 +111,12 @@ static void nft_ctx_init(struct nft_ctx *ctx,
|
||||||
ctx->seq = nlh->nlmsg_seq;
|
ctx->seq = nlh->nlmsg_seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
|
static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx,
|
||||||
int msg_type, u32 size)
|
int msg_type, u32 size, gfp_t gfp)
|
||||||
{
|
{
|
||||||
struct nft_trans *trans;
|
struct nft_trans *trans;
|
||||||
|
|
||||||
trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
|
trans = kzalloc(sizeof(struct nft_trans) + size, gfp);
|
||||||
if (trans == NULL)
|
if (trans == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -126,6 +126,12 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
|
||||||
return trans;
|
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)
|
static void nft_trans_destroy(struct nft_trans *trans)
|
||||||
{
|
{
|
||||||
list_del(&trans->list);
|
list_del(&trans->list);
|
||||||
|
@ -3876,6 +3882,34 @@ err1:
|
||||||
return err;
|
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,
|
static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
|
||||||
struct sk_buff *skb, const struct nlmsghdr *nlh,
|
struct sk_buff *skb, const struct nlmsghdr *nlh,
|
||||||
const struct nlattr * const nla[])
|
const struct nlattr * const nla[])
|
||||||
|
@ -3886,9 +3920,6 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
|
||||||
struct nft_ctx ctx;
|
struct nft_ctx ctx;
|
||||||
int rem, err = 0;
|
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);
|
err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, genmask);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
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)
|
if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
|
||||||
return -EBUSY;
|
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) {
|
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
|
||||||
err = nft_del_setelem(&ctx, set, attr);
|
err = nft_del_setelem(&ctx, set, attr);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
|
@ -397,6 +397,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
|
||||||
.insert = nft_hash_insert,
|
.insert = nft_hash_insert,
|
||||||
.activate = nft_hash_activate,
|
.activate = nft_hash_activate,
|
||||||
.deactivate = nft_hash_deactivate,
|
.deactivate = nft_hash_deactivate,
|
||||||
|
.deactivate_one = nft_hash_deactivate_one,
|
||||||
.remove = nft_hash_remove,
|
.remove = nft_hash_remove,
|
||||||
.lookup = nft_hash_lookup,
|
.lookup = nft_hash_lookup,
|
||||||
.update = nft_hash_update,
|
.update = nft_hash_update,
|
||||||
|
|
|
@ -304,6 +304,7 @@ static struct nft_set_ops nft_rbtree_ops __read_mostly = {
|
||||||
.insert = nft_rbtree_insert,
|
.insert = nft_rbtree_insert,
|
||||||
.remove = nft_rbtree_remove,
|
.remove = nft_rbtree_remove,
|
||||||
.deactivate = nft_rbtree_deactivate,
|
.deactivate = nft_rbtree_deactivate,
|
||||||
|
.deactivate_one = nft_rbtree_deactivate_one,
|
||||||
.activate = nft_rbtree_activate,
|
.activate = nft_rbtree_activate,
|
||||||
.lookup = nft_rbtree_lookup,
|
.lookup = nft_rbtree_lookup,
|
||||||
.walk = nft_rbtree_walk,
|
.walk = nft_rbtree_walk,
|
||||||
|
|
Loading…
Reference in a new issue