|
@@ -40,13 +40,19 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
|
|
__be16 protocol = skb->protocol;
|
|
__be16 protocol = skb->protocol;
|
|
u16 mac_len = skb->mac_len;
|
|
u16 mac_len = skb->mac_len;
|
|
int udp_offset, outer_hlen;
|
|
int udp_offset, outer_hlen;
|
|
- u32 partial;
|
|
|
|
|
|
+ __wsum partial;
|
|
|
|
|
|
if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
|
|
if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
- /* adjust partial header checksum to negate old length */
|
|
|
|
- partial = (__force u32)uh->check + (__force u16)~uh->len;
|
|
|
|
|
|
+ /* Adjust partial header checksum to negate old length.
|
|
|
|
+ * We cannot rely on the value contained in uh->len as it is
|
|
|
|
+ * possible that the actual value exceeds the boundaries of the
|
|
|
|
+ * 16 bit length field due to the header being added outside of an
|
|
|
|
+ * IP or IPv6 frame that was already limited to 64K - 1.
|
|
|
|
+ */
|
|
|
|
+ partial = csum_sub(csum_unfold(uh->check),
|
|
|
|
+ (__force __wsum)htonl(skb->len));
|
|
|
|
|
|
/* setup inner skb. */
|
|
/* setup inner skb. */
|
|
skb->encapsulation = 0;
|
|
skb->encapsulation = 0;
|
|
@@ -119,8 +125,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
|
|
if (!need_csum)
|
|
if (!need_csum)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- uh->check = ~csum_fold((__force __wsum)
|
|
|
|
- ((__force u32)len + partial));
|
|
|
|
|
|
+ uh->check = ~csum_fold(csum_add(partial, (__force __wsum)len));
|
|
|
|
|
|
if (skb->encapsulation || !offload_csum) {
|
|
if (skb->encapsulation || !offload_csum) {
|
|
uh->check = gso_make_checksum(skb, ~uh->check);
|
|
uh->check = gso_make_checksum(skb, ~uh->check);
|