Commit e889eb17 authored by Paolo Abeni's avatar Paolo Abeni
Browse files
Pablo Neira Ayuso says:

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

The following patchset contains Netfilter fixes for net:

Patch #1 syzbot reports that nf_reinject() could be called without
         rcu_read_lock() when flushing pending packets at nfnetlink
         queue removal, from Eric Dumazet.

Patch #2 flushes ipset list:set when canceling garbage collection to
         reference to other lists to fix a race, from Jozsef Kadlecsik.

Patch #3 restores q-in-q matching with nft_payload by reverting
         f6ae9f12 ("netfilter: nft_payload: add C-VLAN support").

Patch #4 fixes vlan mangling in skbuff when vlan offload is present
         in skbuff, without this patch nft_payload corrupts packets
         in this case.

Patch #5 fixes possible nul-deref in tproxy no IP address is found in
         netdevice, reported by syzbot and patch from Florian Westphal.

Patch #6 removes a superfluous restriction which prevents loose fib
         lookups from input and forward hooks, from Eric Garver.

My assessment is that patches #1, #2 and #5 address possible kernel
crash, anything else in this batch fixes broken features.

netfilter pull request 24-05-29

* tag 'nf-24-05-29' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nft_fib: allow from forward/input without iif selector
  netfilter: tproxy: bail out if IP has been disabled on the device
  netfilter: nft_payload: skbuff vlan metadata mangle support
  netfilter: nft_payload: restore vlan q-in-q match support
  netfilter: ipset: Add list flush to cancel_gc
  netfilter: nfnetlink_queue: acquire rcu_read_lock() in instance_destroy_rcu()
====================

Link: https://lore.kernel.org/r/20240528225519.1155786-1-pablo@netfilter.org


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents 2dc8b1e7 e8ded22e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -58,6 +58,8 @@ __be32 nf_tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr)

	laddr = 0;
	indev = __in_dev_get_rcu(skb->dev);
	if (!indev)
		return daddr;

	in_dev_for_each_ifa_rcu(ifa, indev) {
		if (ifa->ifa_flags & IFA_F_SECONDARY)
+3 −0
Original line number Diff line number Diff line
@@ -549,6 +549,9 @@ list_set_cancel_gc(struct ip_set *set)

	if (SET_WITH_TIMEOUT(set))
		timer_shutdown_sync(&map->gc);

	/* Flush list to drop references to other ipsets */
	list_set_flush(set);
}

static const struct ip_set_type_variant set_variant = {
+2 −0
Original line number Diff line number Diff line
@@ -169,7 +169,9 @@ instance_destroy_rcu(struct rcu_head *head)
	struct nfqnl_instance *inst = container_of(head, struct nfqnl_instance,
						   rcu);

	rcu_read_lock();
	nfqnl_flush(inst, NULL, 0);
	rcu_read_unlock();
	kfree(inst);
	module_put(THIS_MODULE);
}
+3 −5
Original line number Diff line number Diff line
@@ -35,11 +35,9 @@ int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
	switch (priv->result) {
	case NFT_FIB_RESULT_OIF:
	case NFT_FIB_RESULT_OIFNAME:
		hooks = (1 << NF_INET_PRE_ROUTING);
		if (priv->flags & NFTA_FIB_F_IIF) {
			hooks |= (1 << NF_INET_LOCAL_IN) |
		hooks = (1 << NF_INET_PRE_ROUTING) |
			(1 << NF_INET_LOCAL_IN) |
			(1 << NF_INET_FORWARD);
		}
		break;
	case NFT_FIB_RESULT_ADDRTYPE:
		if (priv->flags & NFTA_FIB_F_IIF)
+72 −23
Original line number Diff line number Diff line
@@ -45,36 +45,27 @@ nft_payload_copy_vlan(u32 *d, const struct sk_buff *skb, u8 offset, u8 len)
	int mac_off = skb_mac_header(skb) - skb->data;
	u8 *vlanh, *dst_u8 = (u8 *) d;
	struct vlan_ethhdr veth;
	u8 vlan_hlen = 0;

	if ((skb->protocol == htons(ETH_P_8021AD) ||
	     skb->protocol == htons(ETH_P_8021Q)) &&
	    offset >= VLAN_ETH_HLEN && offset < VLAN_ETH_HLEN + VLAN_HLEN)
		vlan_hlen += VLAN_HLEN;

	vlanh = (u8 *) &veth;
	if (offset < VLAN_ETH_HLEN + vlan_hlen) {
	if (offset < VLAN_ETH_HLEN) {
		u8 ethlen = len;

		if (vlan_hlen &&
		    skb_copy_bits(skb, mac_off, &veth, VLAN_ETH_HLEN) < 0)
			return false;
		else if (!nft_payload_rebuild_vlan_hdr(skb, mac_off, &veth))
		if (!nft_payload_rebuild_vlan_hdr(skb, mac_off, &veth))
			return false;

		if (offset + len > VLAN_ETH_HLEN + vlan_hlen)
			ethlen -= offset + len - VLAN_ETH_HLEN - vlan_hlen;
		if (offset + len > VLAN_ETH_HLEN)
			ethlen -= offset + len - VLAN_ETH_HLEN;

		memcpy(dst_u8, vlanh + offset - vlan_hlen, ethlen);
		memcpy(dst_u8, vlanh + offset, ethlen);

		len -= ethlen;
		if (len == 0)
			return true;

		dst_u8 += ethlen;
		offset = ETH_HLEN + vlan_hlen;
		offset = ETH_HLEN;
	} else {
		offset -= VLAN_HLEN + vlan_hlen;
		offset -= VLAN_HLEN;
	}

	return skb_copy_bits(skb, offset + mac_off, dst_u8, len) == 0;
@@ -154,12 +145,12 @@ int nft_payload_inner_offset(const struct nft_pktinfo *pkt)
	return pkt->inneroff;
}

static bool nft_payload_need_vlan_copy(const struct nft_payload *priv)
static bool nft_payload_need_vlan_adjust(u32 offset, u32 len)
{
	unsigned int len = priv->offset + priv->len;
	unsigned int boundary = offset + len;

	/* data past ether src/dst requested, copy needed */
	if (len > offsetof(struct ethhdr, h_proto))
	if (boundary > offsetof(struct ethhdr, h_proto))
		return true;

	return false;
@@ -183,7 +174,7 @@ void nft_payload_eval(const struct nft_expr *expr,
			goto err;

		if (skb_vlan_tag_present(skb) &&
		    nft_payload_need_vlan_copy(priv)) {
		    nft_payload_need_vlan_adjust(priv->offset, priv->len)) {
			if (!nft_payload_copy_vlan(dest, skb,
						   priv->offset, priv->len))
				goto err;
@@ -810,21 +801,79 @@ struct nft_payload_set {
	u8			csum_flags;
};

/* This is not struct vlan_hdr. */
struct nft_payload_vlan_hdr {
	__be16			h_vlan_proto;
	__be16			h_vlan_TCI;
};

static bool
nft_payload_set_vlan(const u32 *src, struct sk_buff *skb, u8 offset, u8 len,
		     int *vlan_hlen)
{
	struct nft_payload_vlan_hdr *vlanh;
	__be16 vlan_proto;
	u16 vlan_tci;

	if (offset >= offsetof(struct vlan_ethhdr, h_vlan_encapsulated_proto)) {
		*vlan_hlen = VLAN_HLEN;
		return true;
	}

	switch (offset) {
	case offsetof(struct vlan_ethhdr, h_vlan_proto):
		if (len == 2) {
			vlan_proto = nft_reg_load_be16(src);
			skb->vlan_proto = vlan_proto;
		} else if (len == 4) {
			vlanh = (struct nft_payload_vlan_hdr *)src;
			__vlan_hwaccel_put_tag(skb, vlanh->h_vlan_proto,
					       ntohs(vlanh->h_vlan_TCI));
		} else {
			return false;
		}
		break;
	case offsetof(struct vlan_ethhdr, h_vlan_TCI):
		if (len != 2)
			return false;

		vlan_tci = ntohs(nft_reg_load_be16(src));
		skb->vlan_tci = vlan_tci;
		break;
	default:
		return false;
	}

	return true;
}

static void nft_payload_set_eval(const struct nft_expr *expr,
				 struct nft_regs *regs,
				 const struct nft_pktinfo *pkt)
{
	const struct nft_payload_set *priv = nft_expr_priv(expr);
	struct sk_buff *skb = pkt->skb;
	const u32 *src = &regs->data[priv->sreg];
	int offset, csum_offset;
	int offset, csum_offset, vlan_hlen = 0;
	struct sk_buff *skb = pkt->skb;
	__wsum fsum, tsum;

	switch (priv->base) {
	case NFT_PAYLOAD_LL_HEADER:
		if (!skb_mac_header_was_set(skb))
			goto err;
		offset = skb_mac_header(skb) - skb->data;

		if (skb_vlan_tag_present(skb) &&
		    nft_payload_need_vlan_adjust(priv->offset, priv->len)) {
			if (!nft_payload_set_vlan(src, skb,
						  priv->offset, priv->len,
						  &vlan_hlen))
				goto err;

			if (!vlan_hlen)
				return;
		}

		offset = skb_mac_header(skb) - skb->data - vlan_hlen;
		break;
	case NFT_PAYLOAD_NETWORK_HEADER:
		offset = skb_network_offset(skb);