18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010 secunet Security Networks AG
68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <net/xfrm.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ciu32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	u32 seq, seq_hi, bottom;
158c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	if (!(x->props.flags & XFRM_STATE_ESN))
188c2ecf20Sopenharmony_ci		return 0;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	seq = ntohl(net_seq);
218c2ecf20Sopenharmony_ci	seq_hi = replay_esn->seq_hi;
228c2ecf20Sopenharmony_ci	bottom = replay_esn->seq - replay_esn->replay_window + 1;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if (likely(replay_esn->seq >= replay_esn->replay_window - 1)) {
258c2ecf20Sopenharmony_ci		/* A. same subspace */
268c2ecf20Sopenharmony_ci		if (unlikely(seq < bottom))
278c2ecf20Sopenharmony_ci			seq_hi++;
288c2ecf20Sopenharmony_ci	} else {
298c2ecf20Sopenharmony_ci		/* B. window spans two subspaces */
308c2ecf20Sopenharmony_ci		if (unlikely(seq >= bottom))
318c2ecf20Sopenharmony_ci			seq_hi--;
328c2ecf20Sopenharmony_ci	}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return seq_hi;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_replay_seqhi);
378c2ecf20Sopenharmony_ci;
388c2ecf20Sopenharmony_cistatic void xfrm_replay_notify(struct xfrm_state *x, int event)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct km_event c;
418c2ecf20Sopenharmony_ci	/* we send notify messages in case
428c2ecf20Sopenharmony_ci	 *  1. we updated on of the sequence numbers, and the seqno difference
438c2ecf20Sopenharmony_ci	 *     is at least x->replay_maxdiff, in this case we also update the
448c2ecf20Sopenharmony_ci	 *     timeout of our timer function
458c2ecf20Sopenharmony_ci	 *  2. if x->replay_maxage has elapsed since last update,
468c2ecf20Sopenharmony_ci	 *     and there were changes
478c2ecf20Sopenharmony_ci	 *
488c2ecf20Sopenharmony_ci	 *  The state structure must be locked!
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	switch (event) {
528c2ecf20Sopenharmony_ci	case XFRM_REPLAY_UPDATE:
538c2ecf20Sopenharmony_ci		if (!x->replay_maxdiff ||
548c2ecf20Sopenharmony_ci		    ((x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
558c2ecf20Sopenharmony_ci		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) {
568c2ecf20Sopenharmony_ci			if (x->xflags & XFRM_TIME_DEFER)
578c2ecf20Sopenharmony_ci				event = XFRM_REPLAY_TIMEOUT;
588c2ecf20Sopenharmony_ci			else
598c2ecf20Sopenharmony_ci				return;
608c2ecf20Sopenharmony_ci		}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		break;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	case XFRM_REPLAY_TIMEOUT:
658c2ecf20Sopenharmony_ci		if (memcmp(&x->replay, &x->preplay,
668c2ecf20Sopenharmony_ci			   sizeof(struct xfrm_replay_state)) == 0) {
678c2ecf20Sopenharmony_ci			x->xflags |= XFRM_TIME_DEFER;
688c2ecf20Sopenharmony_ci			return;
698c2ecf20Sopenharmony_ci		}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		break;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
758c2ecf20Sopenharmony_ci	c.event = XFRM_MSG_NEWAE;
768c2ecf20Sopenharmony_ci	c.data.aevent = event;
778c2ecf20Sopenharmony_ci	km_state_notify(x, &c);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (x->replay_maxage &&
808c2ecf20Sopenharmony_ci	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
818c2ecf20Sopenharmony_ci		x->xflags &= ~XFRM_TIME_DEFER;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	int err = 0;
878c2ecf20Sopenharmony_ci	struct net *net = xs_net(x);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
908c2ecf20Sopenharmony_ci		XFRM_SKB_CB(skb)->seq.output.low = ++x->replay.oseq;
918c2ecf20Sopenharmony_ci		XFRM_SKB_CB(skb)->seq.output.hi = 0;
928c2ecf20Sopenharmony_ci		if (unlikely(x->replay.oseq == 0) &&
938c2ecf20Sopenharmony_ci		    !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) {
948c2ecf20Sopenharmony_ci			x->replay.oseq--;
958c2ecf20Sopenharmony_ci			xfrm_audit_state_replay_overflow(x, skb);
968c2ecf20Sopenharmony_ci			err = -EOVERFLOW;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci			return err;
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci		if (xfrm_aevent_is_on(net))
1018c2ecf20Sopenharmony_ci			x->repl->notify(x, XFRM_REPLAY_UPDATE);
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return err;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int xfrm_replay_check(struct xfrm_state *x,
1088c2ecf20Sopenharmony_ci		      struct sk_buff *skb, __be32 net_seq)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	u32 diff;
1118c2ecf20Sopenharmony_ci	u32 seq = ntohl(net_seq);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (!x->props.replay_window)
1148c2ecf20Sopenharmony_ci		return 0;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (unlikely(seq == 0))
1178c2ecf20Sopenharmony_ci		goto err;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (likely(seq > x->replay.seq))
1208c2ecf20Sopenharmony_ci		return 0;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	diff = x->replay.seq - seq;
1238c2ecf20Sopenharmony_ci	if (diff >= x->props.replay_window) {
1248c2ecf20Sopenharmony_ci		x->stats.replay_window++;
1258c2ecf20Sopenharmony_ci		goto err;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (x->replay.bitmap & (1U << diff)) {
1298c2ecf20Sopenharmony_ci		x->stats.replay++;
1308c2ecf20Sopenharmony_ci		goto err;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cierr:
1358c2ecf20Sopenharmony_ci	xfrm_audit_state_replay(x, skb, net_seq);
1368c2ecf20Sopenharmony_ci	return -EINVAL;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	u32 diff;
1428c2ecf20Sopenharmony_ci	u32 seq = ntohl(net_seq);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!x->props.replay_window)
1458c2ecf20Sopenharmony_ci		return;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (seq > x->replay.seq) {
1488c2ecf20Sopenharmony_ci		diff = seq - x->replay.seq;
1498c2ecf20Sopenharmony_ci		if (diff < x->props.replay_window)
1508c2ecf20Sopenharmony_ci			x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
1518c2ecf20Sopenharmony_ci		else
1528c2ecf20Sopenharmony_ci			x->replay.bitmap = 1;
1538c2ecf20Sopenharmony_ci		x->replay.seq = seq;
1548c2ecf20Sopenharmony_ci	} else {
1558c2ecf20Sopenharmony_ci		diff = x->replay.seq - seq;
1568c2ecf20Sopenharmony_ci		x->replay.bitmap |= (1U << diff);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (xfrm_aevent_is_on(xs_net(x)))
1608c2ecf20Sopenharmony_ci		x->repl->notify(x, XFRM_REPLAY_UPDATE);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int xfrm_replay_overflow_bmp(struct xfrm_state *x, struct sk_buff *skb)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	int err = 0;
1668c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
1678c2ecf20Sopenharmony_ci	struct net *net = xs_net(x);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
1708c2ecf20Sopenharmony_ci		XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq;
1718c2ecf20Sopenharmony_ci		XFRM_SKB_CB(skb)->seq.output.hi = 0;
1728c2ecf20Sopenharmony_ci		if (unlikely(replay_esn->oseq == 0) &&
1738c2ecf20Sopenharmony_ci		    !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) {
1748c2ecf20Sopenharmony_ci			replay_esn->oseq--;
1758c2ecf20Sopenharmony_ci			xfrm_audit_state_replay_overflow(x, skb);
1768c2ecf20Sopenharmony_ci			err = -EOVERFLOW;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci			return err;
1798c2ecf20Sopenharmony_ci		}
1808c2ecf20Sopenharmony_ci		if (xfrm_aevent_is_on(net))
1818c2ecf20Sopenharmony_ci			x->repl->notify(x, XFRM_REPLAY_UPDATE);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return err;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int xfrm_replay_check_bmp(struct xfrm_state *x,
1888c2ecf20Sopenharmony_ci				 struct sk_buff *skb, __be32 net_seq)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	unsigned int bitnr, nr;
1918c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
1928c2ecf20Sopenharmony_ci	u32 pos;
1938c2ecf20Sopenharmony_ci	u32 seq = ntohl(net_seq);
1948c2ecf20Sopenharmony_ci	u32 diff =  replay_esn->seq - seq;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (!replay_esn->replay_window)
1978c2ecf20Sopenharmony_ci		return 0;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (unlikely(seq == 0))
2008c2ecf20Sopenharmony_ci		goto err;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (likely(seq > replay_esn->seq))
2038c2ecf20Sopenharmony_ci		return 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (diff >= replay_esn->replay_window) {
2068c2ecf20Sopenharmony_ci		x->stats.replay_window++;
2078c2ecf20Sopenharmony_ci		goto err;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	pos = (replay_esn->seq - 1) % replay_esn->replay_window;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (pos >= diff)
2138c2ecf20Sopenharmony_ci		bitnr = (pos - diff) % replay_esn->replay_window;
2148c2ecf20Sopenharmony_ci	else
2158c2ecf20Sopenharmony_ci		bitnr = replay_esn->replay_window - (diff - pos);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	nr = bitnr >> 5;
2188c2ecf20Sopenharmony_ci	bitnr = bitnr & 0x1F;
2198c2ecf20Sopenharmony_ci	if (replay_esn->bmp[nr] & (1U << bitnr))
2208c2ecf20Sopenharmony_ci		goto err_replay;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cierr_replay:
2258c2ecf20Sopenharmony_ci	x->stats.replay++;
2268c2ecf20Sopenharmony_cierr:
2278c2ecf20Sopenharmony_ci	xfrm_audit_state_replay(x, skb, net_seq);
2288c2ecf20Sopenharmony_ci	return -EINVAL;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	unsigned int bitnr, nr, i;
2348c2ecf20Sopenharmony_ci	u32 diff;
2358c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
2368c2ecf20Sopenharmony_ci	u32 seq = ntohl(net_seq);
2378c2ecf20Sopenharmony_ci	u32 pos;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (!replay_esn->replay_window)
2408c2ecf20Sopenharmony_ci		return;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	pos = (replay_esn->seq - 1) % replay_esn->replay_window;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (seq > replay_esn->seq) {
2458c2ecf20Sopenharmony_ci		diff = seq - replay_esn->seq;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		if (diff < replay_esn->replay_window) {
2488c2ecf20Sopenharmony_ci			for (i = 1; i < diff; i++) {
2498c2ecf20Sopenharmony_ci				bitnr = (pos + i) % replay_esn->replay_window;
2508c2ecf20Sopenharmony_ci				nr = bitnr >> 5;
2518c2ecf20Sopenharmony_ci				bitnr = bitnr & 0x1F;
2528c2ecf20Sopenharmony_ci				replay_esn->bmp[nr] &=  ~(1U << bitnr);
2538c2ecf20Sopenharmony_ci			}
2548c2ecf20Sopenharmony_ci		} else {
2558c2ecf20Sopenharmony_ci			nr = (replay_esn->replay_window - 1) >> 5;
2568c2ecf20Sopenharmony_ci			for (i = 0; i <= nr; i++)
2578c2ecf20Sopenharmony_ci				replay_esn->bmp[i] = 0;
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		bitnr = (pos + diff) % replay_esn->replay_window;
2618c2ecf20Sopenharmony_ci		replay_esn->seq = seq;
2628c2ecf20Sopenharmony_ci	} else {
2638c2ecf20Sopenharmony_ci		diff = replay_esn->seq - seq;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		if (pos >= diff)
2668c2ecf20Sopenharmony_ci			bitnr = (pos - diff) % replay_esn->replay_window;
2678c2ecf20Sopenharmony_ci		else
2688c2ecf20Sopenharmony_ci			bitnr = replay_esn->replay_window - (diff - pos);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	nr = bitnr >> 5;
2728c2ecf20Sopenharmony_ci	bitnr = bitnr & 0x1F;
2738c2ecf20Sopenharmony_ci	replay_esn->bmp[nr] |= (1U << bitnr);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (xfrm_aevent_is_on(xs_net(x)))
2768c2ecf20Sopenharmony_ci		x->repl->notify(x, XFRM_REPLAY_UPDATE);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct km_event c;
2828c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
2838c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* we send notify messages in case
2868c2ecf20Sopenharmony_ci	 *  1. we updated on of the sequence numbers, and the seqno difference
2878c2ecf20Sopenharmony_ci	 *     is at least x->replay_maxdiff, in this case we also update the
2888c2ecf20Sopenharmony_ci	 *     timeout of our timer function
2898c2ecf20Sopenharmony_ci	 *  2. if x->replay_maxage has elapsed since last update,
2908c2ecf20Sopenharmony_ci	 *     and there were changes
2918c2ecf20Sopenharmony_ci	 *
2928c2ecf20Sopenharmony_ci	 *  The state structure must be locked!
2938c2ecf20Sopenharmony_ci	 */
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	switch (event) {
2968c2ecf20Sopenharmony_ci	case XFRM_REPLAY_UPDATE:
2978c2ecf20Sopenharmony_ci		if (!x->replay_maxdiff ||
2988c2ecf20Sopenharmony_ci		    ((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
2998c2ecf20Sopenharmony_ci		    (replay_esn->oseq - preplay_esn->oseq
3008c2ecf20Sopenharmony_ci		     < x->replay_maxdiff))) {
3018c2ecf20Sopenharmony_ci			if (x->xflags & XFRM_TIME_DEFER)
3028c2ecf20Sopenharmony_ci				event = XFRM_REPLAY_TIMEOUT;
3038c2ecf20Sopenharmony_ci			else
3048c2ecf20Sopenharmony_ci				return;
3058c2ecf20Sopenharmony_ci		}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		break;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	case XFRM_REPLAY_TIMEOUT:
3108c2ecf20Sopenharmony_ci		if (memcmp(x->replay_esn, x->preplay_esn,
3118c2ecf20Sopenharmony_ci			   xfrm_replay_state_esn_len(replay_esn)) == 0) {
3128c2ecf20Sopenharmony_ci			x->xflags |= XFRM_TIME_DEFER;
3138c2ecf20Sopenharmony_ci			return;
3148c2ecf20Sopenharmony_ci		}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	memcpy(x->preplay_esn, x->replay_esn,
3208c2ecf20Sopenharmony_ci	       xfrm_replay_state_esn_len(replay_esn));
3218c2ecf20Sopenharmony_ci	c.event = XFRM_MSG_NEWAE;
3228c2ecf20Sopenharmony_ci	c.data.aevent = event;
3238c2ecf20Sopenharmony_ci	km_state_notify(x, &c);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (x->replay_maxage &&
3268c2ecf20Sopenharmony_ci	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
3278c2ecf20Sopenharmony_ci		x->xflags &= ~XFRM_TIME_DEFER;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void xfrm_replay_notify_esn(struct xfrm_state *x, int event)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	u32 seq_diff, oseq_diff;
3338c2ecf20Sopenharmony_ci	struct km_event c;
3348c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
3358c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* we send notify messages in case
3388c2ecf20Sopenharmony_ci	 *  1. we updated on of the sequence numbers, and the seqno difference
3398c2ecf20Sopenharmony_ci	 *     is at least x->replay_maxdiff, in this case we also update the
3408c2ecf20Sopenharmony_ci	 *     timeout of our timer function
3418c2ecf20Sopenharmony_ci	 *  2. if x->replay_maxage has elapsed since last update,
3428c2ecf20Sopenharmony_ci	 *     and there were changes
3438c2ecf20Sopenharmony_ci	 *
3448c2ecf20Sopenharmony_ci	 *  The state structure must be locked!
3458c2ecf20Sopenharmony_ci	 */
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	switch (event) {
3488c2ecf20Sopenharmony_ci	case XFRM_REPLAY_UPDATE:
3498c2ecf20Sopenharmony_ci		if (x->replay_maxdiff) {
3508c2ecf20Sopenharmony_ci			if (replay_esn->seq_hi == preplay_esn->seq_hi)
3518c2ecf20Sopenharmony_ci				seq_diff = replay_esn->seq - preplay_esn->seq;
3528c2ecf20Sopenharmony_ci			else
3538c2ecf20Sopenharmony_ci				seq_diff = ~preplay_esn->seq + replay_esn->seq
3548c2ecf20Sopenharmony_ci					   + 1;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci			if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
3578c2ecf20Sopenharmony_ci				oseq_diff = replay_esn->oseq
3588c2ecf20Sopenharmony_ci					    - preplay_esn->oseq;
3598c2ecf20Sopenharmony_ci			else
3608c2ecf20Sopenharmony_ci				oseq_diff = ~preplay_esn->oseq
3618c2ecf20Sopenharmony_ci					    + replay_esn->oseq + 1;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci			if (seq_diff >= x->replay_maxdiff ||
3648c2ecf20Sopenharmony_ci			    oseq_diff >= x->replay_maxdiff)
3658c2ecf20Sopenharmony_ci				break;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		if (x->xflags & XFRM_TIME_DEFER)
3698c2ecf20Sopenharmony_ci			event = XFRM_REPLAY_TIMEOUT;
3708c2ecf20Sopenharmony_ci		else
3718c2ecf20Sopenharmony_ci			return;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		break;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	case XFRM_REPLAY_TIMEOUT:
3768c2ecf20Sopenharmony_ci		if (memcmp(x->replay_esn, x->preplay_esn,
3778c2ecf20Sopenharmony_ci			   xfrm_replay_state_esn_len(replay_esn)) == 0) {
3788c2ecf20Sopenharmony_ci			x->xflags |= XFRM_TIME_DEFER;
3798c2ecf20Sopenharmony_ci			return;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		break;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	memcpy(x->preplay_esn, x->replay_esn,
3868c2ecf20Sopenharmony_ci	       xfrm_replay_state_esn_len(replay_esn));
3878c2ecf20Sopenharmony_ci	c.event = XFRM_MSG_NEWAE;
3888c2ecf20Sopenharmony_ci	c.data.aevent = event;
3898c2ecf20Sopenharmony_ci	km_state_notify(x, &c);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (x->replay_maxage &&
3928c2ecf20Sopenharmony_ci	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
3938c2ecf20Sopenharmony_ci		x->xflags &= ~XFRM_TIME_DEFER;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int xfrm_replay_overflow_esn(struct xfrm_state *x, struct sk_buff *skb)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	int err = 0;
3998c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
4008c2ecf20Sopenharmony_ci	struct net *net = xs_net(x);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
4038c2ecf20Sopenharmony_ci		XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq;
4048c2ecf20Sopenharmony_ci		XFRM_SKB_CB(skb)->seq.output.hi = replay_esn->oseq_hi;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		if (unlikely(replay_esn->oseq == 0)) {
4078c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.hi = ++replay_esn->oseq_hi;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci			if (replay_esn->oseq_hi == 0) {
4108c2ecf20Sopenharmony_ci				replay_esn->oseq--;
4118c2ecf20Sopenharmony_ci				replay_esn->oseq_hi--;
4128c2ecf20Sopenharmony_ci				xfrm_audit_state_replay_overflow(x, skb);
4138c2ecf20Sopenharmony_ci				err = -EOVERFLOW;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci				return err;
4168c2ecf20Sopenharmony_ci			}
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci		if (xfrm_aevent_is_on(net))
4198c2ecf20Sopenharmony_ci			x->repl->notify(x, XFRM_REPLAY_UPDATE);
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return err;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int xfrm_replay_check_esn(struct xfrm_state *x,
4268c2ecf20Sopenharmony_ci				 struct sk_buff *skb, __be32 net_seq)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	unsigned int bitnr, nr;
4298c2ecf20Sopenharmony_ci	u32 diff;
4308c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
4318c2ecf20Sopenharmony_ci	u32 pos;
4328c2ecf20Sopenharmony_ci	u32 seq = ntohl(net_seq);
4338c2ecf20Sopenharmony_ci	u32 wsize = replay_esn->replay_window;
4348c2ecf20Sopenharmony_ci	u32 top = replay_esn->seq;
4358c2ecf20Sopenharmony_ci	u32 bottom = top - wsize + 1;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (!wsize)
4388c2ecf20Sopenharmony_ci		return 0;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (unlikely(seq == 0 && replay_esn->seq_hi == 0 &&
4418c2ecf20Sopenharmony_ci		     (replay_esn->seq < replay_esn->replay_window - 1)))
4428c2ecf20Sopenharmony_ci		goto err;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	diff = top - seq;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (likely(top >= wsize - 1)) {
4478c2ecf20Sopenharmony_ci		/* A. same subspace */
4488c2ecf20Sopenharmony_ci		if (likely(seq > top) || seq < bottom)
4498c2ecf20Sopenharmony_ci			return 0;
4508c2ecf20Sopenharmony_ci	} else {
4518c2ecf20Sopenharmony_ci		/* B. window spans two subspaces */
4528c2ecf20Sopenharmony_ci		if (likely(seq > top && seq < bottom))
4538c2ecf20Sopenharmony_ci			return 0;
4548c2ecf20Sopenharmony_ci		if (seq >= bottom)
4558c2ecf20Sopenharmony_ci			diff = ~seq + top + 1;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (diff >= replay_esn->replay_window) {
4598c2ecf20Sopenharmony_ci		x->stats.replay_window++;
4608c2ecf20Sopenharmony_ci		goto err;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	pos = (replay_esn->seq - 1) % replay_esn->replay_window;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (pos >= diff)
4668c2ecf20Sopenharmony_ci		bitnr = (pos - diff) % replay_esn->replay_window;
4678c2ecf20Sopenharmony_ci	else
4688c2ecf20Sopenharmony_ci		bitnr = replay_esn->replay_window - (diff - pos);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	nr = bitnr >> 5;
4718c2ecf20Sopenharmony_ci	bitnr = bitnr & 0x1F;
4728c2ecf20Sopenharmony_ci	if (replay_esn->bmp[nr] & (1U << bitnr))
4738c2ecf20Sopenharmony_ci		goto err_replay;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return 0;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cierr_replay:
4788c2ecf20Sopenharmony_ci	x->stats.replay++;
4798c2ecf20Sopenharmony_cierr:
4808c2ecf20Sopenharmony_ci	xfrm_audit_state_replay(x, skb, net_seq);
4818c2ecf20Sopenharmony_ci	return -EINVAL;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic int xfrm_replay_recheck_esn(struct xfrm_state *x,
4858c2ecf20Sopenharmony_ci				   struct sk_buff *skb, __be32 net_seq)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	if (unlikely(XFRM_SKB_CB(skb)->seq.input.hi !=
4888c2ecf20Sopenharmony_ci		     htonl(xfrm_replay_seqhi(x, net_seq)))) {
4898c2ecf20Sopenharmony_ci			x->stats.replay_window++;
4908c2ecf20Sopenharmony_ci			return -EINVAL;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	return xfrm_replay_check_esn(x, skb, net_seq);
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	unsigned int bitnr, nr, i;
4998c2ecf20Sopenharmony_ci	int wrap;
5008c2ecf20Sopenharmony_ci	u32 diff, pos, seq, seq_hi;
5018c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (!replay_esn->replay_window)
5048c2ecf20Sopenharmony_ci		return;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	seq = ntohl(net_seq);
5078c2ecf20Sopenharmony_ci	pos = (replay_esn->seq - 1) % replay_esn->replay_window;
5088c2ecf20Sopenharmony_ci	seq_hi = xfrm_replay_seqhi(x, net_seq);
5098c2ecf20Sopenharmony_ci	wrap = seq_hi - replay_esn->seq_hi;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if ((!wrap && seq > replay_esn->seq) || wrap > 0) {
5128c2ecf20Sopenharmony_ci		if (likely(!wrap))
5138c2ecf20Sopenharmony_ci			diff = seq - replay_esn->seq;
5148c2ecf20Sopenharmony_ci		else
5158c2ecf20Sopenharmony_ci			diff = ~replay_esn->seq + seq + 1;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		if (diff < replay_esn->replay_window) {
5188c2ecf20Sopenharmony_ci			for (i = 1; i < diff; i++) {
5198c2ecf20Sopenharmony_ci				bitnr = (pos + i) % replay_esn->replay_window;
5208c2ecf20Sopenharmony_ci				nr = bitnr >> 5;
5218c2ecf20Sopenharmony_ci				bitnr = bitnr & 0x1F;
5228c2ecf20Sopenharmony_ci				replay_esn->bmp[nr] &=  ~(1U << bitnr);
5238c2ecf20Sopenharmony_ci			}
5248c2ecf20Sopenharmony_ci		} else {
5258c2ecf20Sopenharmony_ci			nr = (replay_esn->replay_window - 1) >> 5;
5268c2ecf20Sopenharmony_ci			for (i = 0; i <= nr; i++)
5278c2ecf20Sopenharmony_ci				replay_esn->bmp[i] = 0;
5288c2ecf20Sopenharmony_ci		}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		bitnr = (pos + diff) % replay_esn->replay_window;
5318c2ecf20Sopenharmony_ci		replay_esn->seq = seq;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		if (unlikely(wrap > 0))
5348c2ecf20Sopenharmony_ci			replay_esn->seq_hi++;
5358c2ecf20Sopenharmony_ci	} else {
5368c2ecf20Sopenharmony_ci		diff = replay_esn->seq - seq;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci		if (pos >= diff)
5398c2ecf20Sopenharmony_ci			bitnr = (pos - diff) % replay_esn->replay_window;
5408c2ecf20Sopenharmony_ci		else
5418c2ecf20Sopenharmony_ci			bitnr = replay_esn->replay_window - (diff - pos);
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	xfrm_dev_state_advance_esn(x);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	nr = bitnr >> 5;
5478c2ecf20Sopenharmony_ci	bitnr = bitnr & 0x1F;
5488c2ecf20Sopenharmony_ci	replay_esn->bmp[nr] |= (1U << bitnr);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (xfrm_aevent_is_on(xs_net(x)))
5518c2ecf20Sopenharmony_ci		x->repl->notify(x, XFRM_REPLAY_UPDATE);
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM_OFFLOAD
5558c2ecf20Sopenharmony_cistatic int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *skb)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	int err = 0;
5588c2ecf20Sopenharmony_ci	struct net *net = xs_net(x);
5598c2ecf20Sopenharmony_ci	struct xfrm_offload *xo = xfrm_offload(skb);
5608c2ecf20Sopenharmony_ci	__u32 oseq = x->replay.oseq;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (!xo)
5638c2ecf20Sopenharmony_ci		return xfrm_replay_overflow(x, skb);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
5668c2ecf20Sopenharmony_ci		if (!skb_is_gso(skb)) {
5678c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
5688c2ecf20Sopenharmony_ci			xo->seq.low = oseq;
5698c2ecf20Sopenharmony_ci		} else {
5708c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
5718c2ecf20Sopenharmony_ci			xo->seq.low = oseq + 1;
5728c2ecf20Sopenharmony_ci			oseq += skb_shinfo(skb)->gso_segs;
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		XFRM_SKB_CB(skb)->seq.output.hi = 0;
5768c2ecf20Sopenharmony_ci		xo->seq.hi = 0;
5778c2ecf20Sopenharmony_ci		if (unlikely(oseq < x->replay.oseq) &&
5788c2ecf20Sopenharmony_ci		    !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) {
5798c2ecf20Sopenharmony_ci			xfrm_audit_state_replay_overflow(x, skb);
5808c2ecf20Sopenharmony_ci			err = -EOVERFLOW;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci			return err;
5838c2ecf20Sopenharmony_ci		}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		x->replay.oseq = oseq;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci		if (xfrm_aevent_is_on(net))
5888c2ecf20Sopenharmony_ci			x->repl->notify(x, XFRM_REPLAY_UPDATE);
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return err;
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic int xfrm_replay_overflow_offload_bmp(struct xfrm_state *x, struct sk_buff *skb)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	int err = 0;
5978c2ecf20Sopenharmony_ci	struct xfrm_offload *xo = xfrm_offload(skb);
5988c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
5998c2ecf20Sopenharmony_ci	struct net *net = xs_net(x);
6008c2ecf20Sopenharmony_ci	__u32 oseq = replay_esn->oseq;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	if (!xo)
6038c2ecf20Sopenharmony_ci		return xfrm_replay_overflow_bmp(x, skb);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
6068c2ecf20Sopenharmony_ci		if (!skb_is_gso(skb)) {
6078c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
6088c2ecf20Sopenharmony_ci			xo->seq.low = oseq;
6098c2ecf20Sopenharmony_ci		} else {
6108c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
6118c2ecf20Sopenharmony_ci			xo->seq.low = oseq + 1;
6128c2ecf20Sopenharmony_ci			oseq += skb_shinfo(skb)->gso_segs;
6138c2ecf20Sopenharmony_ci		}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci		XFRM_SKB_CB(skb)->seq.output.hi = 0;
6168c2ecf20Sopenharmony_ci		xo->seq.hi = 0;
6178c2ecf20Sopenharmony_ci		if (unlikely(oseq < replay_esn->oseq) &&
6188c2ecf20Sopenharmony_ci		    !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) {
6198c2ecf20Sopenharmony_ci			xfrm_audit_state_replay_overflow(x, skb);
6208c2ecf20Sopenharmony_ci			err = -EOVERFLOW;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci			return err;
6238c2ecf20Sopenharmony_ci		} else {
6248c2ecf20Sopenharmony_ci			replay_esn->oseq = oseq;
6258c2ecf20Sopenharmony_ci		}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		if (xfrm_aevent_is_on(net))
6288c2ecf20Sopenharmony_ci			x->repl->notify(x, XFRM_REPLAY_UPDATE);
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	return err;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff *skb)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	int err = 0;
6378c2ecf20Sopenharmony_ci	struct xfrm_offload *xo = xfrm_offload(skb);
6388c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
6398c2ecf20Sopenharmony_ci	struct net *net = xs_net(x);
6408c2ecf20Sopenharmony_ci	__u32 oseq = replay_esn->oseq;
6418c2ecf20Sopenharmony_ci	__u32 oseq_hi = replay_esn->oseq_hi;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (!xo)
6448c2ecf20Sopenharmony_ci		return xfrm_replay_overflow_esn(x, skb);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
6478c2ecf20Sopenharmony_ci		if (!skb_is_gso(skb)) {
6488c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
6498c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi;
6508c2ecf20Sopenharmony_ci			xo->seq.low = oseq;
6518c2ecf20Sopenharmony_ci			xo->seq.hi = oseq_hi;
6528c2ecf20Sopenharmony_ci		} else {
6538c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
6548c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi;
6558c2ecf20Sopenharmony_ci			xo->seq.low = oseq + 1;
6568c2ecf20Sopenharmony_ci			xo->seq.hi = oseq_hi;
6578c2ecf20Sopenharmony_ci			oseq += skb_shinfo(skb)->gso_segs;
6588c2ecf20Sopenharmony_ci		}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci		if (unlikely(xo->seq.low < replay_esn->oseq)) {
6618c2ecf20Sopenharmony_ci			XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi;
6628c2ecf20Sopenharmony_ci			xo->seq.hi = oseq_hi;
6638c2ecf20Sopenharmony_ci			replay_esn->oseq_hi = oseq_hi;
6648c2ecf20Sopenharmony_ci			if (replay_esn->oseq_hi == 0) {
6658c2ecf20Sopenharmony_ci				replay_esn->oseq--;
6668c2ecf20Sopenharmony_ci				replay_esn->oseq_hi--;
6678c2ecf20Sopenharmony_ci				xfrm_audit_state_replay_overflow(x, skb);
6688c2ecf20Sopenharmony_ci				err = -EOVERFLOW;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci				return err;
6718c2ecf20Sopenharmony_ci			}
6728c2ecf20Sopenharmony_ci		}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci		replay_esn->oseq = oseq;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci		if (xfrm_aevent_is_on(net))
6778c2ecf20Sopenharmony_ci			x->repl->notify(x, XFRM_REPLAY_UPDATE);
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return err;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cistatic const struct xfrm_replay xfrm_replay_legacy = {
6848c2ecf20Sopenharmony_ci	.advance	= xfrm_replay_advance,
6858c2ecf20Sopenharmony_ci	.check		= xfrm_replay_check,
6868c2ecf20Sopenharmony_ci	.recheck	= xfrm_replay_check,
6878c2ecf20Sopenharmony_ci	.notify		= xfrm_replay_notify,
6888c2ecf20Sopenharmony_ci	.overflow	= xfrm_replay_overflow_offload,
6898c2ecf20Sopenharmony_ci};
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic const struct xfrm_replay xfrm_replay_bmp = {
6928c2ecf20Sopenharmony_ci	.advance	= xfrm_replay_advance_bmp,
6938c2ecf20Sopenharmony_ci	.check		= xfrm_replay_check_bmp,
6948c2ecf20Sopenharmony_ci	.recheck	= xfrm_replay_check_bmp,
6958c2ecf20Sopenharmony_ci	.notify		= xfrm_replay_notify_bmp,
6968c2ecf20Sopenharmony_ci	.overflow	= xfrm_replay_overflow_offload_bmp,
6978c2ecf20Sopenharmony_ci};
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic const struct xfrm_replay xfrm_replay_esn = {
7008c2ecf20Sopenharmony_ci	.advance	= xfrm_replay_advance_esn,
7018c2ecf20Sopenharmony_ci	.check		= xfrm_replay_check_esn,
7028c2ecf20Sopenharmony_ci	.recheck	= xfrm_replay_recheck_esn,
7038c2ecf20Sopenharmony_ci	.notify		= xfrm_replay_notify_esn,
7048c2ecf20Sopenharmony_ci	.overflow	= xfrm_replay_overflow_offload_esn,
7058c2ecf20Sopenharmony_ci};
7068c2ecf20Sopenharmony_ci#else
7078c2ecf20Sopenharmony_cistatic const struct xfrm_replay xfrm_replay_legacy = {
7088c2ecf20Sopenharmony_ci	.advance	= xfrm_replay_advance,
7098c2ecf20Sopenharmony_ci	.check		= xfrm_replay_check,
7108c2ecf20Sopenharmony_ci	.recheck	= xfrm_replay_check,
7118c2ecf20Sopenharmony_ci	.notify		= xfrm_replay_notify,
7128c2ecf20Sopenharmony_ci	.overflow	= xfrm_replay_overflow,
7138c2ecf20Sopenharmony_ci};
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic const struct xfrm_replay xfrm_replay_bmp = {
7168c2ecf20Sopenharmony_ci	.advance	= xfrm_replay_advance_bmp,
7178c2ecf20Sopenharmony_ci	.check		= xfrm_replay_check_bmp,
7188c2ecf20Sopenharmony_ci	.recheck	= xfrm_replay_check_bmp,
7198c2ecf20Sopenharmony_ci	.notify		= xfrm_replay_notify_bmp,
7208c2ecf20Sopenharmony_ci	.overflow	= xfrm_replay_overflow_bmp,
7218c2ecf20Sopenharmony_ci};
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic const struct xfrm_replay xfrm_replay_esn = {
7248c2ecf20Sopenharmony_ci	.advance	= xfrm_replay_advance_esn,
7258c2ecf20Sopenharmony_ci	.check		= xfrm_replay_check_esn,
7268c2ecf20Sopenharmony_ci	.recheck	= xfrm_replay_recheck_esn,
7278c2ecf20Sopenharmony_ci	.notify		= xfrm_replay_notify_esn,
7288c2ecf20Sopenharmony_ci	.overflow	= xfrm_replay_overflow_esn,
7298c2ecf20Sopenharmony_ci};
7308c2ecf20Sopenharmony_ci#endif
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ciint xfrm_init_replay(struct xfrm_state *x)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (replay_esn) {
7378c2ecf20Sopenharmony_ci		if (replay_esn->replay_window >
7388c2ecf20Sopenharmony_ci		    replay_esn->bmp_len * sizeof(__u32) * 8)
7398c2ecf20Sopenharmony_ci			return -EINVAL;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci		if (x->props.flags & XFRM_STATE_ESN) {
7428c2ecf20Sopenharmony_ci			if (replay_esn->replay_window == 0)
7438c2ecf20Sopenharmony_ci				return -EINVAL;
7448c2ecf20Sopenharmony_ci			x->repl = &xfrm_replay_esn;
7458c2ecf20Sopenharmony_ci		} else {
7468c2ecf20Sopenharmony_ci			x->repl = &xfrm_replay_bmp;
7478c2ecf20Sopenharmony_ci		}
7488c2ecf20Sopenharmony_ci	} else {
7498c2ecf20Sopenharmony_ci		x->repl = &xfrm_replay_legacy;
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	return 0;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm_init_replay);
755