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


Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) A new selftest for nf_queue, from Florian Westphal. This test
   covers two recent fixes: 07f8e4d0 ("tcp: also NULL skb->dev
   when copy was needed") and b738a185 ("tcp: ensure skb->dev is
   NULL before leaving TCP stack").

2) The fwd action breaks with ifb. For safety in next extensions,
   make sure the fwd action only runs from ingress until it is extended
   to be used from a different hook.

3) The pipapo set type now reports EEXIST in case of subrange overlaps.
   Update the rbtree set to validate range overlaps, so far this
   validation is only done only from userspace. From Stefano Brivio.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7e566df6 a64d558d
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -5082,6 +5082,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
				err = -EBUSY;
			else if (!(nlmsg_flags & NLM_F_EXCL))
				err = 0;
		} else if (err == -ENOTEMPTY) {
			/* ENOTEMPTY reports overlapping between this element
			 * and an existing one.
			 */
			err = -EEXIST;
		}
		goto err_element_clash;
	}
+13 −0
Original line number Diff line number Diff line
@@ -28,6 +28,10 @@ static void nft_fwd_netdev_eval(const struct nft_expr *expr,
	struct nft_fwd_netdev *priv = nft_expr_priv(expr);
	int oif = regs->data[priv->sreg_dev];

	/* These are used by ifb only. */
	pkt->skb->tc_redirected = 1;
	pkt->skb->tc_from_ingress = 1;

	nf_fwd_netdev_egress(pkt, oif);
	regs->verdict.code = NF_STOLEN;
}
@@ -190,6 +194,13 @@ nla_put_failure:
	return -1;
}

static int nft_fwd_validate(const struct nft_ctx *ctx,
			    const struct nft_expr *expr,
			    const struct nft_data **data)
{
	return nft_chain_validate_hooks(ctx->chain, (1 << NF_NETDEV_INGRESS));
}

static struct nft_expr_type nft_fwd_netdev_type;
static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = {
	.type		= &nft_fwd_netdev_type,
@@ -197,6 +208,7 @@ static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = {
	.eval		= nft_fwd_neigh_eval,
	.init		= nft_fwd_neigh_init,
	.dump		= nft_fwd_neigh_dump,
	.validate	= nft_fwd_validate,
};

static const struct nft_expr_ops nft_fwd_netdev_ops = {
@@ -205,6 +217,7 @@ static const struct nft_expr_ops nft_fwd_netdev_ops = {
	.eval		= nft_fwd_netdev_eval,
	.init		= nft_fwd_netdev_init,
	.dump		= nft_fwd_netdev_dump,
	.validate	= nft_fwd_validate,
	.offload	= nft_fwd_netdev_offload,
};

+27 −7
Original line number Diff line number Diff line
@@ -1098,21 +1098,41 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
	struct nft_pipapo_field *f;
	int i, bsize_max, err = 0;

	dup = pipapo_get(net, set, start, genmask);
	if (PTR_ERR(dup) == -ENOENT) {
		if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) {
	if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
		end = (const u8 *)nft_set_ext_key_end(ext)->data;
			dup = pipapo_get(net, set, end, nft_genmask_next(net));
		} else {
	else
		end = start;

	dup = pipapo_get(net, set, start, genmask);
	if (!IS_ERR(dup)) {
		/* Check if we already have the same exact entry */
		const struct nft_data *dup_key, *dup_end;

		dup_key = nft_set_ext_key(&dup->ext);
		if (nft_set_ext_exists(&dup->ext, NFT_SET_EXT_KEY_END))
			dup_end = nft_set_ext_key_end(&dup->ext);
		else
			dup_end = dup_key;

		if (!memcmp(start, dup_key->data, sizeof(*dup_key->data)) &&
		    !memcmp(end, dup_end->data, sizeof(*dup_end->data))) {
			*ext2 = &dup->ext;
			return -EEXIST;
		}

		return -ENOTEMPTY;
	}

	if (PTR_ERR(dup) == -ENOENT) {
		/* Look for partially overlapping entries */
		dup = pipapo_get(net, set, end, nft_genmask_next(net));
	}

	if (PTR_ERR(dup) != -ENOENT) {
		if (IS_ERR(dup))
			return PTR_ERR(dup);
		*ext2 = &dup->ext;
		return -EEXIST;
		return -ENOTEMPTY;
	}

	/* Validate */
+78 −9
Original line number Diff line number Diff line
@@ -33,6 +33,11 @@ static bool nft_rbtree_interval_end(const struct nft_rbtree_elem *rbe)
	       (*nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END);
}

static bool nft_rbtree_interval_start(const struct nft_rbtree_elem *rbe)
{
	return !nft_rbtree_interval_end(rbe);
}

static bool nft_rbtree_equal(const struct nft_set *set, const void *this,
			     const struct nft_rbtree_elem *interval)
{
@@ -64,7 +69,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
			if (interval &&
			    nft_rbtree_equal(set, this, interval) &&
			    nft_rbtree_interval_end(rbe) &&
			    !nft_rbtree_interval_end(interval))
			    nft_rbtree_interval_start(interval))
				continue;
			interval = rbe;
		} else if (d > 0)
@@ -89,7 +94,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set

	if (set->flags & NFT_SET_INTERVAL && interval != NULL &&
	    nft_set_elem_active(&interval->ext, genmask) &&
	    !nft_rbtree_interval_end(interval)) {
	    nft_rbtree_interval_start(interval)) {
		*ext = &interval->ext;
		return true;
	}
@@ -208,8 +213,43 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
	u8 genmask = nft_genmask_next(net);
	struct nft_rbtree_elem *rbe;
	struct rb_node *parent, **p;
	bool overlap = false;
	int d;

	/* Detect overlaps as we descend the tree. Set the flag in these cases:
	 *
	 * a1. |__ _ _?  >|__ _ _  (insert start after existing start)
	 * a2. _ _ __>|  ?_ _ __|  (insert end before existing end)
	 * a3. _ _ ___|  ?_ _ _>|  (insert end after existing end)
	 * a4. >|__ _ _   _ _ __|  (insert start before existing end)
	 *
	 * and clear it later on, as we eventually reach the points indicated by
	 * '?' above, in the cases described below. We'll always meet these
	 * later, locally, due to tree ordering, and overlaps for the intervals
	 * that are the closest together are always evaluated last.
	 *
	 * b1. |__ _ _!  >|__ _ _  (insert start after existing end)
	 * b2. _ _ __>|  !_ _ __|  (insert end before existing start)
	 * b3. !_____>|            (insert end after existing start)
	 *
	 * Case a4. resolves to b1.:
	 * - if the inserted start element is the leftmost, because the '0'
	 *   element in the tree serves as end element
	 * - otherwise, if an existing end is found. Note that end elements are
	 *   always inserted after corresponding start elements.
	 *
	 * For a new, rightmost pair of elements, we'll hit cases b1. and b3.,
	 * in that order.
	 *
	 * The flag is also cleared in two special cases:
	 *
	 * b4. |__ _ _!|<_ _ _   (insert start right before existing end)
	 * b5. |__ _ >|!__ _ _   (insert end right after existing start)
	 *
	 * which always happen as last step and imply that no further
	 * overlapping is possible.
	 */

	parent = NULL;
	p = &priv->root.rb_node;
	while (*p != NULL) {
@@ -218,17 +258,42 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
		d = memcmp(nft_set_ext_key(&rbe->ext),
			   nft_set_ext_key(&new->ext),
			   set->klen);
		if (d < 0)
		if (d < 0) {
			p = &parent->rb_left;
		else if (d > 0)

			if (nft_rbtree_interval_start(new)) {
				overlap = nft_rbtree_interval_start(rbe) &&
					  nft_set_elem_active(&rbe->ext,
							      genmask);
			} else {
				overlap = nft_rbtree_interval_end(rbe) &&
					  nft_set_elem_active(&rbe->ext,
							      genmask);
			}
		} else if (d > 0) {
			p = &parent->rb_right;
		else {

			if (nft_rbtree_interval_end(new)) {
				overlap = nft_rbtree_interval_end(rbe) &&
					  nft_set_elem_active(&rbe->ext,
							      genmask);
			} else if (nft_rbtree_interval_end(rbe) &&
				   nft_set_elem_active(&rbe->ext, genmask)) {
				overlap = true;
			}
		} else {
			if (nft_rbtree_interval_end(rbe) &&
			    !nft_rbtree_interval_end(new)) {
			    nft_rbtree_interval_start(new)) {
				p = &parent->rb_left;
			} else if (!nft_rbtree_interval_end(rbe) &&

				if (nft_set_elem_active(&rbe->ext, genmask))
					overlap = false;
			} else if (nft_rbtree_interval_start(rbe) &&
				   nft_rbtree_interval_end(new)) {
				p = &parent->rb_right;

				if (nft_set_elem_active(&rbe->ext, genmask))
					overlap = false;
			} else if (nft_set_elem_active(&rbe->ext, genmask)) {
				*ext = &rbe->ext;
				return -EEXIST;
@@ -237,6 +302,10 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
			}
		}
	}

	if (overlap)
		return -ENOTEMPTY;

	rb_link_node_rcu(&new->node, parent, p);
	rb_insert_color(&new->node, &priv->root);
	return 0;
@@ -317,10 +386,10 @@ static void *nft_rbtree_deactivate(const struct net *net,
			parent = parent->rb_right;
		else {
			if (nft_rbtree_interval_end(rbe) &&
			    !nft_rbtree_interval_end(this)) {
			    nft_rbtree_interval_start(this)) {
				parent = parent->rb_left;
				continue;
			} else if (!nft_rbtree_interval_end(rbe) &&
			} else if (nft_rbtree_interval_start(rbe) &&
				   nft_rbtree_interval_end(this)) {
				parent = parent->rb_right;
				continue;
+5 −1
Original line number Diff line number Diff line
@@ -3,6 +3,10 @@

TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \
	conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh \
	nft_concat_range.sh
	nft_concat_range.sh \
	nft_queue.sh

LDLIBS = -lmnl
TEST_GEN_FILES =  nf-queue

include ../lib.mk
Loading