diff mbox series

[net] udplite: fix partial checksum initialization

Message ID 1518715123-5990-1-git-send-email-alexey.kodanev@oracle.com
State Accepted, archived
Delegated to: David Miller
Headers show
Series [net] udplite: fix partial checksum initialization | expand

Commit Message

Alexey Kodanev Feb. 15, 2018, 5:18 p.m. UTC
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>
---

Alternatively, we could modify skb_checksum_init_zero_check() because
it is used only in udp4_csum_init() and udp6_csum_init() as well as
introducing a new 'cscov' parameter in __skb_checksum_validate_complete()
and calling __skb_checksum_complete_head() if 'cscov' not equals zero.
But these changes would touch general code in skbuff.h

 include/net/udplite.h   | 1 +
 net/ipv4/udp.c          | 5 +++++
 net/ipv6/ip6_checksum.c | 5 +++++
 3 files changed, 11 insertions(+)

Comments

David Miller Feb. 16, 2018, 8:58 p.m. UTC | #1
From: Alexey Kodanev <alexey.kodanev@oracle.com>
Date: Thu, 15 Feb 2018 20:18:43 +0300

> 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>

Applied and queued up for -stable, thanks Alexey.
diff mbox series

Patch

diff --git a/include/net/udplite.h b/include/net/udplite.h
index 81bdbf9..9185e45 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)
 		UDP_SKB_CB(skb)->cscov = cscov;
 		if (skb->ip_summed == CHECKSUM_COMPLETE)
 			skb->ip_summed = CHECKSUM_NONE;
+		skb->csum_valid = 0;
         }
 
 	return 0;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index bfaefe5..e5ef7c3 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,
 		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;
+		}
 	}
 
 	/* 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 ec43d18..547515e 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)
 		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)