When a GSO packet is split in software for IPv4 ESP,
validate_xmit_xfrm() calls esp_xmit() for each segment. These
segments have XFRM_GSO_SEGMENT set, but after the split, skb_is_gso()
returns false for each skb, so each call increments xo->seq.low by one
after writing the ESP sequence number.
The low bits are saved in seq before the increment, but esp.seqno is
set afterwards, using the saved low bits and the high bits from
xo->seq.hi.
When encryption is done in software with ESN enabled, the segment at
(H, 0xffffffff) uses (H+1, 0xffffffff) to generate the AES-GCM IV
because xo->seq.hi has already been incremented when the low bits
wrapped. One full 32-bit cycle later, if the packet at
(H+1, 0xffffffff) on the same SA is non-GSO, it generates the same IV.
Move the assignment to esp.seqno before xo->seq is advanced. This
saves H before the wrap increments xo->seq.hi to H+1, so the segment
at (H, 0xffffffff) generates its IV from (H, 0xffffffff).
Fixes: 4b549ccce941 ("xfrm: replay: Fix ESN wrap around for GSO")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Jérémy Jean <redacted>
---
net/ipv4/esp4_offload.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c
index abd77162f5e7..3a0aafe991f4 100644
--- a/net/ipv4/esp4_offload.c
+++ b/net/ipv4/esp4_offload.c
@@ -316,6 +316,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
}
seq = xo->seq.low;
+ esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32));
esph = esp.esph;
esph->spi = x->id.spi;
@@ -334,8 +335,6 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
if (xo->seq.low < seq)
xo->seq.hi++;
- esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32));
-
if (hw_offload && encap_type == UDP_ENCAP_ESPINUDP) {
/* In the XFRM stack, the encapsulation protocol is set to iphdr->protocol by
* setting *skb_mac_header(skb) (see esp_output_udp_encap()) where skb->mac_header--
2.47.3