Commit 4b549ccc authored by Christian Langrock's avatar Christian Langrock Committed by Steffen Klassert
Browse files

xfrm: replay: Fix ESN wrap around for GSO



When using GSO it can happen that the wrong seq_hi is used for the last
packets before the wrap around. This can lead to double usage of a
sequence number. To avoid this, we should serialize this last GSO
packet.

Fixes: d7dbefc4 ("xfrm: Add xfrm_replay_overflow functions for offloading")
Co-developed-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: default avatarChristian Langrock <christian.langrock@secunet.com>
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
parent d83f7040
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -314,6 +314,9 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
			xo->seq.low += skb_shinfo(skb)->gso_segs;
	}

	if (xo->seq.low < seq)
		xo->seq.hi++;

	esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32));

	ip_hdr(skb)->tot_len = htons(skb->len);
+3 −0
Original line number Diff line number Diff line
@@ -346,6 +346,9 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features
			xo->seq.low += skb_shinfo(skb)->gso_segs;
	}

	if (xo->seq.low < seq)
		xo->seq.hi++;

	esp.seqno = cpu_to_be64(xo->seq.low + ((u64)xo->seq.hi << 32));

	len = skb->len - sizeof(struct ipv6hdr);
+14 −1
Original line number Diff line number Diff line
@@ -97,6 +97,18 @@ static void xfrm_outer_mode_prep(struct xfrm_state *x, struct sk_buff *skb)
	}
}

static inline bool xmit_xfrm_check_overflow(struct sk_buff *skb)
{
	struct xfrm_offload *xo = xfrm_offload(skb);
	__u32 seq = xo->seq.low;

	seq += skb_shinfo(skb)->gso_segs;
	if (unlikely(seq < xo->seq.low))
		return true;

	return false;
}

struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again)
{
	int err;
@@ -134,7 +146,8 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
		return skb;
	}

	if (skb_is_gso(skb) && unlikely(x->xso.dev != dev)) {
	if (skb_is_gso(skb) && (unlikely(x->xso.dev != dev) ||
				unlikely(xmit_xfrm_check_overflow(skb)))) {
		struct sk_buff *segs;

		/* Packet got rerouted, fixup features and segment it. */
+1 −1
Original line number Diff line number Diff line
@@ -714,7 +714,7 @@ static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff
			oseq += skb_shinfo(skb)->gso_segs;
		}

		if (unlikely(oseq < replay_esn->oseq)) {
		if (unlikely(xo->seq.low < replay_esn->oseq)) {
			XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi;
			xo->seq.hi = oseq_hi;
			replay_esn->oseq_hi = oseq_hi;