mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 08:45:18 -04:00
udp: fix encapsulation packet resubmit in multicast deliver
When a UDP encapsulation socket (e.g., FOU) receives a multicast
packet, __udp4_lib_mcast_deliver() and __udp6_lib_mcast_deliver()
call consume_skb() when udp_queue_rcv_skb() returns a positive value.
A positive return value from udp_queue_rcv_skb() indicates that the
encap_rcv handler (e.g., fou_udp_recv) has consumed the UDP header
and wants the packet to be resubmitted to the IP protocol handler
for further processing (e.g., as a GRE packet).
The unicast paths handle this correctly by propagating the return
value up to ip_protocol_deliver_rcu() / ip6_protocol_deliver_rcu()
for resubmission. However, the multicast paths destroy the packet
via consume_skb() instead of resubmitting it, causing silent packet
loss.
This affects any UDP encapsulation (FOU, GUE) combined with multicast
destination addresses.
Fix this by returning the value from udp_queue_rcv_skb() when it is
positive, matching the behavior of the corresponding unicast paths.
Note the sign difference between IPv4 and IPv6:
- IPv4: udp_unicast_rcv_skb() returns -ret, and
ip_protocol_deliver_rcu() resubmits when ret < 0
(using -ret as the protocol number).
- IPv6: udp6_unicast_rcv_skb() returns ret, and
ip6_protocol_deliver_rcu() resubmits when ret > 0
(using ret as the nexthdr).
Both mcast paths now follow the same convention as their respective
unicast paths.
Suggested-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Anton Danilov <littlesmilingcloud@gmail.com>
Assisted-by: Claude:claude-opus-4-6
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/5372ccac062193147e02b991d5328a5c3fa3a85a.1783372173.git.littlesmilingcloud@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
committed by
Paolo Abeni
parent
777f49d336
commit
3cb8d4b9bf
@@ -2476,6 +2476,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;
|
||||
@@ -2520,8 +2521,9 @@ 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);
|
||||
if (ret > 0)
|
||||
return -ret;
|
||||
} else {
|
||||
kfree_skb(skb);
|
||||
__UDP_INC_STATS(net, UDP_MIB_IGNOREDMULTI);
|
||||
|
||||
@@ -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;
|
||||
@@ -998,8 +999,9 @@ 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);
|
||||
if (ret > 0)
|
||||
return ret;
|
||||
} else {
|
||||
kfree_skb(skb);
|
||||
__UDP6_INC_STATS(net, UDP_MIB_IGNOREDMULTI);
|
||||
|
||||
Reference in New Issue
Block a user