diff --git a/include/net/udp.h b/include/net/udp.h index 8bd83b044ecd..9289b6425032 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -175,8 +175,7 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup); struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, - netdev_features_t features, - __sum16 check); + netdev_features_t features); static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb) { diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index c1afcd2f1a76..92c182e99ddc 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -188,8 +188,7 @@ out_unlock: EXPORT_SYMBOL(skb_udp_tunnel_segment); struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, - netdev_features_t features, - __sum16 check) + netdev_features_t features) { struct sock *sk = gso_skb->sk; unsigned int sum_truesize = 0; @@ -197,6 +196,8 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, unsigned int hdrlen; struct udphdr *uh; unsigned int mss; + __sum16 check; + __be16 newlen; mss = skb_shinfo(gso_skb)->gso_size; if (gso_skb->len <= sizeof(*uh) + mss) @@ -215,17 +216,25 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, return segs; } + uh = udp_hdr(segs); + + /* compute checksum adjustment based on old length versus new */ + newlen = htons(sizeof(*uh) + mss); + check = csum16_add(csum16_sub(uh->check, uh->len), newlen); + for (seg = segs; seg; seg = seg->next) { uh = udp_hdr(seg); - uh->len = htons(seg->len - hdrlen); - uh->check = check; /* last packet can be partial gso_size */ - if (!seg->next) - csum_replace2(&uh->check, htons(mss), - htons(seg->len - hdrlen - sizeof(*uh))); + if (!seg->next) { + newlen = htons(seg->len - hdrlen); + check = csum16_add(csum16_sub(uh->check, uh->len), + newlen); + } + + uh->len = newlen; + uh->check = check; - uh->check = ~uh->check; seg->destructor = sock_wfree; seg->sk = sk; sum_truesize += seg->truesize; @@ -240,15 +249,10 @@ EXPORT_SYMBOL_GPL(__udp_gso_segment); static struct sk_buff *__udp4_gso_segment(struct sk_buff *gso_skb, netdev_features_t features) { - const struct iphdr *iph = ip_hdr(gso_skb); - unsigned int mss = skb_shinfo(gso_skb)->gso_size; - if (!can_checksum_protocol(features, htons(ETH_P_IP))) return ERR_PTR(-EIO); - return __udp_gso_segment(gso_skb, features, - udp_v4_check(sizeof(struct udphdr) + mss, - iph->saddr, iph->daddr, 0)); + return __udp_gso_segment(gso_skb, features); } static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index dea03ec09715..61e34f1d2fa2 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -20,15 +20,10 @@ static struct sk_buff *__udp6_gso_segment(struct sk_buff *gso_skb, netdev_features_t features) { - const struct ipv6hdr *ip6h = ipv6_hdr(gso_skb); - unsigned int mss = skb_shinfo(gso_skb)->gso_size; - if (!can_checksum_protocol(features, htons(ETH_P_IPV6))) return ERR_PTR(-EIO); - return __udp_gso_segment(gso_skb, features, - udp_v6_check(sizeof(struct udphdr) + mss, - &ip6h->saddr, &ip6h->daddr, 0)); + return __udp_gso_segment(gso_skb, features); } static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,