162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 secunet Security Networks AG 662306a36Sopenharmony_ci * Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <net/xfrm.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciu32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci u32 seq, seq_hi, bottom; 1562306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci if (!(x->props.flags & XFRM_STATE_ESN)) 1862306a36Sopenharmony_ci return 0; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci seq = ntohl(net_seq); 2162306a36Sopenharmony_ci seq_hi = replay_esn->seq_hi; 2262306a36Sopenharmony_ci bottom = replay_esn->seq - replay_esn->replay_window + 1; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci if (likely(replay_esn->seq >= replay_esn->replay_window - 1)) { 2562306a36Sopenharmony_ci /* A. same subspace */ 2662306a36Sopenharmony_ci if (unlikely(seq < bottom)) 2762306a36Sopenharmony_ci seq_hi++; 2862306a36Sopenharmony_ci } else { 2962306a36Sopenharmony_ci /* B. window spans two subspaces */ 3062306a36Sopenharmony_ci if (unlikely(seq >= bottom)) 3162306a36Sopenharmony_ci seq_hi--; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci return seq_hi; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm_replay_seqhi); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void xfrm_replay_notify_bmp(struct xfrm_state *x, int event); 3962306a36Sopenharmony_cistatic void xfrm_replay_notify_esn(struct xfrm_state *x, int event); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_civoid xfrm_replay_notify(struct xfrm_state *x, int event) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct km_event c; 4462306a36Sopenharmony_ci /* we send notify messages in case 4562306a36Sopenharmony_ci * 1. we updated on of the sequence numbers, and the seqno difference 4662306a36Sopenharmony_ci * is at least x->replay_maxdiff, in this case we also update the 4762306a36Sopenharmony_ci * timeout of our timer function 4862306a36Sopenharmony_ci * 2. if x->replay_maxage has elapsed since last update, 4962306a36Sopenharmony_ci * and there were changes 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * The state structure must be locked! 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci switch (x->repl_mode) { 5562306a36Sopenharmony_ci case XFRM_REPLAY_MODE_LEGACY: 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci case XFRM_REPLAY_MODE_BMP: 5862306a36Sopenharmony_ci xfrm_replay_notify_bmp(x, event); 5962306a36Sopenharmony_ci return; 6062306a36Sopenharmony_ci case XFRM_REPLAY_MODE_ESN: 6162306a36Sopenharmony_ci xfrm_replay_notify_esn(x, event); 6262306a36Sopenharmony_ci return; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci switch (event) { 6662306a36Sopenharmony_ci case XFRM_REPLAY_UPDATE: 6762306a36Sopenharmony_ci if (!x->replay_maxdiff || 6862306a36Sopenharmony_ci ((x->replay.seq - x->preplay.seq < x->replay_maxdiff) && 6962306a36Sopenharmony_ci (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) { 7062306a36Sopenharmony_ci if (x->xflags & XFRM_TIME_DEFER) 7162306a36Sopenharmony_ci event = XFRM_REPLAY_TIMEOUT; 7262306a36Sopenharmony_ci else 7362306a36Sopenharmony_ci return; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci case XFRM_REPLAY_TIMEOUT: 7962306a36Sopenharmony_ci if (memcmp(&x->replay, &x->preplay, 8062306a36Sopenharmony_ci sizeof(struct xfrm_replay_state)) == 0) { 8162306a36Sopenharmony_ci x->xflags |= XFRM_TIME_DEFER; 8262306a36Sopenharmony_ci return; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state)); 8962306a36Sopenharmony_ci c.event = XFRM_MSG_NEWAE; 9062306a36Sopenharmony_ci c.data.aevent = event; 9162306a36Sopenharmony_ci km_state_notify(x, &c); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (x->replay_maxage && 9462306a36Sopenharmony_ci !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) 9562306a36Sopenharmony_ci x->xflags &= ~XFRM_TIME_DEFER; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int __xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci int err = 0; 10162306a36Sopenharmony_ci struct net *net = xs_net(x); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 10462306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = ++x->replay.oseq; 10562306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = 0; 10662306a36Sopenharmony_ci if (unlikely(x->replay.oseq == 0) && 10762306a36Sopenharmony_ci !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { 10862306a36Sopenharmony_ci x->replay.oseq--; 10962306a36Sopenharmony_ci xfrm_audit_state_replay_overflow(x, skb); 11062306a36Sopenharmony_ci err = -EOVERFLOW; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return err; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci if (xfrm_aevent_is_on(net)) 11562306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return err; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int xfrm_replay_check_legacy(struct xfrm_state *x, 12262306a36Sopenharmony_ci struct sk_buff *skb, __be32 net_seq) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci u32 diff; 12562306a36Sopenharmony_ci u32 seq = ntohl(net_seq); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!x->props.replay_window) 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (unlikely(seq == 0)) 13162306a36Sopenharmony_ci goto err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (likely(seq > x->replay.seq)) 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci diff = x->replay.seq - seq; 13762306a36Sopenharmony_ci if (diff >= x->props.replay_window) { 13862306a36Sopenharmony_ci x->stats.replay_window++; 13962306a36Sopenharmony_ci goto err; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (x->replay.bitmap & (1U << diff)) { 14362306a36Sopenharmony_ci x->stats.replay++; 14462306a36Sopenharmony_ci goto err; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cierr: 14962306a36Sopenharmony_ci xfrm_audit_state_replay(x, skb, net_seq); 15062306a36Sopenharmony_ci return -EINVAL; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq); 15462306a36Sopenharmony_cistatic void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_civoid xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci u32 diff, seq; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci switch (x->repl_mode) { 16162306a36Sopenharmony_ci case XFRM_REPLAY_MODE_LEGACY: 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci case XFRM_REPLAY_MODE_BMP: 16462306a36Sopenharmony_ci return xfrm_replay_advance_bmp(x, net_seq); 16562306a36Sopenharmony_ci case XFRM_REPLAY_MODE_ESN: 16662306a36Sopenharmony_ci return xfrm_replay_advance_esn(x, net_seq); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!x->props.replay_window) 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci seq = ntohl(net_seq); 17362306a36Sopenharmony_ci if (seq > x->replay.seq) { 17462306a36Sopenharmony_ci diff = seq - x->replay.seq; 17562306a36Sopenharmony_ci if (diff < x->props.replay_window) 17662306a36Sopenharmony_ci x->replay.bitmap = ((x->replay.bitmap) << diff) | 1; 17762306a36Sopenharmony_ci else 17862306a36Sopenharmony_ci x->replay.bitmap = 1; 17962306a36Sopenharmony_ci x->replay.seq = seq; 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci diff = x->replay.seq - seq; 18262306a36Sopenharmony_ci x->replay.bitmap |= (1U << diff); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (xfrm_aevent_is_on(xs_net(x))) 18662306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int xfrm_replay_overflow_bmp(struct xfrm_state *x, struct sk_buff *skb) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int err = 0; 19262306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 19362306a36Sopenharmony_ci struct net *net = xs_net(x); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 19662306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq; 19762306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = 0; 19862306a36Sopenharmony_ci if (unlikely(replay_esn->oseq == 0) && 19962306a36Sopenharmony_ci !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { 20062306a36Sopenharmony_ci replay_esn->oseq--; 20162306a36Sopenharmony_ci xfrm_audit_state_replay_overflow(x, skb); 20262306a36Sopenharmony_ci err = -EOVERFLOW; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return err; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci if (xfrm_aevent_is_on(net)) 20762306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return err; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int xfrm_replay_check_bmp(struct xfrm_state *x, 21462306a36Sopenharmony_ci struct sk_buff *skb, __be32 net_seq) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci unsigned int bitnr, nr; 21762306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 21862306a36Sopenharmony_ci u32 pos; 21962306a36Sopenharmony_ci u32 seq = ntohl(net_seq); 22062306a36Sopenharmony_ci u32 diff = replay_esn->seq - seq; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!replay_esn->replay_window) 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (unlikely(seq == 0)) 22662306a36Sopenharmony_ci goto err; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (likely(seq > replay_esn->seq)) 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (diff >= replay_esn->replay_window) { 23262306a36Sopenharmony_ci x->stats.replay_window++; 23362306a36Sopenharmony_ci goto err; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci pos = (replay_esn->seq - 1) % replay_esn->replay_window; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (pos >= diff) 23962306a36Sopenharmony_ci bitnr = (pos - diff) % replay_esn->replay_window; 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci bitnr = replay_esn->replay_window - (diff - pos); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci nr = bitnr >> 5; 24462306a36Sopenharmony_ci bitnr = bitnr & 0x1F; 24562306a36Sopenharmony_ci if (replay_esn->bmp[nr] & (1U << bitnr)) 24662306a36Sopenharmony_ci goto err_replay; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cierr_replay: 25162306a36Sopenharmony_ci x->stats.replay++; 25262306a36Sopenharmony_cierr: 25362306a36Sopenharmony_ci xfrm_audit_state_replay(x, skb, net_seq); 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci unsigned int bitnr, nr, i; 26062306a36Sopenharmony_ci u32 diff; 26162306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 26262306a36Sopenharmony_ci u32 seq = ntohl(net_seq); 26362306a36Sopenharmony_ci u32 pos; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!replay_esn->replay_window) 26662306a36Sopenharmony_ci return; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci pos = (replay_esn->seq - 1) % replay_esn->replay_window; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (seq > replay_esn->seq) { 27162306a36Sopenharmony_ci diff = seq - replay_esn->seq; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (diff < replay_esn->replay_window) { 27462306a36Sopenharmony_ci for (i = 1; i < diff; i++) { 27562306a36Sopenharmony_ci bitnr = (pos + i) % replay_esn->replay_window; 27662306a36Sopenharmony_ci nr = bitnr >> 5; 27762306a36Sopenharmony_ci bitnr = bitnr & 0x1F; 27862306a36Sopenharmony_ci replay_esn->bmp[nr] &= ~(1U << bitnr); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci nr = (replay_esn->replay_window - 1) >> 5; 28262306a36Sopenharmony_ci for (i = 0; i <= nr; i++) 28362306a36Sopenharmony_ci replay_esn->bmp[i] = 0; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci bitnr = (pos + diff) % replay_esn->replay_window; 28762306a36Sopenharmony_ci replay_esn->seq = seq; 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci diff = replay_esn->seq - seq; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (pos >= diff) 29262306a36Sopenharmony_ci bitnr = (pos - diff) % replay_esn->replay_window; 29362306a36Sopenharmony_ci else 29462306a36Sopenharmony_ci bitnr = replay_esn->replay_window - (diff - pos); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci nr = bitnr >> 5; 29862306a36Sopenharmony_ci bitnr = bitnr & 0x1F; 29962306a36Sopenharmony_ci replay_esn->bmp[nr] |= (1U << bitnr); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (xfrm_aevent_is_on(xs_net(x))) 30262306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void xfrm_replay_notify_bmp(struct xfrm_state *x, int event) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct km_event c; 30862306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 30962306a36Sopenharmony_ci struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* we send notify messages in case 31262306a36Sopenharmony_ci * 1. we updated on of the sequence numbers, and the seqno difference 31362306a36Sopenharmony_ci * is at least x->replay_maxdiff, in this case we also update the 31462306a36Sopenharmony_ci * timeout of our timer function 31562306a36Sopenharmony_ci * 2. if x->replay_maxage has elapsed since last update, 31662306a36Sopenharmony_ci * and there were changes 31762306a36Sopenharmony_ci * 31862306a36Sopenharmony_ci * The state structure must be locked! 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci switch (event) { 32262306a36Sopenharmony_ci case XFRM_REPLAY_UPDATE: 32362306a36Sopenharmony_ci if (!x->replay_maxdiff || 32462306a36Sopenharmony_ci ((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) && 32562306a36Sopenharmony_ci (replay_esn->oseq - preplay_esn->oseq 32662306a36Sopenharmony_ci < x->replay_maxdiff))) { 32762306a36Sopenharmony_ci if (x->xflags & XFRM_TIME_DEFER) 32862306a36Sopenharmony_ci event = XFRM_REPLAY_TIMEOUT; 32962306a36Sopenharmony_ci else 33062306a36Sopenharmony_ci return; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci case XFRM_REPLAY_TIMEOUT: 33662306a36Sopenharmony_ci if (memcmp(x->replay_esn, x->preplay_esn, 33762306a36Sopenharmony_ci xfrm_replay_state_esn_len(replay_esn)) == 0) { 33862306a36Sopenharmony_ci x->xflags |= XFRM_TIME_DEFER; 33962306a36Sopenharmony_ci return; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci memcpy(x->preplay_esn, x->replay_esn, 34662306a36Sopenharmony_ci xfrm_replay_state_esn_len(replay_esn)); 34762306a36Sopenharmony_ci c.event = XFRM_MSG_NEWAE; 34862306a36Sopenharmony_ci c.data.aevent = event; 34962306a36Sopenharmony_ci km_state_notify(x, &c); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (x->replay_maxage && 35262306a36Sopenharmony_ci !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) 35362306a36Sopenharmony_ci x->xflags &= ~XFRM_TIME_DEFER; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic void xfrm_replay_notify_esn(struct xfrm_state *x, int event) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci u32 seq_diff, oseq_diff; 35962306a36Sopenharmony_ci struct km_event c; 36062306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 36162306a36Sopenharmony_ci struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* we send notify messages in case 36462306a36Sopenharmony_ci * 1. we updated on of the sequence numbers, and the seqno difference 36562306a36Sopenharmony_ci * is at least x->replay_maxdiff, in this case we also update the 36662306a36Sopenharmony_ci * timeout of our timer function 36762306a36Sopenharmony_ci * 2. if x->replay_maxage has elapsed since last update, 36862306a36Sopenharmony_ci * and there were changes 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * The state structure must be locked! 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci switch (event) { 37462306a36Sopenharmony_ci case XFRM_REPLAY_UPDATE: 37562306a36Sopenharmony_ci if (x->replay_maxdiff) { 37662306a36Sopenharmony_ci if (replay_esn->seq_hi == preplay_esn->seq_hi) 37762306a36Sopenharmony_ci seq_diff = replay_esn->seq - preplay_esn->seq; 37862306a36Sopenharmony_ci else 37962306a36Sopenharmony_ci seq_diff = ~preplay_esn->seq + replay_esn->seq 38062306a36Sopenharmony_ci + 1; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (replay_esn->oseq_hi == preplay_esn->oseq_hi) 38362306a36Sopenharmony_ci oseq_diff = replay_esn->oseq 38462306a36Sopenharmony_ci - preplay_esn->oseq; 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci oseq_diff = ~preplay_esn->oseq 38762306a36Sopenharmony_ci + replay_esn->oseq + 1; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (seq_diff >= x->replay_maxdiff || 39062306a36Sopenharmony_ci oseq_diff >= x->replay_maxdiff) 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (x->xflags & XFRM_TIME_DEFER) 39562306a36Sopenharmony_ci event = XFRM_REPLAY_TIMEOUT; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci return; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci case XFRM_REPLAY_TIMEOUT: 40262306a36Sopenharmony_ci if (memcmp(x->replay_esn, x->preplay_esn, 40362306a36Sopenharmony_ci xfrm_replay_state_esn_len(replay_esn)) == 0) { 40462306a36Sopenharmony_ci x->xflags |= XFRM_TIME_DEFER; 40562306a36Sopenharmony_ci return; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci memcpy(x->preplay_esn, x->replay_esn, 41262306a36Sopenharmony_ci xfrm_replay_state_esn_len(replay_esn)); 41362306a36Sopenharmony_ci c.event = XFRM_MSG_NEWAE; 41462306a36Sopenharmony_ci c.data.aevent = event; 41562306a36Sopenharmony_ci km_state_notify(x, &c); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (x->replay_maxage && 41862306a36Sopenharmony_ci !mod_timer(&x->rtimer, jiffies + x->replay_maxage)) 41962306a36Sopenharmony_ci x->xflags &= ~XFRM_TIME_DEFER; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int xfrm_replay_overflow_esn(struct xfrm_state *x, struct sk_buff *skb) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci int err = 0; 42562306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 42662306a36Sopenharmony_ci struct net *net = xs_net(x); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 42962306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq; 43062306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = replay_esn->oseq_hi; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (unlikely(replay_esn->oseq == 0)) { 43362306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = ++replay_esn->oseq_hi; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (replay_esn->oseq_hi == 0) { 43662306a36Sopenharmony_ci replay_esn->oseq--; 43762306a36Sopenharmony_ci replay_esn->oseq_hi--; 43862306a36Sopenharmony_ci xfrm_audit_state_replay_overflow(x, skb); 43962306a36Sopenharmony_ci err = -EOVERFLOW; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return err; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci if (xfrm_aevent_is_on(net)) 44562306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int xfrm_replay_check_esn(struct xfrm_state *x, 45262306a36Sopenharmony_ci struct sk_buff *skb, __be32 net_seq) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci unsigned int bitnr, nr; 45562306a36Sopenharmony_ci u32 diff; 45662306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 45762306a36Sopenharmony_ci u32 pos; 45862306a36Sopenharmony_ci u32 seq = ntohl(net_seq); 45962306a36Sopenharmony_ci u32 wsize = replay_esn->replay_window; 46062306a36Sopenharmony_ci u32 top = replay_esn->seq; 46162306a36Sopenharmony_ci u32 bottom = top - wsize + 1; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (!wsize) 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (unlikely(seq == 0 && replay_esn->seq_hi == 0 && 46762306a36Sopenharmony_ci (replay_esn->seq < replay_esn->replay_window - 1))) 46862306a36Sopenharmony_ci goto err; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci diff = top - seq; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (likely(top >= wsize - 1)) { 47362306a36Sopenharmony_ci /* A. same subspace */ 47462306a36Sopenharmony_ci if (likely(seq > top) || seq < bottom) 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci /* B. window spans two subspaces */ 47862306a36Sopenharmony_ci if (likely(seq > top && seq < bottom)) 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci if (seq >= bottom) 48162306a36Sopenharmony_ci diff = ~seq + top + 1; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (diff >= replay_esn->replay_window) { 48562306a36Sopenharmony_ci x->stats.replay_window++; 48662306a36Sopenharmony_ci goto err; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci pos = (replay_esn->seq - 1) % replay_esn->replay_window; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (pos >= diff) 49262306a36Sopenharmony_ci bitnr = (pos - diff) % replay_esn->replay_window; 49362306a36Sopenharmony_ci else 49462306a36Sopenharmony_ci bitnr = replay_esn->replay_window - (diff - pos); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci nr = bitnr >> 5; 49762306a36Sopenharmony_ci bitnr = bitnr & 0x1F; 49862306a36Sopenharmony_ci if (replay_esn->bmp[nr] & (1U << bitnr)) 49962306a36Sopenharmony_ci goto err_replay; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cierr_replay: 50462306a36Sopenharmony_ci x->stats.replay++; 50562306a36Sopenharmony_cierr: 50662306a36Sopenharmony_ci xfrm_audit_state_replay(x, skb, net_seq); 50762306a36Sopenharmony_ci return -EINVAL; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ciint xfrm_replay_check(struct xfrm_state *x, 51162306a36Sopenharmony_ci struct sk_buff *skb, __be32 net_seq) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci switch (x->repl_mode) { 51462306a36Sopenharmony_ci case XFRM_REPLAY_MODE_LEGACY: 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci case XFRM_REPLAY_MODE_BMP: 51762306a36Sopenharmony_ci return xfrm_replay_check_bmp(x, skb, net_seq); 51862306a36Sopenharmony_ci case XFRM_REPLAY_MODE_ESN: 51962306a36Sopenharmony_ci return xfrm_replay_check_esn(x, skb, net_seq); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return xfrm_replay_check_legacy(x, skb, net_seq); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int xfrm_replay_recheck_esn(struct xfrm_state *x, 52662306a36Sopenharmony_ci struct sk_buff *skb, __be32 net_seq) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci if (unlikely(XFRM_SKB_CB(skb)->seq.input.hi != 52962306a36Sopenharmony_ci htonl(xfrm_replay_seqhi(x, net_seq)))) { 53062306a36Sopenharmony_ci x->stats.replay_window++; 53162306a36Sopenharmony_ci return -EINVAL; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return xfrm_replay_check_esn(x, skb, net_seq); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ciint xfrm_replay_recheck(struct xfrm_state *x, 53862306a36Sopenharmony_ci struct sk_buff *skb, __be32 net_seq) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci switch (x->repl_mode) { 54162306a36Sopenharmony_ci case XFRM_REPLAY_MODE_LEGACY: 54262306a36Sopenharmony_ci break; 54362306a36Sopenharmony_ci case XFRM_REPLAY_MODE_BMP: 54462306a36Sopenharmony_ci /* no special recheck treatment */ 54562306a36Sopenharmony_ci return xfrm_replay_check_bmp(x, skb, net_seq); 54662306a36Sopenharmony_ci case XFRM_REPLAY_MODE_ESN: 54762306a36Sopenharmony_ci return xfrm_replay_recheck_esn(x, skb, net_seq); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return xfrm_replay_check_legacy(x, skb, net_seq); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci unsigned int bitnr, nr, i; 55662306a36Sopenharmony_ci int wrap; 55762306a36Sopenharmony_ci u32 diff, pos, seq, seq_hi; 55862306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (!replay_esn->replay_window) 56162306a36Sopenharmony_ci return; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci seq = ntohl(net_seq); 56462306a36Sopenharmony_ci pos = (replay_esn->seq - 1) % replay_esn->replay_window; 56562306a36Sopenharmony_ci seq_hi = xfrm_replay_seqhi(x, net_seq); 56662306a36Sopenharmony_ci wrap = seq_hi - replay_esn->seq_hi; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if ((!wrap && seq > replay_esn->seq) || wrap > 0) { 56962306a36Sopenharmony_ci if (likely(!wrap)) 57062306a36Sopenharmony_ci diff = seq - replay_esn->seq; 57162306a36Sopenharmony_ci else 57262306a36Sopenharmony_ci diff = ~replay_esn->seq + seq + 1; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (diff < replay_esn->replay_window) { 57562306a36Sopenharmony_ci for (i = 1; i < diff; i++) { 57662306a36Sopenharmony_ci bitnr = (pos + i) % replay_esn->replay_window; 57762306a36Sopenharmony_ci nr = bitnr >> 5; 57862306a36Sopenharmony_ci bitnr = bitnr & 0x1F; 57962306a36Sopenharmony_ci replay_esn->bmp[nr] &= ~(1U << bitnr); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci } else { 58262306a36Sopenharmony_ci nr = (replay_esn->replay_window - 1) >> 5; 58362306a36Sopenharmony_ci for (i = 0; i <= nr; i++) 58462306a36Sopenharmony_ci replay_esn->bmp[i] = 0; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci bitnr = (pos + diff) % replay_esn->replay_window; 58862306a36Sopenharmony_ci replay_esn->seq = seq; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (unlikely(wrap > 0)) 59162306a36Sopenharmony_ci replay_esn->seq_hi++; 59262306a36Sopenharmony_ci } else { 59362306a36Sopenharmony_ci diff = replay_esn->seq - seq; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (pos >= diff) 59662306a36Sopenharmony_ci bitnr = (pos - diff) % replay_esn->replay_window; 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci bitnr = replay_esn->replay_window - (diff - pos); 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci xfrm_dev_state_advance_esn(x); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci nr = bitnr >> 5; 60462306a36Sopenharmony_ci bitnr = bitnr & 0x1F; 60562306a36Sopenharmony_ci replay_esn->bmp[nr] |= (1U << bitnr); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (xfrm_aevent_is_on(xs_net(x))) 60862306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci#ifdef CONFIG_XFRM_OFFLOAD 61262306a36Sopenharmony_cistatic int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *skb) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci int err = 0; 61562306a36Sopenharmony_ci struct net *net = xs_net(x); 61662306a36Sopenharmony_ci struct xfrm_offload *xo = xfrm_offload(skb); 61762306a36Sopenharmony_ci __u32 oseq = x->replay.oseq; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!xo) 62062306a36Sopenharmony_ci return __xfrm_replay_overflow(x, skb); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 62362306a36Sopenharmony_ci if (!skb_is_gso(skb)) { 62462306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = ++oseq; 62562306a36Sopenharmony_ci xo->seq.low = oseq; 62662306a36Sopenharmony_ci } else { 62762306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; 62862306a36Sopenharmony_ci xo->seq.low = oseq + 1; 62962306a36Sopenharmony_ci oseq += skb_shinfo(skb)->gso_segs; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = 0; 63362306a36Sopenharmony_ci xo->seq.hi = 0; 63462306a36Sopenharmony_ci if (unlikely(oseq < x->replay.oseq) && 63562306a36Sopenharmony_ci !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { 63662306a36Sopenharmony_ci xfrm_audit_state_replay_overflow(x, skb); 63762306a36Sopenharmony_ci err = -EOVERFLOW; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return err; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci x->replay.oseq = oseq; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (xfrm_aevent_is_on(net)) 64562306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return err; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic int xfrm_replay_overflow_offload_bmp(struct xfrm_state *x, struct sk_buff *skb) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci int err = 0; 65462306a36Sopenharmony_ci struct xfrm_offload *xo = xfrm_offload(skb); 65562306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 65662306a36Sopenharmony_ci struct net *net = xs_net(x); 65762306a36Sopenharmony_ci __u32 oseq = replay_esn->oseq; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (!xo) 66062306a36Sopenharmony_ci return xfrm_replay_overflow_bmp(x, skb); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 66362306a36Sopenharmony_ci if (!skb_is_gso(skb)) { 66462306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = ++oseq; 66562306a36Sopenharmony_ci xo->seq.low = oseq; 66662306a36Sopenharmony_ci } else { 66762306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; 66862306a36Sopenharmony_ci xo->seq.low = oseq + 1; 66962306a36Sopenharmony_ci oseq += skb_shinfo(skb)->gso_segs; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = 0; 67362306a36Sopenharmony_ci xo->seq.hi = 0; 67462306a36Sopenharmony_ci if (unlikely(oseq < replay_esn->oseq) && 67562306a36Sopenharmony_ci !(x->props.extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP)) { 67662306a36Sopenharmony_ci xfrm_audit_state_replay_overflow(x, skb); 67762306a36Sopenharmony_ci err = -EOVERFLOW; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return err; 68062306a36Sopenharmony_ci } else { 68162306a36Sopenharmony_ci replay_esn->oseq = oseq; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (xfrm_aevent_is_on(net)) 68562306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return err; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff *skb) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci int err = 0; 69462306a36Sopenharmony_ci struct xfrm_offload *xo = xfrm_offload(skb); 69562306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 69662306a36Sopenharmony_ci struct net *net = xs_net(x); 69762306a36Sopenharmony_ci __u32 oseq = replay_esn->oseq; 69862306a36Sopenharmony_ci __u32 oseq_hi = replay_esn->oseq_hi; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (!xo) 70162306a36Sopenharmony_ci return xfrm_replay_overflow_esn(x, skb); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 70462306a36Sopenharmony_ci if (!skb_is_gso(skb)) { 70562306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = ++oseq; 70662306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi; 70762306a36Sopenharmony_ci xo->seq.low = oseq; 70862306a36Sopenharmony_ci xo->seq.hi = oseq_hi; 70962306a36Sopenharmony_ci } else { 71062306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; 71162306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi; 71262306a36Sopenharmony_ci xo->seq.low = oseq + 1; 71362306a36Sopenharmony_ci xo->seq.hi = oseq_hi; 71462306a36Sopenharmony_ci oseq += skb_shinfo(skb)->gso_segs; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (unlikely(xo->seq.low < replay_esn->oseq)) { 71862306a36Sopenharmony_ci XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi; 71962306a36Sopenharmony_ci xo->seq.hi = oseq_hi; 72062306a36Sopenharmony_ci replay_esn->oseq_hi = oseq_hi; 72162306a36Sopenharmony_ci if (replay_esn->oseq_hi == 0) { 72262306a36Sopenharmony_ci replay_esn->oseq--; 72362306a36Sopenharmony_ci replay_esn->oseq_hi--; 72462306a36Sopenharmony_ci xfrm_audit_state_replay_overflow(x, skb); 72562306a36Sopenharmony_ci err = -EOVERFLOW; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return err; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci replay_esn->oseq = oseq; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (xfrm_aevent_is_on(net)) 73462306a36Sopenharmony_ci xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return err; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ciint xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci switch (x->repl_mode) { 74362306a36Sopenharmony_ci case XFRM_REPLAY_MODE_LEGACY: 74462306a36Sopenharmony_ci break; 74562306a36Sopenharmony_ci case XFRM_REPLAY_MODE_BMP: 74662306a36Sopenharmony_ci return xfrm_replay_overflow_offload_bmp(x, skb); 74762306a36Sopenharmony_ci case XFRM_REPLAY_MODE_ESN: 74862306a36Sopenharmony_ci return xfrm_replay_overflow_offload_esn(x, skb); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci return xfrm_replay_overflow_offload(x, skb); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci#else 75462306a36Sopenharmony_ciint xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci switch (x->repl_mode) { 75762306a36Sopenharmony_ci case XFRM_REPLAY_MODE_LEGACY: 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci case XFRM_REPLAY_MODE_BMP: 76062306a36Sopenharmony_ci return xfrm_replay_overflow_bmp(x, skb); 76162306a36Sopenharmony_ci case XFRM_REPLAY_MODE_ESN: 76262306a36Sopenharmony_ci return xfrm_replay_overflow_esn(x, skb); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return __xfrm_replay_overflow(x, skb); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci#endif 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ciint xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (replay_esn) { 77462306a36Sopenharmony_ci if (replay_esn->replay_window > 77562306a36Sopenharmony_ci replay_esn->bmp_len * sizeof(__u32) * 8) { 77662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ESN replay window is too large for the chosen bitmap size"); 77762306a36Sopenharmony_ci return -EINVAL; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (x->props.flags & XFRM_STATE_ESN) { 78162306a36Sopenharmony_ci if (replay_esn->replay_window == 0) { 78262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ESN replay window must be > 0"); 78362306a36Sopenharmony_ci return -EINVAL; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci x->repl_mode = XFRM_REPLAY_MODE_ESN; 78662306a36Sopenharmony_ci } else { 78762306a36Sopenharmony_ci x->repl_mode = XFRM_REPLAY_MODE_BMP; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci } else { 79062306a36Sopenharmony_ci x->repl_mode = XFRM_REPLAY_MODE_LEGACY; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm_init_replay); 796