]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
udp: Remove partial csum code in RX.
authorKuniyuki Iwashima <kuniyu@google.com>
Wed, 11 Mar 2026 05:19:54 +0000 (05:19 +0000)
committerJakub Kicinski <kuba@kernel.org>
Sat, 14 Mar 2026 01:57:45 +0000 (18:57 -0700)
UDP-Lite supports the partial checksum and the coverage is
stored in the position of the length field of struct udphdr.

In RX paths, udp4_csum_init() / udp6_csum_init() save the value
in UDP_SKB_CB(skb)->cscov and set UDP_SKB_CB(skb)->partial_cov
to 1 if the coverage is not full.

The subsequent processing diverges depending on the value,
but such paths are now dead.

Also, these functions have some code guarded for UDP:

  * udp_unicast_rcv_skb / udp6_unicast_rcv_skb
  * __udp4_lib_rcv() and __udp6_lib_rcv().

Let's remove the partial csum code and the unnecessary
guard for UDP-Lite in RX.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260311052020.1213705-8-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/udp.h
include/net/udplite.h
net/ipv4/udp.c
net/ipv6/udp.c

index 264c10607d2e9c28d477bb194b0170d99236f239..bc275cda9f8ce21177011b3aed40802badbc807c 100644 (file)
 #include <linux/math.h>
 
 /**
- *     struct udp_skb_cb  -  UDP(-Lite) private variables
+ *     struct udp_skb_cb  -  UDP private variables
  *
  *     @header:      private variables used by IPv4/IPv6
- *     @cscov:       checksum coverage length (UDP-Lite only)
- *     @partial_cov: if set indicates partial csum coverage
  */
 struct udp_skb_cb {
        union {
@@ -45,8 +43,6 @@ struct udp_skb_cb {
                struct inet6_skb_parm   h6;
 #endif
        } header;
-       __u16           cscov;
-       __u8            partial_cov;
 };
 #define UDP_SKB_CB(__skb)      ((struct udp_skb_cb *)((__skb)->cb))
 
@@ -216,13 +212,11 @@ extern int sysctl_udp_wmem_min;
 struct sk_buff;
 
 /*
- *     Generic checksumming routines for UDP(-Lite) v4 and v6
+ *     Generic checksumming routines for UDP v4 and v6
  */
 static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb)
 {
-       return (UDP_SKB_CB(skb)->cscov == skb->len ?
-               __skb_checksum_complete(skb) :
-               __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov));
+       return __skb_checksum_complete(skb);
 }
 
 static inline int udp_lib_checksum_complete(struct sk_buff *skb)
@@ -273,7 +267,6 @@ static inline void udp_csum_pull_header(struct sk_buff *skb)
                skb->csum = csum_partial(skb->data, sizeof(struct udphdr),
                                         skb->csum);
        skb_pull_rcsum(skb, sizeof(struct udphdr));
-       UDP_SKB_CB(skb)->cscov -= sizeof(struct udphdr);
 }
 
 typedef struct sock *(*udp_lookup_t)(const struct sk_buff *skb, __be16 sport,
@@ -641,9 +634,6 @@ drop:
 
 static inline void udp_post_segment_fix_csum(struct sk_buff *skb)
 {
-       /* UDP-lite can't land here - no GRO */
-       WARN_ON_ONCE(UDP_SKB_CB(skb)->partial_cov);
-
        /* UDP packets generated with UDP_SEGMENT and traversing:
         *
         * UDP tunnel(xmit) -> veth (segmentation) -> veth (gro) -> UDP tunnel (rx)
@@ -657,7 +647,6 @@ static inline void udp_post_segment_fix_csum(struct sk_buff *skb)
         * a valid csum after the segmentation.
         * Additionally fixup the UDP CB.
         */
-       UDP_SKB_CB(skb)->cscov = skb->len;
        if (skb->ip_summed == CHECKSUM_NONE && !skb->csum_valid)
                skb->csum_valid = 1;
 }
index fdd769745ac4d925414fb605b73aa272ba7f4d3e..0456a14c993b65e881b9db321d2b226d31c5c6ca 100644 (file)
@@ -25,40 +25,6 @@ static __inline__ int udplite_getfrag(void *from, char *to, int  offset,
 /*
  *     Checksumming routines
  */
-static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh)
-{
-       u16 cscov;
-
-        /* In UDPv4 a zero checksum means that the transmitter generated no
-         * checksum. UDP-Lite (like IPv6) mandates checksums, hence packets
-         * with a zero checksum field are illegal.                            */
-       if (uh->check == 0) {
-               net_dbg_ratelimited("UDPLite: zeroed checksum field\n");
-               return 1;
-       }
-
-       cscov = ntohs(uh->len);
-
-       if (cscov == 0)          /* Indicates that full coverage is required. */
-               ;
-       else if (cscov < 8  || cscov > skb->len) {
-               /*
-                * Coverage length violates RFC 3828: log and discard silently.
-                */
-               net_dbg_ratelimited("UDPLite: bad csum coverage %d/%d\n",
-                                   cscov, skb->len);
-               return 1;
-
-       } else if (cscov < skb->len) {
-               UDP_SKB_CB(skb)->partial_cov = 1;
-               UDP_SKB_CB(skb)->cscov = cscov;
-               if (skb->ip_summed == CHECKSUM_COMPLETE)
-                       skb->ip_summed = CHECKSUM_NONE;
-               skb->csum_valid = 0;
-        }
-
-       return 0;
-}
 
 /* Fast-path computation of checksum. Socket may not be locked. */
 static inline __wsum udplite_csum(struct sk_buff *skb)
index 10082095e6338a30fcf2baf0cb82406e0b1f0c88..d42fb9330c22ecd4991a12d059769331e5e4869e 100644 (file)
@@ -2072,14 +2072,13 @@ EXPORT_IPV6_MOD(udp_read_skb);
 INDIRECT_CALLABLE_SCOPE
 int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags)
 {
-       struct inet_sock *inet = inet_sk(sk);
        DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
-       struct sk_buff *skb;
-       unsigned int ulen, copied;
        int off, err, peeking = flags & MSG_PEEK;
-       int is_udplite = IS_UDPLITE(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct net *net = sock_net(sk);
        bool checksum_valid = false;
+       unsigned int ulen, copied;
+       struct sk_buff *skb;
 
        if (flags & MSG_ERRQUEUE)
                return ip_recv_error(sk, msg, len);
@@ -2097,14 +2096,10 @@ try_again:
        else if (copied < ulen)
                msg->msg_flags |= MSG_TRUNC;
 
-       /*
-        * If checksum is needed at all, try to do it while copying the
-        * data.  If the data is truncated, or if we only want a partial
-        * coverage checksum (UDP-Lite), do it before the copy.
+       /* If checksum is needed at all, try to do it while copying the
+        * data.  If the data is truncated, do it before the copy.
         */
-
-       if (copied < ulen || peeking ||
-           (is_udplite && UDP_SKB_CB(skb)->partial_cov)) {
+       if (copied < ulen || peeking) {
                checksum_valid = udp_skb_csum_unnecessary(skb) ||
                                !__udp_lib_checksum_complete(skb);
                if (!checksum_valid)
@@ -2444,42 +2439,6 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
                /* FALLTHROUGH -- it's a UDP Packet */
        }
 
-       /*
-        *      UDP-Lite specific tests, ignored on UDP sockets
-        */
-       if (unlikely(udp_test_bit(UDPLITE_RECV_CC, sk) &&
-                    UDP_SKB_CB(skb)->partial_cov)) {
-               u16 pcrlen = READ_ONCE(up->pcrlen);
-
-               /*
-                * MIB statistics other than incrementing the error count are
-                * disabled for the following two types of errors: these depend
-                * on the application settings, not on the functioning of the
-                * protocol stack as such.
-                *
-                * RFC 3828 here recommends (sec 3.3): "There should also be a
-                * way ... to ... at least let the receiving application block
-                * delivery of packets with coverage values less than a value
-                * provided by the application."
-                */
-               if (pcrlen == 0) {          /* full coverage was set  */
-                       net_dbg_ratelimited("UDPLite: partial coverage %d while full coverage %d requested\n",
-                                           UDP_SKB_CB(skb)->cscov, skb->len);
-                       goto drop;
-               }
-               /* The next case involves violating the min. coverage requested
-                * by the receiver. This is subtle: if receiver wants x and x is
-                * greater than the buffersize/MTU then receiver will complain
-                * that it wants x while sender emits packets of smaller size y.
-                * Therefore the above ...()->partial_cov statement is essential.
-                */
-               if (UDP_SKB_CB(skb)->cscov < pcrlen) {
-                       net_dbg_ratelimited("UDPLite: coverage %d too small, need min %d\n",
-                                           UDP_SKB_CB(skb)->cscov, pcrlen);
-                       goto drop;
-               }
-       }
-
        prefetch(&sk->sk_rmem_alloc);
        if (rcu_access_pointer(sk->sk_filter) &&
            udp_lib_checksum_complete(skb))
@@ -2613,29 +2572,14 @@ start_lookup:
  * Otherwise, csum completion requires checksumming packet body,
  * including udp header and folding it to skb->csum.
  */
-static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
-                                int proto)
+static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh)
 {
        int err;
 
-       UDP_SKB_CB(skb)->partial_cov = 0;
-       UDP_SKB_CB(skb)->cscov = skb->len;
-
-       if (proto == IPPROTO_UDPLITE) {
-               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
         * force to int.
         */
-       err = (__force int)skb_checksum_init_zero_check(skb, proto, uh->check,
+       err = (__force int)skb_checksum_init_zero_check(skb, IPPROTO_UDP, uh->check,
                                                        inet_compute_pseudo);
        if (err)
                return err;
@@ -2663,7 +2607,7 @@ static int udp_unicast_rcv_skb(struct sock *sk, struct sk_buff *skb,
 {
        int ret;
 
-       if (inet_get_convert_csum(sk) && uh->check && !IS_UDPLITE(sk))
+       if (inet_get_convert_csum(sk) && uh->check)
                skb_checksum_try_convert(skb, IPPROTO_UDP, inet_compute_pseudo);
 
        ret = udp_queue_rcv_skb(sk, skb);
@@ -2708,14 +2652,17 @@ static int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        if (ulen > skb->len)
                goto short_packet;
 
-       if (proto == IPPROTO_UDP) {
-               /* UDP validates ulen. */
-               if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen))
+       if (ulen < sizeof(*uh))
+               goto short_packet;
+
+       if (ulen < skb->len) {
+               if (pskb_trim_rcsum(skb, ulen))
                        goto short_packet;
+
                uh = udp_hdr(skb);
        }
 
-       if (udp4_csum_init(skb, uh, proto))
+       if (udp4_csum_init(skb, uh))
                goto csum_error;
 
        sk = inet_steal_sock(net, skb, sizeof(struct udphdr), saddr, uh->source, daddr, uh->dest,
index 07308b7156a688ac216672ee2a5999e6387f6f6b..bf5430ea66f0a821089b69eb432694f65e0e9447 100644 (file)
@@ -469,15 +469,13 @@ INDIRECT_CALLABLE_SCOPE
 int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
                  int flags)
 {
+       int off, is_udp4, err, peeking = flags & MSG_PEEK;
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct inet_sock *inet = inet_sk(sk);
-       struct sk_buff *skb;
-       unsigned int ulen, copied;
-       int off, err, peeking = flags & MSG_PEEK;
-       int is_udplite = IS_UDPLITE(sk);
        struct udp_mib __percpu *mib;
        bool checksum_valid = false;
-       int is_udp4;
+       unsigned int ulen, copied;
+       struct sk_buff *skb;
 
        if (flags & MSG_ERRQUEUE)
                return ipv6_recv_error(sk, msg, len);
@@ -501,14 +499,10 @@ try_again:
        is_udp4 = (skb->protocol == htons(ETH_P_IP));
        mib = __UDPX_MIB(sk, is_udp4);
 
-       /*
-        * If checksum is needed at all, try to do it while copying the
-        * data.  If the data is truncated, or if we only want a partial
-        * coverage checksum (UDP-Lite), do it before the copy.
+       /* If checksum is needed at all, try to do it while copying the
+        * data.  If the data is truncated, do it before the copy.
         */
-
-       if (copied < ulen || peeking ||
-           (is_udplite && UDP_SKB_CB(skb)->partial_cov)) {
+       if (copied < ulen || peeking) {
                checksum_valid = udp_skb_csum_unnecessary(skb) ||
                                !__udp_lib_checksum_complete(skb);
                if (!checksum_valid)
@@ -870,25 +864,6 @@ static int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
                /* FALLTHROUGH -- it's a UDP Packet */
        }
 
-       /*
-        * UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c).
-        */
-       if (unlikely(udp_test_bit(UDPLITE_RECV_CC, sk) &&
-                    UDP_SKB_CB(skb)->partial_cov)) {
-               u16 pcrlen = READ_ONCE(up->pcrlen);
-
-               if (pcrlen == 0) {          /* full coverage was set  */
-                       net_dbg_ratelimited("UDPLITE6: partial coverage %d while full coverage %d requested\n",
-                                           UDP_SKB_CB(skb)->cscov, skb->len);
-                       goto drop;
-               }
-               if (UDP_SKB_CB(skb)->cscov < pcrlen) {
-                       net_dbg_ratelimited("UDPLITE6: coverage %d too small, need min %d\n",
-                                           UDP_SKB_CB(skb)->cscov, pcrlen);
-                       goto drop;
-               }
-       }
-
        prefetch(&sk->sk_rmem_alloc);
        if (rcu_access_pointer(sk->sk_filter) &&
            udp_lib_checksum_complete(skb))
@@ -1053,7 +1028,7 @@ static int udp6_unicast_rcv_skb(struct sock *sk, struct sk_buff *skb,
 {
        int ret;
 
-       if (inet_get_convert_csum(sk) && uh->check && !IS_UDPLITE(sk))
+       if (inet_get_convert_csum(sk) && uh->check)
                skb_checksum_try_convert(skb, IPPROTO_UDP, ip6_compute_pseudo);
 
        ret = udpv6_queue_rcv_skb(sk, skb);
@@ -1064,24 +1039,10 @@ static int udp6_unicast_rcv_skb(struct sock *sk, struct sk_buff *skb,
        return 0;
 }
 
-static int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
+static int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh)
 {
        int err;
 
-       UDP_SKB_CB(skb)->partial_cov = 0;
-       UDP_SKB_CB(skb)->cscov = skb->len;
-
-       if (proto == IPPROTO_UDPLITE) {
-               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)
         * we accept a checksum of zero here. When we find the socket
         * for the UDP packet we'll check if that socket allows zero checksum
@@ -1090,7 +1051,7 @@ static int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
         * Note, we are only interested in != 0 or == 0, thus the
         * force to int.
         */
-       err = (__force int)skb_checksum_init_zero_check(skb, proto, uh->check,
+       err = (__force int)skb_checksum_init_zero_check(skb, IPPROTO_UDP, uh->check,
                                                        ip6_compute_pseudo);
        if (err)
                return err;
@@ -1132,26 +1093,23 @@ static int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        if (ulen > skb->len)
                goto short_packet;
 
-       if (proto == IPPROTO_UDP) {
-               /* UDP validates ulen. */
+       /* Check for jumbo payload */
+       if (ulen == 0)
+               ulen = skb->len;
 
-               /* Check for jumbo payload */
-               if (ulen == 0)
-                       ulen = skb->len;
+       if (ulen < sizeof(*uh))
+               goto short_packet;
 
-               if (ulen < sizeof(*uh))
+       if (ulen < skb->len) {
+               if (pskb_trim_rcsum(skb, ulen))
                        goto short_packet;
 
-               if (ulen < skb->len) {
-                       if (pskb_trim_rcsum(skb, ulen))
-                               goto short_packet;
-                       saddr = &ipv6_hdr(skb)->saddr;
-                       daddr = &ipv6_hdr(skb)->daddr;
-                       uh = udp_hdr(skb);
-               }
+               saddr = &ipv6_hdr(skb)->saddr;
+               daddr = &ipv6_hdr(skb)->daddr;
+               uh = udp_hdr(skb);
        }
 
-       if (udp6_csum_init(skb, uh, proto))
+       if (udp6_csum_init(skb, uh))
                goto csum_error;
 
        /* Check if the socket is already available, e.g. due to early demux */