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