diff options
author | Alexey Kodanev <alexey.kodanev@oracle.com> | 2018-02-15 12:18:43 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-02-16 15:57:42 -0500 |
commit | 15f35d49c93f4fa9875235e7bf3e3783d2dd7a1b (patch) | |
tree | 2d100c2307313118f62ae83634a3f23046fa1a90 | |
parent | da27988766e338e4a4fe198170497c0920395d4c (diff) |
udplite: fix partial checksum initialization
Since UDP-Lite is always using checksum, the following path is
triggered when calculating pseudo header for it:
udp4_csum_init() or udp6_csum_init()
skb_checksum_init_zero_check()
__skb_checksum_validate_complete()
The problem can appear if skb->len is less than CHECKSUM_BREAK. In
this particular case __skb_checksum_validate_complete() also invokes
__skb_checksum_complete(skb). If UDP-Lite is using partial checksum
that covers only part of a packet, the function will return bad
checksum and the packet will be dropped.
It can be fixed if we skip skb_checksum_init_zero_check() and only
set the required pseudo header checksum for UDP-Lite with partial
checksum before udp4_csum_init()/udp6_csum_init() functions return.
Fixes: ed70fcfcee95 ("net: Call skb_checksum_init in IPv4")
Fixes: e4f45b7f40bd ("net: Call skb_checksum_init in IPv6")
Signed-off-by: Alexey Kodanev <alexey.kodanev@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/udplite.h | 1 | ||||
-rw-r--r-- | net/ipv4/udp.c | 5 | ||||
-rw-r--r-- | net/ipv6/ip6_checksum.c | 5 |
3 files changed, 11 insertions, 0 deletions
diff --git a/include/net/udplite.h b/include/net/udplite.h index 81bdbf97319b..9185e45b997f 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h | |||
@@ -64,6 +64,7 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh) | |||
64 | UDP_SKB_CB(skb)->cscov = cscov; | 64 | UDP_SKB_CB(skb)->cscov = cscov; |
65 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 65 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
66 | skb->ip_summed = CHECKSUM_NONE; | 66 | skb->ip_summed = CHECKSUM_NONE; |
67 | skb->csum_valid = 0; | ||
67 | } | 68 | } |
68 | 69 | ||
69 | return 0; | 70 | return 0; |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index bfaefe560b5c..e5ef7c38c934 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -2024,6 +2024,11 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, | |||
2024 | err = udplite_checksum_init(skb, uh); | 2024 | err = udplite_checksum_init(skb, uh); |
2025 | if (err) | 2025 | if (err) |
2026 | return err; | 2026 | return err; |
2027 | |||
2028 | if (UDP_SKB_CB(skb)->partial_cov) { | ||
2029 | skb->csum = inet_compute_pseudo(skb, proto); | ||
2030 | return 0; | ||
2031 | } | ||
2027 | } | 2032 | } |
2028 | 2033 | ||
2029 | /* Note, we are only interested in != 0 or == 0, thus the | 2034 | /* Note, we are only interested in != 0 or == 0, thus the |
diff --git a/net/ipv6/ip6_checksum.c b/net/ipv6/ip6_checksum.c index ec43d18b5ff9..547515e8450a 100644 --- a/net/ipv6/ip6_checksum.c +++ b/net/ipv6/ip6_checksum.c | |||
@@ -73,6 +73,11 @@ int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) | |||
73 | err = udplite_checksum_init(skb, uh); | 73 | err = udplite_checksum_init(skb, uh); |
74 | if (err) | 74 | if (err) |
75 | return err; | 75 | return err; |
76 | |||
77 | if (UDP_SKB_CB(skb)->partial_cov) { | ||
78 | skb->csum = ip6_compute_pseudo(skb, proto); | ||
79 | return 0; | ||
80 | } | ||
76 | } | 81 | } |
77 | 82 | ||
78 | /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels) | 83 | /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels) |