| From: Alexey Kodanev <alexey.kodanev@oracle.com> |
| Date: Thu, 15 Feb 2018 20:18:43 +0300 |
| Subject: udplite: fix partial checksum initialization |
| |
| commit 15f35d49c93f4fa9875235e7bf3e3783d2dd7a1b upstream. |
| |
| 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> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| include/net/udplite.h | 1 + |
| net/ipv4/udp.c | 5 +++++ |
| net/ipv6/ip6_checksum.c | 5 +++++ |
| 3 files changed, 11 insertions(+) |
| |
| --- a/include/net/udplite.h |
| +++ b/include/net/udplite.h |
| @@ -61,6 +61,7 @@ static inline int udplite_checksum_init( |
| UDP_SKB_CB(skb)->cscov = cscov; |
| if (skb->ip_summed == CHECKSUM_COMPLETE) |
| skb->ip_summed = CHECKSUM_NONE; |
| + skb->csum_valid = 0; |
| } |
| |
| return 0; |
| --- a/net/ipv4/udp.c |
| +++ b/net/ipv4/udp.c |
| @@ -1729,6 +1729,11 @@ static inline int udp4_csum_init(struct |
| err = udplite_checksum_init(skb, uh); |
| if (err) |
| return err; |
| + |
| + if (UDP_SKB_CB(skb)->partial_cov) { |
| + skb->csum = inet_compute_pseudo(skb, proto); |
| + return 0; |
| + } |
| } |
| |
| return skb_checksum_init_zero_check(skb, proto, uh->check, |
| --- a/net/ipv6/ip6_checksum.c |
| +++ b/net/ipv6/ip6_checksum.c |
| @@ -73,6 +73,11 @@ int udp6_csum_init(struct sk_buff *skb, |
| err = udplite_checksum_init(skb, uh); |
| if (err) |
| return err; |
| + |
| + if (UDP_SKB_CB(skb)->partial_cov) { |
| + skb->csum = ip6_compute_pseudo(skb, proto); |
| + return 0; |
| + } |
| } |
| |
| /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels) |