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