xref: /openbmc/linux/net/xfrm/xfrm_replay.c (revision 060f35a317ef09101b128f399dce7ed13d019461)
1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29fdc4883SSteffen Klassert /*
39fdc4883SSteffen Klassert  * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c.
497e15c3aSSteffen Klassert  *
597e15c3aSSteffen Klassert  * Copyright (C) 2010 secunet Security Networks AG
697e15c3aSSteffen Klassert  * Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com>
79fdc4883SSteffen Klassert  */
89fdc4883SSteffen Klassert 
9bc3b2d7fSPaul Gortmaker #include <linux/export.h>
109fdc4883SSteffen Klassert #include <net/xfrm.h>
119fdc4883SSteffen Klassert 
xfrm_replay_seqhi(struct xfrm_state * x,__be32 net_seq)122cd08467SSteffen Klassert u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq)
132cd08467SSteffen Klassert {
142cd08467SSteffen Klassert 	u32 seq, seq_hi, bottom;
152cd08467SSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
162cd08467SSteffen Klassert 
172cd08467SSteffen Klassert 	if (!(x->props.flags & XFRM_STATE_ESN))
182cd08467SSteffen Klassert 		return 0;
192cd08467SSteffen Klassert 
202cd08467SSteffen Klassert 	seq = ntohl(net_seq);
212cd08467SSteffen Klassert 	seq_hi = replay_esn->seq_hi;
222cd08467SSteffen Klassert 	bottom = replay_esn->seq - replay_esn->replay_window + 1;
232cd08467SSteffen Klassert 
242cd08467SSteffen Klassert 	if (likely(replay_esn->seq >= replay_esn->replay_window - 1)) {
252cd08467SSteffen Klassert 		/* A. same subspace */
262cd08467SSteffen Klassert 		if (unlikely(seq < bottom))
272cd08467SSteffen Klassert 			seq_hi++;
282cd08467SSteffen Klassert 	} else {
292cd08467SSteffen Klassert 		/* B. window spans two subspaces */
302cd08467SSteffen Klassert 		if (unlikely(seq >= bottom))
312cd08467SSteffen Klassert 			seq_hi--;
322cd08467SSteffen Klassert 	}
332cd08467SSteffen Klassert 
342cd08467SSteffen Klassert 	return seq_hi;
352cd08467SSteffen Klassert }
367862b405SSteffen Klassert EXPORT_SYMBOL(xfrm_replay_seqhi);
37cfc61c59SFlorian Westphal 
38cfc61c59SFlorian Westphal static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event);
39cfc61c59SFlorian Westphal static void xfrm_replay_notify_esn(struct xfrm_state *x, int event);
40cfc61c59SFlorian Westphal 
xfrm_replay_notify(struct xfrm_state * x,int event)41cfc61c59SFlorian Westphal void xfrm_replay_notify(struct xfrm_state *x, int event)
429fdc4883SSteffen Klassert {
439fdc4883SSteffen Klassert 	struct km_event c;
449fdc4883SSteffen Klassert 	/* we send notify messages in case
459fdc4883SSteffen Klassert 	 *  1. we updated on of the sequence numbers, and the seqno difference
469fdc4883SSteffen Klassert 	 *     is at least x->replay_maxdiff, in this case we also update the
479fdc4883SSteffen Klassert 	 *     timeout of our timer function
489fdc4883SSteffen Klassert 	 *  2. if x->replay_maxage has elapsed since last update,
499fdc4883SSteffen Klassert 	 *     and there were changes
509fdc4883SSteffen Klassert 	 *
519fdc4883SSteffen Klassert 	 *  The state structure must be locked!
529fdc4883SSteffen Klassert 	 */
539fdc4883SSteffen Klassert 
54cfc61c59SFlorian Westphal 	switch (x->repl_mode) {
55cfc61c59SFlorian Westphal 	case XFRM_REPLAY_MODE_LEGACY:
56cfc61c59SFlorian Westphal 		break;
57cfc61c59SFlorian Westphal 	case XFRM_REPLAY_MODE_BMP:
58cfc61c59SFlorian Westphal 		xfrm_replay_notify_bmp(x, event);
59cfc61c59SFlorian Westphal 		return;
60cfc61c59SFlorian Westphal 	case XFRM_REPLAY_MODE_ESN:
61cfc61c59SFlorian Westphal 		xfrm_replay_notify_esn(x, event);
62cfc61c59SFlorian Westphal 		return;
63cfc61c59SFlorian Westphal 	}
64cfc61c59SFlorian Westphal 
659fdc4883SSteffen Klassert 	switch (event) {
669fdc4883SSteffen Klassert 	case XFRM_REPLAY_UPDATE:
67cd808fc9SThomas Egerer 		if (!x->replay_maxdiff ||
68cd808fc9SThomas Egerer 		    ((x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
69cd808fc9SThomas Egerer 		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) {
709fdc4883SSteffen Klassert 			if (x->xflags & XFRM_TIME_DEFER)
719fdc4883SSteffen Klassert 				event = XFRM_REPLAY_TIMEOUT;
729fdc4883SSteffen Klassert 			else
739fdc4883SSteffen Klassert 				return;
749fdc4883SSteffen Klassert 		}
759fdc4883SSteffen Klassert 
769fdc4883SSteffen Klassert 		break;
779fdc4883SSteffen Klassert 
789fdc4883SSteffen Klassert 	case XFRM_REPLAY_TIMEOUT:
799fdc4883SSteffen Klassert 		if (memcmp(&x->replay, &x->preplay,
809fdc4883SSteffen Klassert 			   sizeof(struct xfrm_replay_state)) == 0) {
819fdc4883SSteffen Klassert 			x->xflags |= XFRM_TIME_DEFER;
829fdc4883SSteffen Klassert 			return;
839fdc4883SSteffen Klassert 		}
849fdc4883SSteffen Klassert 
859fdc4883SSteffen Klassert 		break;
869fdc4883SSteffen Klassert 	}
879fdc4883SSteffen Klassert 
889fdc4883SSteffen Klassert 	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
899fdc4883SSteffen Klassert 	c.event = XFRM_MSG_NEWAE;
909fdc4883SSteffen Klassert 	c.data.aevent = event;
919fdc4883SSteffen Klassert 	km_state_notify(x, &c);
929fdc4883SSteffen Klassert 
939fdc4883SSteffen Klassert 	if (x->replay_maxage &&
949fdc4883SSteffen Klassert 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
959fdc4883SSteffen Klassert 		x->xflags &= ~XFRM_TIME_DEFER;
969fdc4883SSteffen Klassert }
979fdc4883SSteffen Klassert 
__xfrm_replay_overflow(struct xfrm_state * x,struct sk_buff * skb)98b5a1d1feSFlorian Westphal static int __xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
999fdc4883SSteffen Klassert {
1009fdc4883SSteffen Klassert 	int err = 0;
1019fdc4883SSteffen Klassert 	struct net *net = xs_net(x);
1029fdc4883SSteffen Klassert 
1039fdc4883SSteffen Klassert 	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
1049fdc4883SSteffen Klassert 		XFRM_SKB_CB(skb)->seq.output.low = ++x->replay.oseq;
105407d34efSHerbert Xu 		XFRM_SKB_CB(skb)->seq.output.hi = 0;
106428d2459SPetr Vaněk 		if (unlikely(x->replay.oseq == 0) &&
107428d2459SPetr Vaněk 		    !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) {
1089fdc4883SSteffen Klassert 			x->replay.oseq--;
1099fdc4883SSteffen Klassert 			xfrm_audit_state_replay_overflow(x, skb);
1109fdc4883SSteffen Klassert 			err = -EOVERFLOW;
1119fdc4883SSteffen Klassert 
1129fdc4883SSteffen Klassert 			return err;
1139fdc4883SSteffen Klassert 		}
1149fdc4883SSteffen Klassert 		if (xfrm_aevent_is_on(net))
115cfc61c59SFlorian Westphal 			xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
1169fdc4883SSteffen Klassert 	}
1179fdc4883SSteffen Klassert 
1189fdc4883SSteffen Klassert 	return err;
1199fdc4883SSteffen Klassert }
1209fdc4883SSteffen Klassert 
xfrm_replay_check_legacy(struct xfrm_state * x,struct sk_buff * skb,__be32 net_seq)121adfc2fdbSFlorian Westphal static int xfrm_replay_check_legacy(struct xfrm_state *x,
1229fdc4883SSteffen Klassert 				    struct sk_buff *skb, __be32 net_seq)
1239fdc4883SSteffen Klassert {
1249fdc4883SSteffen Klassert 	u32 diff;
1259fdc4883SSteffen Klassert 	u32 seq = ntohl(net_seq);
1269fdc4883SSteffen Klassert 
12736ae0148SSteffen Klassert 	if (!x->props.replay_window)
12836ae0148SSteffen Klassert 		return 0;
12936ae0148SSteffen Klassert 
1309fdc4883SSteffen Klassert 	if (unlikely(seq == 0))
1319fdc4883SSteffen Klassert 		goto err;
1329fdc4883SSteffen Klassert 
1339fdc4883SSteffen Klassert 	if (likely(seq > x->replay.seq))
1349fdc4883SSteffen Klassert 		return 0;
1359fdc4883SSteffen Klassert 
1369fdc4883SSteffen Klassert 	diff = x->replay.seq - seq;
13733fce60dSFan Du 	if (diff >= x->props.replay_window) {
1389fdc4883SSteffen Klassert 		x->stats.replay_window++;
1399fdc4883SSteffen Klassert 		goto err;
1409fdc4883SSteffen Klassert 	}
1419fdc4883SSteffen Klassert 
1429fdc4883SSteffen Klassert 	if (x->replay.bitmap & (1U << diff)) {
1439fdc4883SSteffen Klassert 		x->stats.replay++;
1449fdc4883SSteffen Klassert 		goto err;
1459fdc4883SSteffen Klassert 	}
1469fdc4883SSteffen Klassert 	return 0;
1479fdc4883SSteffen Klassert 
1489fdc4883SSteffen Klassert err:
1499fdc4883SSteffen Klassert 	xfrm_audit_state_replay(x, skb, net_seq);
1509fdc4883SSteffen Klassert 	return -EINVAL;
1519fdc4883SSteffen Klassert }
1529fdc4883SSteffen Klassert 
153c7f87783SFlorian Westphal static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq);
154c7f87783SFlorian Westphal static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq);
155c7f87783SFlorian Westphal 
xfrm_replay_advance(struct xfrm_state * x,__be32 net_seq)156c7f87783SFlorian Westphal void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
1579fdc4883SSteffen Klassert {
158c7f87783SFlorian Westphal 	u32 diff, seq;
159c7f87783SFlorian Westphal 
160c7f87783SFlorian Westphal 	switch (x->repl_mode) {
161c7f87783SFlorian Westphal 	case XFRM_REPLAY_MODE_LEGACY:
162c7f87783SFlorian Westphal 		break;
163c7f87783SFlorian Westphal 	case XFRM_REPLAY_MODE_BMP:
164c7f87783SFlorian Westphal 		return xfrm_replay_advance_bmp(x, net_seq);
165c7f87783SFlorian Westphal 	case XFRM_REPLAY_MODE_ESN:
166c7f87783SFlorian Westphal 		return xfrm_replay_advance_esn(x, net_seq);
167c7f87783SFlorian Westphal 	}
1689fdc4883SSteffen Klassert 
1699fdc4883SSteffen Klassert 	if (!x->props.replay_window)
1709fdc4883SSteffen Klassert 		return;
1719fdc4883SSteffen Klassert 
172c7f87783SFlorian Westphal 	seq = ntohl(net_seq);
1739fdc4883SSteffen Klassert 	if (seq > x->replay.seq) {
1749fdc4883SSteffen Klassert 		diff = seq - x->replay.seq;
1759fdc4883SSteffen Klassert 		if (diff < x->props.replay_window)
1769fdc4883SSteffen Klassert 			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1779fdc4883SSteffen Klassert 		else
1789fdc4883SSteffen Klassert 			x->replay.bitmap = 1;
1799fdc4883SSteffen Klassert 		x->replay.seq = seq;
1809fdc4883SSteffen Klassert 	} else {
1819fdc4883SSteffen Klassert 		diff = x->replay.seq - seq;
1829fdc4883SSteffen Klassert 		x->replay.bitmap |= (1U << diff);
1839fdc4883SSteffen Klassert 	}
1849fdc4883SSteffen Klassert 
1859fdc4883SSteffen Klassert 	if (xfrm_aevent_is_on(xs_net(x)))
186cfc61c59SFlorian Westphal 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
1879fdc4883SSteffen Klassert }
1889fdc4883SSteffen Klassert 
xfrm_replay_overflow_bmp(struct xfrm_state * x,struct sk_buff * skb)18997e15c3aSSteffen Klassert static int xfrm_replay_overflow_bmp(struct xfrm_state *x, struct sk_buff *skb)
19097e15c3aSSteffen Klassert {
19197e15c3aSSteffen Klassert 	int err = 0;
19297e15c3aSSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
19397e15c3aSSteffen Klassert 	struct net *net = xs_net(x);
19497e15c3aSSteffen Klassert 
19597e15c3aSSteffen Klassert 	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
19697e15c3aSSteffen Klassert 		XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq;
197407d34efSHerbert Xu 		XFRM_SKB_CB(skb)->seq.output.hi = 0;
198428d2459SPetr Vaněk 		if (unlikely(replay_esn->oseq == 0) &&
199428d2459SPetr Vaněk 		    !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) {
20097e15c3aSSteffen Klassert 			replay_esn->oseq--;
20197e15c3aSSteffen Klassert 			xfrm_audit_state_replay_overflow(x, skb);
20297e15c3aSSteffen Klassert 			err = -EOVERFLOW;
20397e15c3aSSteffen Klassert 
20497e15c3aSSteffen Klassert 			return err;
20597e15c3aSSteffen Klassert 		}
20697e15c3aSSteffen Klassert 		if (xfrm_aevent_is_on(net))
207cfc61c59SFlorian Westphal 			xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
20897e15c3aSSteffen Klassert 	}
20997e15c3aSSteffen Klassert 
21097e15c3aSSteffen Klassert 	return err;
21197e15c3aSSteffen Klassert }
21297e15c3aSSteffen Klassert 
xfrm_replay_check_bmp(struct xfrm_state * x,struct sk_buff * skb,__be32 net_seq)21397e15c3aSSteffen Klassert static int xfrm_replay_check_bmp(struct xfrm_state *x,
21497e15c3aSSteffen Klassert 				 struct sk_buff *skb, __be32 net_seq)
21597e15c3aSSteffen Klassert {
21697e15c3aSSteffen Klassert 	unsigned int bitnr, nr;
21797e15c3aSSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
21836ae0148SSteffen Klassert 	u32 pos;
21997e15c3aSSteffen Klassert 	u32 seq = ntohl(net_seq);
22097e15c3aSSteffen Klassert 	u32 diff =  replay_esn->seq - seq;
22136ae0148SSteffen Klassert 
22236ae0148SSteffen Klassert 	if (!replay_esn->replay_window)
22336ae0148SSteffen Klassert 		return 0;
22436ae0148SSteffen Klassert 
22597e15c3aSSteffen Klassert 	if (unlikely(seq == 0))
22697e15c3aSSteffen Klassert 		goto err;
22797e15c3aSSteffen Klassert 
22897e15c3aSSteffen Klassert 	if (likely(seq > replay_esn->seq))
22997e15c3aSSteffen Klassert 		return 0;
23097e15c3aSSteffen Klassert 
23197e15c3aSSteffen Klassert 	if (diff >= replay_esn->replay_window) {
23297e15c3aSSteffen Klassert 		x->stats.replay_window++;
23397e15c3aSSteffen Klassert 		goto err;
23497e15c3aSSteffen Klassert 	}
23597e15c3aSSteffen Klassert 
2361d974374SSteffen Klassert 	pos = (replay_esn->seq - 1) % replay_esn->replay_window;
2371d974374SSteffen Klassert 
2381d974374SSteffen Klassert 	if (pos >= diff)
23997e15c3aSSteffen Klassert 		bitnr = (pos - diff) % replay_esn->replay_window;
2401d974374SSteffen Klassert 	else
24197e15c3aSSteffen Klassert 		bitnr = replay_esn->replay_window - (diff - pos);
2421d974374SSteffen Klassert 
24397e15c3aSSteffen Klassert 	nr = bitnr >> 5;
24497e15c3aSSteffen Klassert 	bitnr = bitnr & 0x1F;
24597e15c3aSSteffen Klassert 	if (replay_esn->bmp[nr] & (1U << bitnr))
24697e15c3aSSteffen Klassert 		goto err_replay;
2471d974374SSteffen Klassert 
24897e15c3aSSteffen Klassert 	return 0;
24997e15c3aSSteffen Klassert 
25097e15c3aSSteffen Klassert err_replay:
25197e15c3aSSteffen Klassert 	x->stats.replay++;
25297e15c3aSSteffen Klassert err:
25397e15c3aSSteffen Klassert 	xfrm_audit_state_replay(x, skb, net_seq);
25497e15c3aSSteffen Klassert 	return -EINVAL;
25597e15c3aSSteffen Klassert }
25697e15c3aSSteffen Klassert 
xfrm_replay_advance_bmp(struct xfrm_state * x,__be32 net_seq)25797e15c3aSSteffen Klassert static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq)
25897e15c3aSSteffen Klassert {
25997e15c3aSSteffen Klassert 	unsigned int bitnr, nr, i;
26097e15c3aSSteffen Klassert 	u32 diff;
26197e15c3aSSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
26297e15c3aSSteffen Klassert 	u32 seq = ntohl(net_seq);
263e2f67259SNickolai Zeldovich 	u32 pos;
26497e15c3aSSteffen Klassert 
26597e15c3aSSteffen Klassert 	if (!replay_esn->replay_window)
26697e15c3aSSteffen Klassert 		return;
26797e15c3aSSteffen Klassert 
268e2f67259SNickolai Zeldovich 	pos = (replay_esn->seq - 1) % replay_esn->replay_window;
269e2f67259SNickolai Zeldovich 
27097e15c3aSSteffen Klassert 	if (seq > replay_esn->seq) {
27197e15c3aSSteffen Klassert 		diff = seq - replay_esn->seq;
27297e15c3aSSteffen Klassert 
27397e15c3aSSteffen Klassert 		if (diff < replay_esn->replay_window) {
27497e15c3aSSteffen Klassert 			for (i = 1; i < diff; i++) {
27597e15c3aSSteffen Klassert 				bitnr = (pos + i) % replay_esn->replay_window;
27697e15c3aSSteffen Klassert 				nr = bitnr >> 5;
27797e15c3aSSteffen Klassert 				bitnr = bitnr & 0x1F;
27897e15c3aSSteffen Klassert 				replay_esn->bmp[nr] &=  ~(1U << bitnr);
27997e15c3aSSteffen Klassert 			}
28097e15c3aSSteffen Klassert 		} else {
281e756682cSSteffen Klassert 			nr = (replay_esn->replay_window - 1) >> 5;
28297e15c3aSSteffen Klassert 			for (i = 0; i <= nr; i++)
28397e15c3aSSteffen Klassert 				replay_esn->bmp[i] = 0;
28497e15c3aSSteffen Klassert 		}
28597e15c3aSSteffen Klassert 
2861d974374SSteffen Klassert 		bitnr = (pos + diff) % replay_esn->replay_window;
28797e15c3aSSteffen Klassert 		replay_esn->seq = seq;
28897e15c3aSSteffen Klassert 	} else {
28997e15c3aSSteffen Klassert 		diff = replay_esn->seq - seq;
29097e15c3aSSteffen Klassert 
2911d974374SSteffen Klassert 		if (pos >= diff)
29297e15c3aSSteffen Klassert 			bitnr = (pos - diff) % replay_esn->replay_window;
2931d974374SSteffen Klassert 		else
29497e15c3aSSteffen Klassert 			bitnr = replay_esn->replay_window - (diff - pos);
2951d974374SSteffen Klassert 	}
2961d974374SSteffen Klassert 
29797e15c3aSSteffen Klassert 	nr = bitnr >> 5;
29897e15c3aSSteffen Klassert 	bitnr = bitnr & 0x1F;
29997e15c3aSSteffen Klassert 	replay_esn->bmp[nr] |= (1U << bitnr);
30097e15c3aSSteffen Klassert 
30197e15c3aSSteffen Klassert 	if (xfrm_aevent_is_on(xs_net(x)))
302cfc61c59SFlorian Westphal 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
30397e15c3aSSteffen Klassert }
30497e15c3aSSteffen Klassert 
xfrm_replay_notify_bmp(struct xfrm_state * x,int event)30597e15c3aSSteffen Klassert static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)
30697e15c3aSSteffen Klassert {
30797e15c3aSSteffen Klassert 	struct km_event c;
30897e15c3aSSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
30997e15c3aSSteffen Klassert 	struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn;
31097e15c3aSSteffen Klassert 
31197e15c3aSSteffen Klassert 	/* we send notify messages in case
31297e15c3aSSteffen Klassert 	 *  1. we updated on of the sequence numbers, and the seqno difference
31397e15c3aSSteffen Klassert 	 *     is at least x->replay_maxdiff, in this case we also update the
31497e15c3aSSteffen Klassert 	 *     timeout of our timer function
31597e15c3aSSteffen Klassert 	 *  2. if x->replay_maxage has elapsed since last update,
31697e15c3aSSteffen Klassert 	 *     and there were changes
31797e15c3aSSteffen Klassert 	 *
31897e15c3aSSteffen Klassert 	 *  The state structure must be locked!
31997e15c3aSSteffen Klassert 	 */
32097e15c3aSSteffen Klassert 
32197e15c3aSSteffen Klassert 	switch (event) {
32297e15c3aSSteffen Klassert 	case XFRM_REPLAY_UPDATE:
323cd808fc9SThomas Egerer 		if (!x->replay_maxdiff ||
324cd808fc9SThomas Egerer 		    ((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
325cd808fc9SThomas Egerer 		    (replay_esn->oseq - preplay_esn->oseq
326cd808fc9SThomas Egerer 		     < x->replay_maxdiff))) {
32797e15c3aSSteffen Klassert 			if (x->xflags & XFRM_TIME_DEFER)
32897e15c3aSSteffen Klassert 				event = XFRM_REPLAY_TIMEOUT;
32997e15c3aSSteffen Klassert 			else
33097e15c3aSSteffen Klassert 				return;
33197e15c3aSSteffen Klassert 		}
33297e15c3aSSteffen Klassert 
33397e15c3aSSteffen Klassert 		break;
33497e15c3aSSteffen Klassert 
33597e15c3aSSteffen Klassert 	case XFRM_REPLAY_TIMEOUT:
33697e15c3aSSteffen Klassert 		if (memcmp(x->replay_esn, x->preplay_esn,
33797e15c3aSSteffen Klassert 			   xfrm_replay_state_esn_len(replay_esn)) == 0) {
33897e15c3aSSteffen Klassert 			x->xflags |= XFRM_TIME_DEFER;
33997e15c3aSSteffen Klassert 			return;
34097e15c3aSSteffen Klassert 		}
34197e15c3aSSteffen Klassert 
34297e15c3aSSteffen Klassert 		break;
34397e15c3aSSteffen Klassert 	}
34497e15c3aSSteffen Klassert 
34597e15c3aSSteffen Klassert 	memcpy(x->preplay_esn, x->replay_esn,
34697e15c3aSSteffen Klassert 	       xfrm_replay_state_esn_len(replay_esn));
34797e15c3aSSteffen Klassert 	c.event = XFRM_MSG_NEWAE;
34897e15c3aSSteffen Klassert 	c.data.aevent = event;
34997e15c3aSSteffen Klassert 	km_state_notify(x, &c);
35097e15c3aSSteffen Klassert 
35197e15c3aSSteffen Klassert 	if (x->replay_maxage &&
35297e15c3aSSteffen Klassert 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
35397e15c3aSSteffen Klassert 		x->xflags &= ~XFRM_TIME_DEFER;
35497e15c3aSSteffen Klassert }
35597e15c3aSSteffen Klassert 
xfrm_replay_notify_esn(struct xfrm_state * x,int event)3560017c0b5SSteffen Klassert static void xfrm_replay_notify_esn(struct xfrm_state *x, int event)
3570017c0b5SSteffen Klassert {
3580017c0b5SSteffen Klassert 	u32 seq_diff, oseq_diff;
3590017c0b5SSteffen Klassert 	struct km_event c;
3600017c0b5SSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
3610017c0b5SSteffen Klassert 	struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn;
3620017c0b5SSteffen Klassert 
3630017c0b5SSteffen Klassert 	/* we send notify messages in case
3640017c0b5SSteffen Klassert 	 *  1. we updated on of the sequence numbers, and the seqno difference
3650017c0b5SSteffen Klassert 	 *     is at least x->replay_maxdiff, in this case we also update the
3660017c0b5SSteffen Klassert 	 *     timeout of our timer function
3670017c0b5SSteffen Klassert 	 *  2. if x->replay_maxage has elapsed since last update,
3680017c0b5SSteffen Klassert 	 *     and there were changes
3690017c0b5SSteffen Klassert 	 *
3700017c0b5SSteffen Klassert 	 *  The state structure must be locked!
3710017c0b5SSteffen Klassert 	 */
3720017c0b5SSteffen Klassert 
3730017c0b5SSteffen Klassert 	switch (event) {
3740017c0b5SSteffen Klassert 	case XFRM_REPLAY_UPDATE:
375cd808fc9SThomas Egerer 		if (x->replay_maxdiff) {
3760017c0b5SSteffen Klassert 			if (replay_esn->seq_hi == preplay_esn->seq_hi)
3770017c0b5SSteffen Klassert 				seq_diff = replay_esn->seq - preplay_esn->seq;
3780017c0b5SSteffen Klassert 			else
379cd808fc9SThomas Egerer 				seq_diff = ~preplay_esn->seq + replay_esn->seq
380cd808fc9SThomas Egerer 					   + 1;
3810017c0b5SSteffen Klassert 
3820017c0b5SSteffen Klassert 			if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
383cd808fc9SThomas Egerer 				oseq_diff = replay_esn->oseq
384cd808fc9SThomas Egerer 					    - preplay_esn->oseq;
3850017c0b5SSteffen Klassert 			else
386cd808fc9SThomas Egerer 				oseq_diff = ~preplay_esn->oseq
387cd808fc9SThomas Egerer 					    + replay_esn->oseq + 1;
3880017c0b5SSteffen Klassert 
389cd808fc9SThomas Egerer 			if (seq_diff >= x->replay_maxdiff ||
390cd808fc9SThomas Egerer 			    oseq_diff >= x->replay_maxdiff)
391cd808fc9SThomas Egerer 				break;
392cd808fc9SThomas Egerer 		}
3930017c0b5SSteffen Klassert 
3940017c0b5SSteffen Klassert 		if (x->xflags & XFRM_TIME_DEFER)
3950017c0b5SSteffen Klassert 			event = XFRM_REPLAY_TIMEOUT;
3960017c0b5SSteffen Klassert 		else
3970017c0b5SSteffen Klassert 			return;
3980017c0b5SSteffen Klassert 
3990017c0b5SSteffen Klassert 		break;
4000017c0b5SSteffen Klassert 
4010017c0b5SSteffen Klassert 	case XFRM_REPLAY_TIMEOUT:
4020017c0b5SSteffen Klassert 		if (memcmp(x->replay_esn, x->preplay_esn,
4030017c0b5SSteffen Klassert 			   xfrm_replay_state_esn_len(replay_esn)) == 0) {
4040017c0b5SSteffen Klassert 			x->xflags |= XFRM_TIME_DEFER;
4050017c0b5SSteffen Klassert 			return;
4060017c0b5SSteffen Klassert 		}
4070017c0b5SSteffen Klassert 
4080017c0b5SSteffen Klassert 		break;
4090017c0b5SSteffen Klassert 	}
4100017c0b5SSteffen Klassert 
4110017c0b5SSteffen Klassert 	memcpy(x->preplay_esn, x->replay_esn,
4120017c0b5SSteffen Klassert 	       xfrm_replay_state_esn_len(replay_esn));
4130017c0b5SSteffen Klassert 	c.event = XFRM_MSG_NEWAE;
4140017c0b5SSteffen Klassert 	c.data.aevent = event;
4150017c0b5SSteffen Klassert 	km_state_notify(x, &c);
4160017c0b5SSteffen Klassert 
4170017c0b5SSteffen Klassert 	if (x->replay_maxage &&
4180017c0b5SSteffen Klassert 	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
4190017c0b5SSteffen Klassert 		x->xflags &= ~XFRM_TIME_DEFER;
4200017c0b5SSteffen Klassert }
4210017c0b5SSteffen Klassert 
xfrm_replay_overflow_esn(struct xfrm_state * x,struct sk_buff * skb)4222cd08467SSteffen Klassert static int xfrm_replay_overflow_esn(struct xfrm_state *x, struct sk_buff *skb)
4232cd08467SSteffen Klassert {
4242cd08467SSteffen Klassert 	int err = 0;
4252cd08467SSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
4262cd08467SSteffen Klassert 	struct net *net = xs_net(x);
4272cd08467SSteffen Klassert 
4282cd08467SSteffen Klassert 	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
4292cd08467SSteffen Klassert 		XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq;
4302cd08467SSteffen Klassert 		XFRM_SKB_CB(skb)->seq.output.hi = replay_esn->oseq_hi;
4312cd08467SSteffen Klassert 
4322cd08467SSteffen Klassert 		if (unlikely(replay_esn->oseq == 0)) {
4332cd08467SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.hi = ++replay_esn->oseq_hi;
4342cd08467SSteffen Klassert 
4352cd08467SSteffen Klassert 			if (replay_esn->oseq_hi == 0) {
4362cd08467SSteffen Klassert 				replay_esn->oseq--;
4372cd08467SSteffen Klassert 				replay_esn->oseq_hi--;
4382cd08467SSteffen Klassert 				xfrm_audit_state_replay_overflow(x, skb);
4392cd08467SSteffen Klassert 				err = -EOVERFLOW;
4402cd08467SSteffen Klassert 
4412cd08467SSteffen Klassert 				return err;
4422cd08467SSteffen Klassert 			}
4432cd08467SSteffen Klassert 		}
4442cd08467SSteffen Klassert 		if (xfrm_aevent_is_on(net))
445cfc61c59SFlorian Westphal 			xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
4462cd08467SSteffen Klassert 	}
4472cd08467SSteffen Klassert 
4482cd08467SSteffen Klassert 	return err;
4492cd08467SSteffen Klassert }
4502cd08467SSteffen Klassert 
xfrm_replay_check_esn(struct xfrm_state * x,struct sk_buff * skb,__be32 net_seq)4512cd08467SSteffen Klassert static int xfrm_replay_check_esn(struct xfrm_state *x,
4522cd08467SSteffen Klassert 				 struct sk_buff *skb, __be32 net_seq)
4532cd08467SSteffen Klassert {
4542cd08467SSteffen Klassert 	unsigned int bitnr, nr;
4552cd08467SSteffen Klassert 	u32 diff;
4562cd08467SSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
45736ae0148SSteffen Klassert 	u32 pos;
4582cd08467SSteffen Klassert 	u32 seq = ntohl(net_seq);
4592cd08467SSteffen Klassert 	u32 wsize = replay_esn->replay_window;
4602cd08467SSteffen Klassert 	u32 top = replay_esn->seq;
4612cd08467SSteffen Klassert 	u32 bottom = top - wsize + 1;
4622cd08467SSteffen Klassert 
46336ae0148SSteffen Klassert 	if (!wsize)
46436ae0148SSteffen Klassert 		return 0;
46536ae0148SSteffen Klassert 
4662cd08467SSteffen Klassert 	if (unlikely(seq == 0 && replay_esn->seq_hi == 0 &&
4672cd08467SSteffen Klassert 		     (replay_esn->seq < replay_esn->replay_window - 1)))
4682cd08467SSteffen Klassert 		goto err;
4692cd08467SSteffen Klassert 
4702cd08467SSteffen Klassert 	diff = top - seq;
4712cd08467SSteffen Klassert 
4722cd08467SSteffen Klassert 	if (likely(top >= wsize - 1)) {
4732cd08467SSteffen Klassert 		/* A. same subspace */
4742cd08467SSteffen Klassert 		if (likely(seq > top) || seq < bottom)
4752cd08467SSteffen Klassert 			return 0;
4762cd08467SSteffen Klassert 	} else {
4772cd08467SSteffen Klassert 		/* B. window spans two subspaces */
4782cd08467SSteffen Klassert 		if (likely(seq > top && seq < bottom))
4792cd08467SSteffen Klassert 			return 0;
4802cd08467SSteffen Klassert 		if (seq >= bottom)
4812cd08467SSteffen Klassert 			diff = ~seq + top + 1;
4822cd08467SSteffen Klassert 	}
4832cd08467SSteffen Klassert 
4842cd08467SSteffen Klassert 	if (diff >= replay_esn->replay_window) {
4852cd08467SSteffen Klassert 		x->stats.replay_window++;
4862cd08467SSteffen Klassert 		goto err;
4872cd08467SSteffen Klassert 	}
4882cd08467SSteffen Klassert 
4891d974374SSteffen Klassert 	pos = (replay_esn->seq - 1) % replay_esn->replay_window;
4901d974374SSteffen Klassert 
4911d974374SSteffen Klassert 	if (pos >= diff)
4922cd08467SSteffen Klassert 		bitnr = (pos - diff) % replay_esn->replay_window;
4931d974374SSteffen Klassert 	else
4942cd08467SSteffen Klassert 		bitnr = replay_esn->replay_window - (diff - pos);
4951d974374SSteffen Klassert 
4962cd08467SSteffen Klassert 	nr = bitnr >> 5;
4972cd08467SSteffen Klassert 	bitnr = bitnr & 0x1F;
4982cd08467SSteffen Klassert 	if (replay_esn->bmp[nr] & (1U << bitnr))
4992cd08467SSteffen Klassert 		goto err_replay;
5001d974374SSteffen Klassert 
5012cd08467SSteffen Klassert 	return 0;
5022cd08467SSteffen Klassert 
5032cd08467SSteffen Klassert err_replay:
5042cd08467SSteffen Klassert 	x->stats.replay++;
5052cd08467SSteffen Klassert err:
5062cd08467SSteffen Klassert 	xfrm_audit_state_replay(x, skb, net_seq);
5072cd08467SSteffen Klassert 	return -EINVAL;
5082cd08467SSteffen Klassert }
5092cd08467SSteffen Klassert 
xfrm_replay_check(struct xfrm_state * x,struct sk_buff * skb,__be32 net_seq)510adfc2fdbSFlorian Westphal int xfrm_replay_check(struct xfrm_state *x,
511adfc2fdbSFlorian Westphal 		      struct sk_buff *skb, __be32 net_seq)
512adfc2fdbSFlorian Westphal {
513adfc2fdbSFlorian Westphal 	switch (x->repl_mode) {
514adfc2fdbSFlorian Westphal 	case XFRM_REPLAY_MODE_LEGACY:
515adfc2fdbSFlorian Westphal 		break;
516adfc2fdbSFlorian Westphal 	case XFRM_REPLAY_MODE_BMP:
517adfc2fdbSFlorian Westphal 		return xfrm_replay_check_bmp(x, skb, net_seq);
518adfc2fdbSFlorian Westphal 	case XFRM_REPLAY_MODE_ESN:
519adfc2fdbSFlorian Westphal 		return xfrm_replay_check_esn(x, skb, net_seq);
520adfc2fdbSFlorian Westphal 	}
521adfc2fdbSFlorian Westphal 
522adfc2fdbSFlorian Westphal 	return xfrm_replay_check_legacy(x, skb, net_seq);
523adfc2fdbSFlorian Westphal }
524adfc2fdbSFlorian Westphal 
xfrm_replay_recheck_esn(struct xfrm_state * x,struct sk_buff * skb,__be32 net_seq)5253b59df46SSteffen Klassert static int xfrm_replay_recheck_esn(struct xfrm_state *x,
5263b59df46SSteffen Klassert 				   struct sk_buff *skb, __be32 net_seq)
5273b59df46SSteffen Klassert {
5283b59df46SSteffen Klassert 	if (unlikely(XFRM_SKB_CB(skb)->seq.input.hi !=
5293b59df46SSteffen Klassert 		     htonl(xfrm_replay_seqhi(x, net_seq)))) {
5303b59df46SSteffen Klassert 			x->stats.replay_window++;
5313b59df46SSteffen Klassert 			return -EINVAL;
5323b59df46SSteffen Klassert 	}
5333b59df46SSteffen Klassert 
5343b59df46SSteffen Klassert 	return xfrm_replay_check_esn(x, skb, net_seq);
5353b59df46SSteffen Klassert }
5363b59df46SSteffen Klassert 
xfrm_replay_recheck(struct xfrm_state * x,struct sk_buff * skb,__be32 net_seq)53725cfb8bcSFlorian Westphal int xfrm_replay_recheck(struct xfrm_state *x,
53825cfb8bcSFlorian Westphal 			struct sk_buff *skb, __be32 net_seq)
53925cfb8bcSFlorian Westphal {
54025cfb8bcSFlorian Westphal 	switch (x->repl_mode) {
54125cfb8bcSFlorian Westphal 	case XFRM_REPLAY_MODE_LEGACY:
54225cfb8bcSFlorian Westphal 		break;
54325cfb8bcSFlorian Westphal 	case XFRM_REPLAY_MODE_BMP:
54425cfb8bcSFlorian Westphal 		/* no special recheck treatment */
54525cfb8bcSFlorian Westphal 		return xfrm_replay_check_bmp(x, skb, net_seq);
54625cfb8bcSFlorian Westphal 	case XFRM_REPLAY_MODE_ESN:
54725cfb8bcSFlorian Westphal 		return xfrm_replay_recheck_esn(x, skb, net_seq);
54825cfb8bcSFlorian Westphal 	}
54925cfb8bcSFlorian Westphal 
550adfc2fdbSFlorian Westphal 	return xfrm_replay_check_legacy(x, skb, net_seq);
55125cfb8bcSFlorian Westphal }
55225cfb8bcSFlorian Westphal 
xfrm_replay_advance_esn(struct xfrm_state * x,__be32 net_seq)5532cd08467SSteffen Klassert static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
5542cd08467SSteffen Klassert {
5552cd08467SSteffen Klassert 	unsigned int bitnr, nr, i;
5562cd08467SSteffen Klassert 	int wrap;
5572cd08467SSteffen Klassert 	u32 diff, pos, seq, seq_hi;
5582cd08467SSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
5592cd08467SSteffen Klassert 
5602cd08467SSteffen Klassert 	if (!replay_esn->replay_window)
5612cd08467SSteffen Klassert 		return;
5622cd08467SSteffen Klassert 
5632cd08467SSteffen Klassert 	seq = ntohl(net_seq);
5642cd08467SSteffen Klassert 	pos = (replay_esn->seq - 1) % replay_esn->replay_window;
5652cd08467SSteffen Klassert 	seq_hi = xfrm_replay_seqhi(x, net_seq);
5662cd08467SSteffen Klassert 	wrap = seq_hi - replay_esn->seq_hi;
5672cd08467SSteffen Klassert 
5682cd08467SSteffen Klassert 	if ((!wrap && seq > replay_esn->seq) || wrap > 0) {
5692cd08467SSteffen Klassert 		if (likely(!wrap))
5702cd08467SSteffen Klassert 			diff = seq - replay_esn->seq;
5712cd08467SSteffen Klassert 		else
5722cd08467SSteffen Klassert 			diff = ~replay_esn->seq + seq + 1;
5732cd08467SSteffen Klassert 
5742cd08467SSteffen Klassert 		if (diff < replay_esn->replay_window) {
5752cd08467SSteffen Klassert 			for (i = 1; i < diff; i++) {
5762cd08467SSteffen Klassert 				bitnr = (pos + i) % replay_esn->replay_window;
5772cd08467SSteffen Klassert 				nr = bitnr >> 5;
5782cd08467SSteffen Klassert 				bitnr = bitnr & 0x1F;
5792cd08467SSteffen Klassert 				replay_esn->bmp[nr] &=  ~(1U << bitnr);
5802cd08467SSteffen Klassert 			}
5812cd08467SSteffen Klassert 		} else {
582e756682cSSteffen Klassert 			nr = (replay_esn->replay_window - 1) >> 5;
5832cd08467SSteffen Klassert 			for (i = 0; i <= nr; i++)
5842cd08467SSteffen Klassert 				replay_esn->bmp[i] = 0;
5852cd08467SSteffen Klassert 		}
5862cd08467SSteffen Klassert 
5871d974374SSteffen Klassert 		bitnr = (pos + diff) % replay_esn->replay_window;
5882cd08467SSteffen Klassert 		replay_esn->seq = seq;
5892cd08467SSteffen Klassert 
5902cd08467SSteffen Klassert 		if (unlikely(wrap > 0))
5912cd08467SSteffen Klassert 			replay_esn->seq_hi++;
5922cd08467SSteffen Klassert 	} else {
5932cd08467SSteffen Klassert 		diff = replay_esn->seq - seq;
5942cd08467SSteffen Klassert 
5951d974374SSteffen Klassert 		if (pos >= diff)
5962cd08467SSteffen Klassert 			bitnr = (pos - diff) % replay_esn->replay_window;
5971d974374SSteffen Klassert 		else
5982cd08467SSteffen Klassert 			bitnr = replay_esn->replay_window - (diff - pos);
5991d974374SSteffen Klassert 	}
6001d974374SSteffen Klassert 
60150bd870aSYossef Efraim 	xfrm_dev_state_advance_esn(x);
60250bd870aSYossef Efraim 
6032cd08467SSteffen Klassert 	nr = bitnr >> 5;
6042cd08467SSteffen Klassert 	bitnr = bitnr & 0x1F;
6052cd08467SSteffen Klassert 	replay_esn->bmp[nr] |= (1U << bitnr);
6062cd08467SSteffen Klassert 
6072cd08467SSteffen Klassert 	if (xfrm_aevent_is_on(xs_net(x)))
608cfc61c59SFlorian Westphal 		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
6092cd08467SSteffen Klassert }
6102cd08467SSteffen Klassert 
611d7dbefc4SSteffen Klassert #ifdef CONFIG_XFRM_OFFLOAD
xfrm_replay_overflow_offload(struct xfrm_state * x,struct sk_buff * skb)612d7dbefc4SSteffen Klassert static int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *skb)
613d7dbefc4SSteffen Klassert {
614d7dbefc4SSteffen Klassert 	int err = 0;
615d7dbefc4SSteffen Klassert 	struct net *net = xs_net(x);
616d7dbefc4SSteffen Klassert 	struct xfrm_offload *xo = xfrm_offload(skb);
617d7dbefc4SSteffen Klassert 	__u32 oseq = x->replay.oseq;
618d7dbefc4SSteffen Klassert 
619d7dbefc4SSteffen Klassert 	if (!xo)
620b5a1d1feSFlorian Westphal 		return __xfrm_replay_overflow(x, skb);
621d7dbefc4SSteffen Klassert 
622d7dbefc4SSteffen Klassert 	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
623d7dbefc4SSteffen Klassert 		if (!skb_is_gso(skb)) {
624d7dbefc4SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
625d7dbefc4SSteffen Klassert 			xo->seq.low = oseq;
626d7dbefc4SSteffen Klassert 		} else {
627d7dbefc4SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
628d7dbefc4SSteffen Klassert 			xo->seq.low = oseq + 1;
629d7dbefc4SSteffen Klassert 			oseq += skb_shinfo(skb)->gso_segs;
630d7dbefc4SSteffen Klassert 		}
631d7dbefc4SSteffen Klassert 
632d7dbefc4SSteffen Klassert 		XFRM_SKB_CB(skb)->seq.output.hi = 0;
633d7dbefc4SSteffen Klassert 		xo->seq.hi = 0;
634428d2459SPetr Vaněk 		if (unlikely(oseq < x->replay.oseq) &&
635428d2459SPetr Vaněk 		    !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) {
636d7dbefc4SSteffen Klassert 			xfrm_audit_state_replay_overflow(x, skb);
637d7dbefc4SSteffen Klassert 			err = -EOVERFLOW;
638d7dbefc4SSteffen Klassert 
639d7dbefc4SSteffen Klassert 			return err;
640d7dbefc4SSteffen Klassert 		}
641d7dbefc4SSteffen Klassert 
642d7dbefc4SSteffen Klassert 		x->replay.oseq = oseq;
643d7dbefc4SSteffen Klassert 
644d7dbefc4SSteffen Klassert 		if (xfrm_aevent_is_on(net))
645cfc61c59SFlorian Westphal 			xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
646d7dbefc4SSteffen Klassert 	}
647d7dbefc4SSteffen Klassert 
648d7dbefc4SSteffen Klassert 	return err;
649d7dbefc4SSteffen Klassert }
650d7dbefc4SSteffen Klassert 
xfrm_replay_overflow_offload_bmp(struct xfrm_state * x,struct sk_buff * skb)651d7dbefc4SSteffen Klassert static int xfrm_replay_overflow_offload_bmp(struct xfrm_state *x, struct sk_buff *skb)
652d7dbefc4SSteffen Klassert {
653d7dbefc4SSteffen Klassert 	int err = 0;
654d7dbefc4SSteffen Klassert 	struct xfrm_offload *xo = xfrm_offload(skb);
655d7dbefc4SSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
656d7dbefc4SSteffen Klassert 	struct net *net = xs_net(x);
657d7dbefc4SSteffen Klassert 	__u32 oseq = replay_esn->oseq;
658d7dbefc4SSteffen Klassert 
659d7dbefc4SSteffen Klassert 	if (!xo)
660d7dbefc4SSteffen Klassert 		return xfrm_replay_overflow_bmp(x, skb);
661d7dbefc4SSteffen Klassert 
662d7dbefc4SSteffen Klassert 	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
663d7dbefc4SSteffen Klassert 		if (!skb_is_gso(skb)) {
664d7dbefc4SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
665d7dbefc4SSteffen Klassert 			xo->seq.low = oseq;
666d7dbefc4SSteffen Klassert 		} else {
667d7dbefc4SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
668d7dbefc4SSteffen Klassert 			xo->seq.low = oseq + 1;
669d7dbefc4SSteffen Klassert 			oseq += skb_shinfo(skb)->gso_segs;
670d7dbefc4SSteffen Klassert 		}
671d7dbefc4SSteffen Klassert 
672d7dbefc4SSteffen Klassert 		XFRM_SKB_CB(skb)->seq.output.hi = 0;
673d7dbefc4SSteffen Klassert 		xo->seq.hi = 0;
674428d2459SPetr Vaněk 		if (unlikely(oseq < replay_esn->oseq) &&
675428d2459SPetr Vaněk 		    !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) {
676d7dbefc4SSteffen Klassert 			xfrm_audit_state_replay_overflow(x, skb);
677d7dbefc4SSteffen Klassert 			err = -EOVERFLOW;
678d7dbefc4SSteffen Klassert 
679d7dbefc4SSteffen Klassert 			return err;
680d7dbefc4SSteffen Klassert 		} else {
681d7dbefc4SSteffen Klassert 			replay_esn->oseq = oseq;
682d7dbefc4SSteffen Klassert 		}
683d7dbefc4SSteffen Klassert 
684d7dbefc4SSteffen Klassert 		if (xfrm_aevent_is_on(net))
685cfc61c59SFlorian Westphal 			xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
686d7dbefc4SSteffen Klassert 	}
687d7dbefc4SSteffen Klassert 
688d7dbefc4SSteffen Klassert 	return err;
689d7dbefc4SSteffen Klassert }
690d7dbefc4SSteffen Klassert 
xfrm_replay_overflow_offload_esn(struct xfrm_state * x,struct sk_buff * skb)691d7dbefc4SSteffen Klassert static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff *skb)
692d7dbefc4SSteffen Klassert {
693d7dbefc4SSteffen Klassert 	int err = 0;
694d7dbefc4SSteffen Klassert 	struct xfrm_offload *xo = xfrm_offload(skb);
695d7dbefc4SSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
696d7dbefc4SSteffen Klassert 	struct net *net = xs_net(x);
697d7dbefc4SSteffen Klassert 	__u32 oseq = replay_esn->oseq;
698d7dbefc4SSteffen Klassert 	__u32 oseq_hi = replay_esn->oseq_hi;
699d7dbefc4SSteffen Klassert 
700d7dbefc4SSteffen Klassert 	if (!xo)
701d7dbefc4SSteffen Klassert 		return xfrm_replay_overflow_esn(x, skb);
702d7dbefc4SSteffen Klassert 
703d7dbefc4SSteffen Klassert 	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
704d7dbefc4SSteffen Klassert 		if (!skb_is_gso(skb)) {
705d7dbefc4SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
706d7dbefc4SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi;
707d7dbefc4SSteffen Klassert 			xo->seq.low = oseq;
708d7dbefc4SSteffen Klassert 			xo->seq.hi = oseq_hi;
709d7dbefc4SSteffen Klassert 		} else {
710d7dbefc4SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
711d7dbefc4SSteffen Klassert 			XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi;
712b8b549eeSSteffen Klassert 			xo->seq.low = oseq + 1;
713d7dbefc4SSteffen Klassert 			xo->seq.hi = oseq_hi;
714d7dbefc4SSteffen Klassert 			oseq += skb_shinfo(skb)->gso_segs;
715d7dbefc4SSteffen Klassert 		}
716d7dbefc4SSteffen Klassert 
717*68a5e8b9SJianbo Liu 		if (unlikely(oseq < replay_esn->oseq)) {
718*68a5e8b9SJianbo Liu 			replay_esn->oseq_hi = ++oseq_hi;
719*68a5e8b9SJianbo Liu 			if (xo->seq.low < replay_esn->oseq) {
720*68a5e8b9SJianbo Liu 				XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi;
721d7dbefc4SSteffen Klassert 				xo->seq.hi = oseq_hi;
722*68a5e8b9SJianbo Liu 			}
723d7dbefc4SSteffen Klassert 			if (replay_esn->oseq_hi == 0) {
724d7dbefc4SSteffen Klassert 				replay_esn->oseq--;
725d7dbefc4SSteffen Klassert 				replay_esn->oseq_hi--;
726d7dbefc4SSteffen Klassert 				xfrm_audit_state_replay_overflow(x, skb);
727d7dbefc4SSteffen Klassert 				err = -EOVERFLOW;
728d7dbefc4SSteffen Klassert 
729d7dbefc4SSteffen Klassert 				return err;
730d7dbefc4SSteffen Klassert 			}
731d7dbefc4SSteffen Klassert 		}
732d7dbefc4SSteffen Klassert 
733d7dbefc4SSteffen Klassert 		replay_esn->oseq = oseq;
734d7dbefc4SSteffen Klassert 
735d7dbefc4SSteffen Klassert 		if (xfrm_aevent_is_on(net))
736cfc61c59SFlorian Westphal 			xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
737d7dbefc4SSteffen Klassert 	}
738d7dbefc4SSteffen Klassert 
739d7dbefc4SSteffen Klassert 	return err;
740d7dbefc4SSteffen Klassert }
741d7dbefc4SSteffen Klassert 
xfrm_replay_overflow(struct xfrm_state * x,struct sk_buff * skb)742b5a1d1feSFlorian Westphal int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
743b5a1d1feSFlorian Westphal {
744b5a1d1feSFlorian Westphal 	switch (x->repl_mode) {
745b5a1d1feSFlorian Westphal 	case XFRM_REPLAY_MODE_LEGACY:
746b5a1d1feSFlorian Westphal 		break;
747b5a1d1feSFlorian Westphal 	case XFRM_REPLAY_MODE_BMP:
748b5a1d1feSFlorian Westphal 		return xfrm_replay_overflow_offload_bmp(x, skb);
749b5a1d1feSFlorian Westphal 	case XFRM_REPLAY_MODE_ESN:
750b5a1d1feSFlorian Westphal 		return xfrm_replay_overflow_offload_esn(x, skb);
751b5a1d1feSFlorian Westphal 	}
752d7dbefc4SSteffen Klassert 
753b5a1d1feSFlorian Westphal 	return xfrm_replay_overflow_offload(x, skb);
754b5a1d1feSFlorian Westphal }
755d7dbefc4SSteffen Klassert #else
xfrm_replay_overflow(struct xfrm_state * x,struct sk_buff * skb)756b5a1d1feSFlorian Westphal int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
757b5a1d1feSFlorian Westphal {
758b5a1d1feSFlorian Westphal 	switch (x->repl_mode) {
759b5a1d1feSFlorian Westphal 	case XFRM_REPLAY_MODE_LEGACY:
760b5a1d1feSFlorian Westphal 		break;
761b5a1d1feSFlorian Westphal 	case XFRM_REPLAY_MODE_BMP:
762b5a1d1feSFlorian Westphal 		return xfrm_replay_overflow_bmp(x, skb);
763b5a1d1feSFlorian Westphal 	case XFRM_REPLAY_MODE_ESN:
764b5a1d1feSFlorian Westphal 		return xfrm_replay_overflow_esn(x, skb);
765b5a1d1feSFlorian Westphal 	}
7669fdc4883SSteffen Klassert 
767b5a1d1feSFlorian Westphal 	return __xfrm_replay_overflow(x, skb);
768b5a1d1feSFlorian Westphal }
769d7dbefc4SSteffen Klassert #endif
7702cd08467SSteffen Klassert 
xfrm_init_replay(struct xfrm_state * x,struct netlink_ext_ack * extack)7711cf9a3aeSSabrina Dubroca int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack)
7729fdc4883SSteffen Klassert {
77397e15c3aSSteffen Klassert 	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
77497e15c3aSSteffen Klassert 
77597e15c3aSSteffen Klassert 	if (replay_esn) {
77697e15c3aSSteffen Klassert 		if (replay_esn->replay_window >
7771cf9a3aeSSabrina Dubroca 		    replay_esn->bmp_len * sizeof(__u32) * 8) {
7781cf9a3aeSSabrina Dubroca 			NL_SET_ERR_MSG(extack, "ESN replay window is too large for the chosen bitmap size");
77997e15c3aSSteffen Klassert 			return -EINVAL;
7801cf9a3aeSSabrina Dubroca 		}
78197e15c3aSSteffen Klassert 
782aafd0d88SUlrich Weber 		if (x->props.flags & XFRM_STATE_ESN) {
7831cf9a3aeSSabrina Dubroca 			if (replay_esn->replay_window == 0) {
7841cf9a3aeSSabrina Dubroca 				NL_SET_ERR_MSG(extack, "ESN replay window must be > 0");
7856fa5ddccSSteffen Klassert 				return -EINVAL;
7861cf9a3aeSSabrina Dubroca 			}
787cfc61c59SFlorian Westphal 			x->repl_mode = XFRM_REPLAY_MODE_ESN;
788d7dbefc4SSteffen Klassert 		} else {
789cfc61c59SFlorian Westphal 			x->repl_mode = XFRM_REPLAY_MODE_BMP;
790d7dbefc4SSteffen Klassert 		}
791d7dbefc4SSteffen Klassert 	} else {
792cfc61c59SFlorian Westphal 		x->repl_mode = XFRM_REPLAY_MODE_LEGACY;
793d7dbefc4SSteffen Klassert 	}
7949fdc4883SSteffen Klassert 
7959fdc4883SSteffen Klassert 	return 0;
7969fdc4883SSteffen Klassert }
7979fdc4883SSteffen Klassert EXPORT_SYMBOL(xfrm_init_replay);
798