Commit 07114590 authored by David S. Miller's avatar David S. Miller
Browse files


Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

The following patchset contains Netfilter updates for net-next:

1) Add support for the catch-all set element. This special element
   can be used to define a default action to be applied in case that
   the set lookup returns no matching element.

2) Fix incorrect #ifdef dependencies in the nftables cgroupsv2
   support, from Arnd Bergmann.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 69e16d01 7acc0bb4
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -497,6 +497,7 @@ struct nft_set {
	u8				dlen;
	u8				num_exprs;
	struct nft_expr			*exprs[NFT_SET_EXPR_MAX];
	struct list_head		catchall_list;
	unsigned char			data[]
		__attribute__((aligned(__alignof__(u64))));
};
@@ -522,6 +523,10 @@ struct nft_set *nft_set_lookup_global(const struct net *net,
				      const struct nlattr *nla_set_id,
				      u8 genmask);

struct nft_set_ext *nft_set_catchall_lookup(const struct net *net,
					    const struct nft_set *set);
void *nft_set_catchall_gc(const struct nft_set *set);

static inline unsigned long nft_set_gc_interval(const struct nft_set *set)
{
	return set->gc_int ? msecs_to_jiffies(set->gc_int) : HZ;
+2 −0
Original line number Diff line number Diff line
@@ -398,9 +398,11 @@ enum nft_set_attributes {
 * enum nft_set_elem_flags - nf_tables set element flags
 *
 * @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval
 * @NFT_SET_ELEM_CATCHALL: special catch-all element
 */
enum nft_set_elem_flags {
	NFT_SET_ELEM_INTERVAL_END	= 0x1,
	NFT_SET_ELEM_CATCHALL		= 0x2,
};

/**
+486 −90
Original line number Diff line number Diff line
@@ -4389,6 +4389,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
	}

	INIT_LIST_HEAD(&set->bindings);
	INIT_LIST_HEAD(&set->catchall_list);
	set->table = table;
	write_pnet(&set->net, net);
	set->ops   = ops;
@@ -4434,6 +4435,24 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
	return err;
}

struct nft_set_elem_catchall {
	struct list_head	list;
	struct rcu_head		rcu;
	void			*elem;
};

static void nft_set_catchall_destroy(const struct nft_ctx *ctx,
				     struct nft_set *set)
{
	struct nft_set_elem_catchall *catchall;

	list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
		list_del_rcu(&catchall->list);
		nft_set_elem_destroy(set, catchall->elem, true);
		kfree_rcu(catchall);
	}
}

static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
{
	int i;
@@ -4445,6 +4464,7 @@ static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
		nft_expr_destroy(ctx, set->exprs[i]);

	set->ops->destroy(set);
	nft_set_catchall_destroy(ctx, set);
	kfree(set->name);
	kvfree(set);
}
@@ -4499,9 +4519,8 @@ static int nft_validate_register_store(const struct nft_ctx *ctx,
				       enum nft_data_types type,
				       unsigned int len);

static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
static int nft_setelem_data_validate(const struct nft_ctx *ctx,
				     struct nft_set *set,
					const struct nft_set_iter *iter,
				     struct nft_set_elem *elem)
{
	const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
@@ -4514,6 +4533,37 @@ static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
					   set->dlen);
}

static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
					struct nft_set *set,
					const struct nft_set_iter *iter,
					struct nft_set_elem *elem)
{
	return nft_setelem_data_validate(ctx, set, elem);
}

static int nft_set_catchall_bind_check(const struct nft_ctx *ctx,
				       struct nft_set *set)
{
	u8 genmask = nft_genmask_next(ctx->net);
	struct nft_set_elem_catchall *catchall;
	struct nft_set_elem elem;
	struct nft_set_ext *ext;
	int ret = 0;

	list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);
		if (!nft_set_elem_active(ext, genmask))
			continue;

		elem.priv = catchall->elem;
		ret = nft_setelem_data_validate(ctx, set, &elem);
		if (ret < 0)
			break;
	}

	return ret;
}

int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
		       struct nft_set_binding *binding)
{
@@ -4543,6 +4593,9 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
		iter.fn		= nf_tables_bind_check_setelem;

		set->ops->walk(ctx, set, &iter);
		if (!iter.err)
			iter.err = nft_set_catchall_bind_check(ctx, set);

		if (iter.err < 0)
			return iter.err;
	}
@@ -4729,7 +4782,8 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
	if (nest == NULL)
		goto nla_put_failure;

	if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext),
	if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) &&
	    nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext),
			  NFT_DATA_VALUE, set->klen) < 0)
		goto nla_put_failure;

@@ -4818,6 +4872,29 @@ struct nft_set_dump_ctx {
	struct nft_ctx		ctx;
};

static int nft_set_catchall_dump(struct net *net, struct sk_buff *skb,
				 const struct nft_set *set)
{
	struct nft_set_elem_catchall *catchall;
	u8 genmask = nft_genmask_cur(net);
	struct nft_set_elem elem;
	struct nft_set_ext *ext;
	int ret = 0;

	list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);
		if (!nft_set_elem_active(ext, genmask) ||
		    nft_set_elem_expired(ext))
			continue;

		elem.priv = catchall->elem;
		ret = nf_tables_fill_setelem(skb, set, &elem);
		break;
	}

	return ret;
}

static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct nft_set_dump_ctx *dump_ctx = cb->data;
@@ -4882,6 +4959,9 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
	args.iter.err		= 0;
	args.iter.fn		= nf_tables_dump_setelem;
	set->ops->walk(&dump_ctx->ctx, set, &args.iter);

	if (!args.iter.err && args.iter.count == cb->args[0])
		args.iter.err = nft_set_catchall_dump(net, skb, set);
	rcu_read_unlock();

	nla_nest_end(skb, nest);
@@ -4961,8 +5041,8 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
		return 0;

	*flags = ntohl(nla_get_be32(attr));
	if (*flags & ~NFT_SET_ELEM_INTERVAL_END)
		return -EINVAL;
	if (*flags & ~(NFT_SET_ELEM_INTERVAL_END | NFT_SET_ELEM_CATCHALL))
		return -EOPNOTSUPP;
	if (!(set->flags & NFT_SET_INTERVAL) &&
	    *flags & NFT_SET_ELEM_INTERVAL_END)
		return -EINVAL;
@@ -5007,6 +5087,46 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
	return 0;
}

static void *nft_setelem_catchall_get(const struct net *net,
				      const struct nft_set *set)
{
	struct nft_set_elem_catchall *catchall;
	u8 genmask = nft_genmask_cur(net);
	struct nft_set_ext *ext;
	void *priv = NULL;

	list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);
		if (!nft_set_elem_active(ext, genmask) ||
		    nft_set_elem_expired(ext))
			continue;

		priv = catchall->elem;
		break;
	}

	return priv;
}

static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set,
			   struct nft_set_elem *elem, u32 flags)
{
	void *priv;

	if (!(flags & NFT_SET_ELEM_CATCHALL)) {
		priv = set->ops->get(ctx->net, set, elem, flags);
		if (IS_ERR(priv))
			return PTR_ERR(priv);
	} else {
		priv = nft_setelem_catchall_get(ctx->net, set);
		if (!priv)
			return -ENOENT;
	}
	elem->priv = priv;

	return 0;
}

static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
			    const struct nlattr *attr)
{
@@ -5014,7 +5134,6 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
	struct nft_set_elem elem;
	struct sk_buff *skb;
	uint32_t flags = 0;
	void *priv;
	int err;

	err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
@@ -5022,17 +5141,19 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
	if (err < 0)
		return err;

	if (!nla[NFTA_SET_ELEM_KEY])
		return -EINVAL;

	err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags);
	if (err < 0)
		return err;

	if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL))
		return -EINVAL;

	if (nla[NFTA_SET_ELEM_KEY]) {
		err = nft_setelem_parse_key(ctx, set, &elem.key.val,
					    nla[NFTA_SET_ELEM_KEY]);
		if (err < 0)
			return err;
	}

	if (nla[NFTA_SET_ELEM_KEY_END]) {
		err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
@@ -5041,11 +5162,9 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
			return err;
	}

	priv = set->ops->get(ctx->net, set, &elem, flags);
	if (IS_ERR(priv))
		return PTR_ERR(priv);

	elem.priv = priv;
	err = nft_setelem_get(ctx, set, &elem, flags);
	if (err < 0)
		return err;

	err = -ENOMEM;
	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
@@ -5205,6 +5324,7 @@ void *nft_set_elem_init(const struct nft_set *set,
	ext = nft_set_elem_ext(set, elem);
	nft_set_ext_init(ext, tmpl);

	if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY))
		memcpy(nft_set_ext_key(ext), key, set->klen);
	if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
		memcpy(nft_set_ext_key_end(ext), key_end, set->klen);
@@ -5263,8 +5383,8 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
}
EXPORT_SYMBOL_GPL(nft_set_elem_destroy);

/* Only called from commit path, nft_set_elem_deactivate() already deals with
 * the refcounting from the preparation phase.
/* Only called from commit path, nft_setelem_data_deactivate() already deals
 * with the refcounting from the preparation phase.
 */
static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
				       const struct nft_set *set, void *elem)
@@ -5336,6 +5456,192 @@ static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
	return -ENOMEM;
}

struct nft_set_ext *nft_set_catchall_lookup(const struct net *net,
					    const struct nft_set *set)
{
	struct nft_set_elem_catchall *catchall;
	u8 genmask = nft_genmask_cur(net);
	struct nft_set_ext *ext;

	list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);
		if (nft_set_elem_active(ext, genmask) &&
		    !nft_set_elem_expired(ext))
			return ext;
	}

	return NULL;
}
EXPORT_SYMBOL_GPL(nft_set_catchall_lookup);

void *nft_set_catchall_gc(const struct nft_set *set)
{
	struct nft_set_elem_catchall *catchall, *next;
	struct nft_set_ext *ext;
	void *elem = NULL;

	list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);

		if (!nft_set_elem_expired(ext) ||
		    nft_set_elem_mark_busy(ext))
			continue;

		elem = catchall->elem;
		list_del_rcu(&catchall->list);
		kfree_rcu(catchall, rcu);
		break;
	}

	return elem;
}
EXPORT_SYMBOL_GPL(nft_set_catchall_gc);

static int nft_setelem_catchall_insert(const struct net *net,
				       struct nft_set *set,
				       const struct nft_set_elem *elem,
				       struct nft_set_ext **pext)
{
	struct nft_set_elem_catchall *catchall;
	u8 genmask = nft_genmask_next(net);
	struct nft_set_ext *ext;

	list_for_each_entry(catchall, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);
		if (nft_set_elem_active(ext, genmask)) {
			*pext = ext;
			return -EEXIST;
		}
	}

	catchall = kmalloc(sizeof(*catchall), GFP_KERNEL);
	if (!catchall)
		return -ENOMEM;

	catchall->elem = elem->priv;
	list_add_tail_rcu(&catchall->list, &set->catchall_list);

	return 0;
}

static int nft_setelem_insert(const struct net *net,
			      struct nft_set *set,
			      const struct nft_set_elem *elem,
			      struct nft_set_ext **ext, unsigned int flags)
{
	int ret;

	if (flags & NFT_SET_ELEM_CATCHALL)
		ret = nft_setelem_catchall_insert(net, set, elem, ext);
	else
		ret = set->ops->insert(net, set, elem, ext);

	return ret;
}

static bool nft_setelem_is_catchall(const struct nft_set *set,
				    const struct nft_set_elem *elem)
{
	struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);

	if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
	    *nft_set_ext_flags(ext) & NFT_SET_ELEM_CATCHALL)
		return true;

	return false;
}

static void nft_setelem_activate(struct net *net, struct nft_set *set,
				 struct nft_set_elem *elem)
{
	struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);

	if (nft_setelem_is_catchall(set, elem)) {
		nft_set_elem_change_active(net, set, ext);
		nft_set_elem_clear_busy(ext);
	} else {
		set->ops->activate(net, set, elem);
	}
}

static int nft_setelem_catchall_deactivate(const struct net *net,
					   struct nft_set *set,
					   struct nft_set_elem *elem)
{
	struct nft_set_elem_catchall *catchall;
	struct nft_set_ext *ext;

	list_for_each_entry(catchall, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);
		if (!nft_is_active(net, ext) ||
		    nft_set_elem_mark_busy(ext))
			continue;

		kfree(elem->priv);
		elem->priv = catchall->elem;
		nft_set_elem_change_active(net, set, ext);
		return 0;
	}

	return -ENOENT;
}

static int __nft_setelem_deactivate(const struct net *net,
				    struct nft_set *set,
				    struct nft_set_elem *elem)
{
	void *priv;

	priv = set->ops->deactivate(net, set, elem);
	if (!priv)
		return -ENOENT;

	kfree(elem->priv);
	elem->priv = priv;
	set->ndeact++;

	return 0;
}

static int nft_setelem_deactivate(const struct net *net,
				  struct nft_set *set,
				  struct nft_set_elem *elem, u32 flags)
{
	int ret;

	if (flags & NFT_SET_ELEM_CATCHALL)
		ret = nft_setelem_catchall_deactivate(net, set, elem);
	else
		ret = __nft_setelem_deactivate(net, set, elem);

	return ret;
}

static void nft_setelem_catchall_remove(const struct net *net,
					const struct nft_set *set,
					const struct nft_set_elem *elem)
{
	struct nft_set_elem_catchall *catchall, *next;

	list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
		if (catchall->elem == elem->priv) {
			list_del_rcu(&catchall->list);
			kfree_rcu(catchall);
			break;
		}
	}
}

static void nft_setelem_remove(const struct net *net,
			       const struct nft_set *set,
			       const struct nft_set_elem *elem)
{
	if (nft_setelem_is_catchall(set, elem))
		nft_setelem_catchall_remove(net, set, elem);
	else
		set->ops->remove(net, set, elem);
}

static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
			    const struct nlattr *attr, u32 nlmsg_flags)
{
@@ -5362,14 +5668,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
	if (err < 0)
		return err;

	if (nla[NFTA_SET_ELEM_KEY] == NULL)
		return -EINVAL;

	nft_set_ext_prepare(&tmpl);

	err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags);
	if (err < 0)
		return err;

	if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL))
		return -EINVAL;

	if (flags != 0)
		nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);

@@ -5474,12 +5781,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
		num_exprs = set->num_exprs;
	}

	if (nla[NFTA_SET_ELEM_KEY]) {
		err = nft_setelem_parse_key(ctx, set, &elem.key.val,
					    nla[NFTA_SET_ELEM_KEY]);
		if (err < 0)
			goto err_set_elem_expr;

		nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
	}

	if (nla[NFTA_SET_ELEM_KEY_END]) {
		err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
@@ -5596,7 +5905,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
	}

	ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
	err = set->ops->insert(ctx->net, set, &elem, &ext2);

	err = nft_setelem_insert(ctx->net, set, &elem, &ext2, flags);
	if (err) {
		if (err == -EEXIST) {
			if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) ^
@@ -5623,7 +5933,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
		goto err_element_clash;
	}

	if (set->size &&
	if (!(flags & NFT_SET_ELEM_CATCHALL) && set->size &&
	    !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) {
		err = -ENFILE;
		goto err_set_full;
@@ -5634,7 +5944,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
	return 0;

err_set_full:
	set->ops->remove(ctx->net, set, &elem);
	nft_setelem_remove(ctx->net, set, &elem);
err_element_clash:
	kfree(trans);
err_elem_expr:
@@ -5733,7 +6043,7 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
	}
}

static void nft_set_elem_activate(const struct net *net,
static void nft_setelem_data_activate(const struct net *net,
				      const struct nft_set *set,
				      struct nft_set_elem *elem)
{
@@ -5745,7 +6055,7 @@ static void nft_set_elem_activate(const struct net *net,
		(*nft_set_ext_obj(ext))->use++;
}

static void nft_set_elem_deactivate(const struct net *net,
static void nft_setelem_data_deactivate(const struct net *net,
					const struct nft_set *set,
					struct nft_set_elem *elem)
{
@@ -5766,7 +6076,6 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
	struct nft_set_ext *ext;
	struct nft_trans *trans;
	u32 flags = 0;
	void *priv;
	int err;

	err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
@@ -5774,23 +6083,26 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
	if (err < 0)
		return err;

	if (nla[NFTA_SET_ELEM_KEY] == NULL)
	err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags);
	if (err < 0)
		return err;

	if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL))
		return -EINVAL;

	nft_set_ext_prepare(&tmpl);

	err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags);
	if (err < 0)
		return err;
	if (flags != 0)
		nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);

	if (nla[NFTA_SET_ELEM_KEY]) {
		err = nft_setelem_parse_key(ctx, set, &elem.key.val,
					    nla[NFTA_SET_ELEM_KEY]);
		if (err < 0)
			return err;

		nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
	}

	if (nla[NFTA_SET_ELEM_KEY_END]) {
		err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
@@ -5816,15 +6128,11 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
	if (trans == NULL)
		goto fail_trans;

	priv = set->ops->deactivate(ctx->net, set, &elem);
	if (priv == NULL) {
		err = -ENOENT;
	err = nft_setelem_deactivate(ctx->net, set, &elem, flags);
	if (err < 0)
		goto fail_ops;
	}
	kfree(elem.priv);
	elem.priv = priv;

	nft_set_elem_deactivate(ctx->net, set, &elem);
	nft_setelem_data_deactivate(ctx->net, set, &elem);

	nft_trans_elem(trans) = elem;
	nft_trans_commit_list_add_tail(ctx->net, trans);
@@ -5839,7 +6147,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
	return err;
}

static int nft_flush_set(const struct nft_ctx *ctx,
static int nft_setelem_flush(const struct nft_ctx *ctx,
			     struct nft_set *set,
			     const struct nft_set_iter *iter,
			     struct nft_set_elem *elem)
@@ -5858,7 +6166,7 @@ static int nft_flush_set(const struct nft_ctx *ctx,
	}
	set->ndeact++;

	nft_set_elem_deactivate(ctx->net, set, elem);
	nft_setelem_data_deactivate(ctx->net, set, elem);
	nft_trans_elem_set(trans) = set;
	nft_trans_elem(trans) = *elem;
	nft_trans_commit_list_add_tail(ctx->net, trans);
@@ -5869,6 +6177,63 @@ static int nft_flush_set(const struct nft_ctx *ctx,
	return err;
}

static int __nft_set_catchall_flush(const struct nft_ctx *ctx,
				    struct nft_set *set,
				    struct nft_set_elem *elem)
{
	struct nft_trans *trans;

	trans = nft_trans_alloc_gfp(ctx, NFT_MSG_DELSETELEM,
				    sizeof(struct nft_trans_elem), GFP_KERNEL);
	if (!trans)
		return -ENOMEM;

	nft_setelem_data_deactivate(ctx->net, set, elem);
	nft_trans_elem_set(trans) = set;
	nft_trans_elem(trans) = *elem;
	nft_trans_commit_list_add_tail(ctx->net, trans);

	return 0;
}

static int nft_set_catchall_flush(const struct nft_ctx *ctx,
				  struct nft_set *set)
{
	u8 genmask = nft_genmask_next(ctx->net);
	struct nft_set_elem_catchall *catchall;
	struct nft_set_elem elem;
	struct nft_set_ext *ext;
	int ret = 0;

	list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);
		if (!nft_set_elem_active(ext, genmask) ||
		    nft_set_elem_mark_busy(ext))
			continue;

		elem.priv = catchall->elem;
		ret = __nft_set_catchall_flush(ctx, set, &elem);
		if (ret < 0)
			break;
	}

	return ret;
}

static int nft_set_flush(struct nft_ctx *ctx, struct nft_set *set, u8 genmask)
{
	struct nft_set_iter iter = {
		.genmask	= genmask,
		.fn		= nft_setelem_flush,
	};

	set->ops->walk(ctx, set, &iter);
	if (!iter.err)
		iter.err = nft_set_catchall_flush(ctx, set);

	return iter.err;
}

static int nf_tables_delsetelem(struct sk_buff *skb,
				const struct nfnl_info *info,
				const struct nlattr * const nla[])
@@ -5892,22 +6257,13 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
	if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
		return -EBUSY;

	if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL) {
		struct nft_set_iter iter = {
			.genmask	= genmask,
			.fn		= nft_flush_set,
		};
		set->ops->walk(&ctx, set, &iter);

		return iter.err;
	}
	if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
		return nft_set_flush(&ctx, set, genmask);

	nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
		err = nft_del_setelem(&ctx, set, attr);
		if (err < 0)
			break;

		set->ndeact++;
	}
	return err;
}
@@ -8258,7 +8614,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
		case NFT_MSG_NEWSETELEM:
			te = (struct nft_trans_elem *)trans->data;

			te->set->ops->activate(net, te->set, &te->elem);
			nft_setelem_activate(net, te->set, &te->elem);
			nf_tables_setelem_notify(&trans->ctx, te->set,
						 &te->elem,
						 NFT_MSG_NEWSETELEM, 0);
@@ -8270,9 +8626,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
			nf_tables_setelem_notify(&trans->ctx, te->set,
						 &te->elem,
						 NFT_MSG_DELSETELEM, 0);
			te->set->ops->remove(net, te->set, &te->elem);
			nft_setelem_remove(net, te->set, &te->elem);
			if (!nft_setelem_is_catchall(te->set, &te->elem)) {
				atomic_dec(&te->set->nelems);
				te->set->ndeact--;
			}
			break;
		case NFT_MSG_NEWOBJ:
			if (nft_trans_obj_update(trans)) {
@@ -8473,14 +8831,16 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
				break;
			}
			te = (struct nft_trans_elem *)trans->data;
			te->set->ops->remove(net, te->set, &te->elem);
			nft_setelem_remove(net, te->set, &te->elem);
			if (!nft_setelem_is_catchall(te->set, &te->elem))
				atomic_dec(&te->set->nelems);
			break;
		case NFT_MSG_DELSETELEM:
			te = (struct nft_trans_elem *)trans->data;

			nft_set_elem_activate(net, te->set, &te->elem);
			te->set->ops->activate(net, te->set, &te->elem);
			nft_setelem_data_activate(net, te->set, &te->elem);
			nft_setelem_activate(net, te->set, &te->elem);
			if (!nft_setelem_is_catchall(te->set, &te->elem))
				te->set->ndeact--;

			nft_trans_destroy(trans);
@@ -8626,26 +8986,59 @@ EXPORT_SYMBOL_GPL(nft_chain_validate_hooks);
static int nf_tables_check_loops(const struct nft_ctx *ctx,
				 const struct nft_chain *chain);

static int nft_check_loops(const struct nft_ctx *ctx,
			   const struct nft_set_ext *ext)
{
	const struct nft_data *data;
	int ret;

	data = nft_set_ext_data(ext);
	switch (data->verdict.code) {
	case NFT_JUMP:
	case NFT_GOTO:
		ret = nf_tables_check_loops(ctx, data->verdict.chain);
		break;
	default:
		ret = 0;
		break;
	}

	return ret;
}

static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
					struct nft_set *set,
					const struct nft_set_iter *iter,
					struct nft_set_elem *elem)
{
	const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
	const struct nft_data *data;

	if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
	    *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
		return 0;

	data = nft_set_ext_data(ext);
	switch (data->verdict.code) {
	case NFT_JUMP:
	case NFT_GOTO:
		return nf_tables_check_loops(ctx, data->verdict.chain);
	default:
		return 0;
	return nft_check_loops(ctx, ext);
}

static int nft_set_catchall_loops(const struct nft_ctx *ctx,
				  struct nft_set *set)
{
	u8 genmask = nft_genmask_next(ctx->net);
	struct nft_set_elem_catchall *catchall;
	struct nft_set_ext *ext;
	int ret = 0;

	list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
		ext = nft_set_elem_ext(set, catchall->elem);
		if (!nft_set_elem_active(ext, genmask))
			continue;

		ret = nft_check_loops(ctx, ext);
		if (ret < 0)
			return ret;
	}

	return ret;
}

static int nf_tables_check_loops(const struct nft_ctx *ctx,
@@ -8707,6 +9100,9 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
			iter.fn		= nf_tables_loop_check_setelem;

			set->ops->walk(ctx, set, &iter);
			if (!iter.err)
				iter.err = nft_set_catchall_loops(ctx, set);

			if (iter.err < 0)
				return iter.err;
		}
+8 −4
Original line number Diff line number Diff line
@@ -30,14 +30,18 @@ void nft_lookup_eval(const struct nft_expr *expr,
	const struct nft_lookup *priv = nft_expr_priv(expr);
	const struct nft_set *set = priv->set;
	const struct nft_set_ext *ext = NULL;
	const struct net *net = nft_net(pkt);
	bool found;

	found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg],
				 &ext) ^ priv->invert;
	found = set->ops->lookup(net, set, &regs->data[priv->sreg], &ext) ^
				 priv->invert;
	if (!found) {
		ext = nft_set_catchall_lookup(net, set);
		if (!ext) {
			regs->verdict.code = NFT_BREAK;
			return;
		}
	}

	if (ext) {
		if (set->flags & NFT_SET_MAP)
+7 −4
Original line number Diff line number Diff line
@@ -105,16 +105,19 @@ static void nft_objref_map_eval(const struct nft_expr *expr,
{
	struct nft_objref_map *priv = nft_expr_priv(expr);
	const struct nft_set *set = priv->set;
	struct net *net = nft_net(pkt);
	const struct nft_set_ext *ext;
	struct nft_object *obj;
	bool found;

	found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg],
				 &ext);
	found = set->ops->lookup(net, set, &regs->data[priv->sreg], &ext);
	if (!found) {
		ext = nft_set_catchall_lookup(net, set);
		if (!ext) {
			regs->verdict.code = NFT_BREAK;
			return;
		}
	}
	obj = *nft_set_ext_obj(ext);
	obj->ops->eval(obj, regs, pkt);
}
Loading