Skip to content

Commit 000ac44

Browse files
Paolo Abenidavem330
authored andcommitted
udp: fixup csum for GSO receive slow path
When UDP packets generated locally by a socket with UDP_SEGMENT traverse the following path: UDP tunnel(xmit) -> veth (segmentation) -> veth (gro) -> UDP tunnel (rx) -> UDP socket (no UDP_GRO) ip_summed will be set to CHECKSUM_PARTIAL at creation time and such checksum mode will be preserved in the above path up to the UDP tunnel receive code where we have: __iptunnel_pull_header() -> skb_pull_rcsum() -> skb_postpull_rcsum() -> __skb_postpull_rcsum() The latter will convert the skb to CHECKSUM_NONE. The UDP GSO packet will be later segmented as part of the rx socket receive operation, and will present a CHECKSUM_NONE after segmentation. Additionally the segmented packets UDP CB still refers to the original GSO packet len. Overall that causes unexpected/wrong csum validation errors later in the UDP receive path. We could possibly address the issue with some additional checks and csum mangling in the UDP tunnel code. Since the issue affects only this UDP receive slow path, let's set a suitable csum status there. Note that SKB_GSO_UDP_L4 or SKB_GSO_FRAGLIST packets lacking an UDP encapsulation present a valid checksum when landing to udp_queue_rcv_skb(), as the UDP checksum has been validated by the GRO engine. v2 -> v3: - even more verbose commit message and comments v1 -> v2: - restrict the csum update to the packets strictly needing them - hopefully clarify the commit message and code comments Signed-off-by: Paolo Abeni <[email protected]> Reviewed-by: Willem de Bruijn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent dc5fa20 commit 000ac44

File tree

3 files changed

+26
-0
lines changed

3 files changed

+26
-0
lines changed

include/net/udp.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,29 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
515515
return segs;
516516
}
517517

518+
static inline void udp_post_segment_fix_csum(struct sk_buff *skb)
519+
{
520+
/* UDP-lite can't land here - no GRO */
521+
WARN_ON_ONCE(UDP_SKB_CB(skb)->partial_cov);
522+
523+
/* UDP packets generated with UDP_SEGMENT and traversing:
524+
*
525+
* UDP tunnel(xmit) -> veth (segmentation) -> veth (gro) -> UDP tunnel (rx)
526+
*
527+
* can reach an UDP socket with CHECKSUM_NONE, because
528+
* __iptunnel_pull_header() converts CHECKSUM_PARTIAL into NONE.
529+
* SKB_GSO_UDP_L4 or SKB_GSO_FRAGLIST packets with no UDP tunnel will
530+
* have a valid checksum, as the GRO engine validates the UDP csum
531+
* before the aggregation and nobody strips such info in between.
532+
* Instead of adding another check in the tunnel fastpath, we can force
533+
* a valid csum after the segmentation.
534+
* Additionally fixup the UDP CB.
535+
*/
536+
UDP_SKB_CB(skb)->cscov = skb->len;
537+
if (skb->ip_summed == CHECKSUM_NONE && !skb->csum_valid)
538+
skb->csum_valid = 1;
539+
}
540+
518541
#ifdef CONFIG_BPF_SYSCALL
519542
struct sk_psock;
520543
struct proto *udp_bpf_get_proto(struct sock *sk, struct sk_psock *psock);

net/ipv4/udp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,6 +2178,8 @@ static int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
21782178
segs = udp_rcv_segment(sk, skb, true);
21792179
skb_list_walk_safe(segs, skb, next) {
21802180
__skb_pull(skb, skb_transport_offset(skb));
2181+
2182+
udp_post_segment_fix_csum(skb);
21812183
ret = udp_queue_rcv_one_skb(sk, skb);
21822184
if (ret > 0)
21832185
ip_protocol_deliver_rcu(dev_net(skb->dev), skb, ret);

net/ipv6/udp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ static int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
749749
skb_list_walk_safe(segs, skb, next) {
750750
__skb_pull(skb, skb_transport_offset(skb));
751751

752+
udp_post_segment_fix_csum(skb);
752753
ret = udpv6_queue_rcv_one_skb(sk, skb);
753754
if (ret > 0)
754755
ip6_protocol_deliver_rcu(dev_net(skb->dev), skb, ret,

0 commit comments

Comments
 (0)