Frag-list GRO validates the UDP checksum of every datagram before linking
it into an aggregate. Completion makes the head's uh->len span the whole
aggregate, but keeps uh->check for the first datagram, which segmentation
restores, so only the checksum state can carry that verdict. Completion
records it by turning a CHECKSUM_NONE head into CHECKSUM_UNNECESSARY, but
leaves a CHECKSUM_COMPLETE head untouched, whose checksum then covers only
the first datagram.

Commit f0b8c3034556 ("udp: do not transition UDP GRO fraglist partial
checksums to unnecessary") limited the conversion to CHECKSUM_NONE so that
CHECKSUM_PARTIAL packets are not marked as validated. It kept
CHECKSUM_COMPLETE because segmentation restores valid datagrams, but the
aggregate is validated before it is segmented.

Any later validation of such an aggregate fails. udp4_csum_init and
udp6_csum_init compute the complete checksum of an aggregate no longer
than CHECKSUM_BREAK and drop it as a checksum error. For a larger
aggregate, conntrack's nf_checksum in PRE_ROUTING falls back to a
software checksum of the whole skb, which fails, marks the packet invalid
and sets csum_complete_sw, so UDP drops the aggregate too.

Record the verdict for a CHECKSUM_COMPLETE head as for a device which
validated the checksums itself, so that completion yields the same
CHECKSUM_UNNECESSARY state as for CHECKSUM_NONE and CHECKSUM_UNNECESSARY.
CHECKSUM_PARTIAL stays as that commit intended.

With rx-gro-list enabled on an mlx5 port, which reports CHECKSUM_COMPLETE,
a 100 Mbit/s flow of 30-byte UDP datagrams to a local socket raised
UdpInCsumErrors by 3880 in five seconds and lost exactly twice as many
datagrams, one pair per dropped aggregate. With rx-gro-list disabled, the
counter stayed at zero.

TCP frag-list completion follows the same pattern and is not changed here.

Sashiko reported the issue while reviewing an ovpn series which uses
frag-list GRO.

Fixes: f0b8c3034556 ("udp: do not transition UDP GRO fraglist partial checksums 
to unnecessary")
Signed-off-by: Ralf Lici <[email protected]>
---
Changes since v2 
https://lore.kernel.org/openvpn-devel/86994f75fd7d92f745f06e4392f89ff2ee12c3d8.1794329430.git.r...@mandelbit.com/
- Reference the commit which stopped converting CHECKSUM_COMPLETE in the
  Fixes tag. (Sashiko)
- Explain why only the checksum state can carry the GRO verdict.

New in v2.

 net/ipv4/udp_offload.c | 8 ++++++++
 net/ipv6/udp_offload.c | 8 ++++++++
 2 files changed, 16 insertions(+)

diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index cf07c3c6611a..572a65cd8977 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -965,6 +965,14 @@ INDIRECT_CALLABLE_SCOPE int udp4_gro_complete(struct 
sk_buff *skb, int nhoff)
                skb_shinfo(skb)->gso_type |= (SKB_GSO_FRAGLIST|SKB_GSO_UDP_L4);
                skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
 
+               /* GRO validated every datagram, but a CHECKSUM_COMPLETE value
+                * covers only the first one: keep the verdict instead, as for
+                * a device which validated the checksums itself
+                */
+               if (skb->ip_summed == CHECKSUM_COMPLETE) {
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       skb->csum_level = 0;
+               }
                __skb_incr_checksum_unnecessary(skb);
 
                return 0;
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 7370bcb80332..0b7ced2b5394 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -176,6 +176,14 @@ int udp6_gro_complete(struct sk_buff *skb, int nhoff)
                skb_shinfo(skb)->gso_type |= (SKB_GSO_FRAGLIST|SKB_GSO_UDP_L4);
                skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
 
+               /* GRO validated every datagram, but a CHECKSUM_COMPLETE value
+                * covers only the first one: keep the verdict instead, as for
+                * a device which validated the checksums itself
+                */
+               if (skb->ip_summed == CHECKSUM_COMPLETE) {
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       skb->csum_level = 0;
+               }
                __skb_incr_checksum_unnecessary(skb);
 
                return 0;
-- 
2.56.0



_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to