[PATCH] udp: resubmit encapsulation packets on all multicast listeners

Mariano Baragiola <[email protected]>
Newsgroups org.kernel.vger.netdev
Message-ID <[email protected]>
UDP encapsulation handlers (FOU/GUE and similar) return a positive
protocol number from udp_queue_rcv_skb()/udpv6_queue_rcv_skb() when the
UDP header has been consumed and the packet must be resubmitted to the
IP protocol handler. Unicast paths already propagate that return value.

Multicast delivery called consume_skb() on every positive return, so the
inner packet was dropped instead of being reinjected.

Commit 3cb8d4b9bfeb ("udp: fix encapsulation packet resubmit in multicast
deliver") fixed only the primary ("first") socket path on net-next and is
not yet in net. Secondary listeners still clone the skb and drop it on a
positive return, and net itself still drops the primary socket path too.

Resubmit secondary clones inline via ip_protocol_deliver_rcu() /
ip6_protocol_deliver_rcu() (matching the GSO segment path in
udp_queue_rcv_skb()/udpv6_queue_rcv_skb()), and propagate the primary
socket return value with the same IPv4/IPv6 sign convention as the
unicast helpers.

Fixes: ca065d0cf80f ("udp: no longer use SLAB_DESTROY_BY_RCU")
Signed-off-by: Mariano Baragiola <[email protected]>
---
 net/ipv4/udp.c | 16 ++++++++++++----
 net/ipv6/udp.c | 16 ++++++++++++----
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 70f6cbd4ef73..d6a17b0462b6 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2475,6 +2475,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
 	struct udp_hslot *hslot;
 	struct sk_buff *nskb;
 	bool use_hash2;
+	int ret;
 
 	hash2_any = 0;
 	hash2 = 0;
@@ -2508,8 +2509,13 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
 			__UDP_INC_STATS(net, UDP_MIB_INERRORS);
 			continue;
 		}
-		if (udp_queue_rcv_skb(sk, nskb) > 0)
-			consume_skb(nskb);
+		/* >0 means the encap handler wants IP-level resubmit. Do that
+		 * inline for secondary listeners; only the first socket can
+		 * propagate the protocol number to the caller.
+		 */
+		ret = udp_queue_rcv_skb(sk, nskb);
+		if (ret > 0)
+			ip_protocol_deliver_rcu(net, nskb, ret);
 	}
 
 	/* Also lookup *:port if we are using hash2 and haven't done so yet. */
@@ -2519,8 +2525,10 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
 	}
 
 	if (first) {
-		if (udp_queue_rcv_skb(first, skb) > 0)
-			consume_skb(skb);
+		ret = udp_queue_rcv_skb(first, skb);
+		/* Match udp_unicast_rcv_skb(): return -protocol for IPv4. */
+		if (ret > 0)
+			return -ret;
 	} else {
 		kfree_skb(skb);
 		__UDP_INC_STATS(net, UDP_MIB_IGNOREDMULTI);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 15e032194ecc..65dd944211e9 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -949,6 +949,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
 	struct udp_hslot *hslot;
 	struct sk_buff *nskb;
 	bool use_hash2;
+	int ret;
 
 	hash2_any = 0;
 	hash2 = 0;
@@ -987,8 +988,13 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
 			continue;
 		}
 
-		if (udpv6_queue_rcv_skb(sk, nskb) > 0)
-			consume_skb(nskb);
+		/* >0 means the encap handler wants IP-level resubmit. Do that
+		 * inline for secondary listeners; only the first socket can
+		 * propagate the nexthdr to the caller.
+		 */
+		ret = udpv6_queue_rcv_skb(sk, nskb);
+		if (ret > 0)
+			ip6_protocol_deliver_rcu(net, nskb, ret, true);
 	}
 
 	/* Also lookup *:port if we are using hash2 and haven't done so yet. */
@@ -998,8 +1004,10 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
 	}
 
 	if (first) {
-		if (udpv6_queue_rcv_skb(first, skb) > 0)
-			consume_skb(skb);
+		ret = udpv6_queue_rcv_skb(first, skb);
+		/* Match udp6_unicast_rcv_skb(): return protocol for IPv6. */
+		if (ret > 0)
+			return ret;
 	} else {
 		kfree_skb(skb);
 		__UDP6_INC_STATS(net, UDP_MIB_IGNOREDMULTI);
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.