Skip to content

Commit 2cd0846

Browse files
klassertdavem330
authored andcommitted
xfrm: Add support for IPsec extended sequence numbers
This patch adds support for IPsec extended sequence numbers (esn) as defined in RFC 4303. The bits to manage the anti-replay window are based on a patch from Alex Badea. Signed-off-by: Steffen Klassert <[email protected]> Acked-by: Herbert Xu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 97e15c3 commit 2cd0846

File tree

3 files changed

+194
-1
lines changed

3 files changed

+194
-1
lines changed

include/net/xfrm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,7 @@ extern int xfrm_state_delete(struct xfrm_state *x);
14271427
extern int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info);
14281428
extern void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
14291429
extern void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
1430+
extern u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
14301431
extern int xfrm_init_replay(struct xfrm_state *x);
14311432
extern int xfrm_state_mtu(struct xfrm_state *x, int mtu);
14321433
extern int xfrm_init_state(struct xfrm_state *x);

net/xfrm/xfrm_input.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
107107
struct net *net = dev_net(skb->dev);
108108
int err;
109109
__be32 seq;
110+
__be32 seq_hi;
110111
struct xfrm_state *x;
111112
xfrm_address_t *daddr;
112113
struct xfrm_mode *inner_mode;
@@ -184,7 +185,10 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
184185

185186
spin_unlock(&x->lock);
186187

188+
seq_hi = htonl(xfrm_replay_seqhi(x, seq));
189+
187190
XFRM_SKB_CB(skb)->seq.input.low = seq;
191+
XFRM_SKB_CB(skb)->seq.input.hi = seq_hi;
188192

189193
nexthdr = x->type->input(x, skb);
190194

net/xfrm/xfrm_replay.c

Lines changed: 189 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,31 @@
2020

2121
#include <net/xfrm.h>
2222

23+
u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq)
24+
{
25+
u32 seq, seq_hi, bottom;
26+
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
27+
28+
if (!(x->props.flags & XFRM_STATE_ESN))
29+
return 0;
30+
31+
seq = ntohl(net_seq);
32+
seq_hi = replay_esn->seq_hi;
33+
bottom = replay_esn->seq - replay_esn->replay_window + 1;
34+
35+
if (likely(replay_esn->seq >= replay_esn->replay_window - 1)) {
36+
/* A. same subspace */
37+
if (unlikely(seq < bottom))
38+
seq_hi++;
39+
} else {
40+
/* B. window spans two subspaces */
41+
if (unlikely(seq >= bottom))
42+
seq_hi--;
43+
}
44+
45+
return seq_hi;
46+
}
47+
2348
static void xfrm_replay_notify(struct xfrm_state *x, int event)
2449
{
2550
struct km_event c;
@@ -313,6 +338,160 @@ static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)
313338
x->xflags &= ~XFRM_TIME_DEFER;
314339
}
315340

341+
static int xfrm_replay_overflow_esn(struct xfrm_state *x, struct sk_buff *skb)
342+
{
343+
int err = 0;
344+
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
345+
struct net *net = xs_net(x);
346+
347+
if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
348+
XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq;
349+
XFRM_SKB_CB(skb)->seq.output.hi = replay_esn->oseq_hi;
350+
351+
if (unlikely(replay_esn->oseq == 0)) {
352+
XFRM_SKB_CB(skb)->seq.output.hi = ++replay_esn->oseq_hi;
353+
354+
if (replay_esn->oseq_hi == 0) {
355+
replay_esn->oseq--;
356+
replay_esn->oseq_hi--;
357+
xfrm_audit_state_replay_overflow(x, skb);
358+
err = -EOVERFLOW;
359+
360+
return err;
361+
}
362+
}
363+
if (xfrm_aevent_is_on(net))
364+
x->repl->notify(x, XFRM_REPLAY_UPDATE);
365+
}
366+
367+
return err;
368+
}
369+
370+
static int xfrm_replay_check_esn(struct xfrm_state *x,
371+
struct sk_buff *skb, __be32 net_seq)
372+
{
373+
unsigned int bitnr, nr;
374+
u32 diff;
375+
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
376+
u32 seq = ntohl(net_seq);
377+
u32 pos = (replay_esn->seq - 1) % replay_esn->replay_window;
378+
u32 wsize = replay_esn->replay_window;
379+
u32 top = replay_esn->seq;
380+
u32 bottom = top - wsize + 1;
381+
382+
if (unlikely(seq == 0 && replay_esn->seq_hi == 0 &&
383+
(replay_esn->seq < replay_esn->replay_window - 1)))
384+
goto err;
385+
386+
diff = top - seq;
387+
388+
if (likely(top >= wsize - 1)) {
389+
/* A. same subspace */
390+
if (likely(seq > top) || seq < bottom)
391+
return 0;
392+
} else {
393+
/* B. window spans two subspaces */
394+
if (likely(seq > top && seq < bottom))
395+
return 0;
396+
if (seq >= bottom)
397+
diff = ~seq + top + 1;
398+
}
399+
400+
if (diff >= replay_esn->replay_window) {
401+
x->stats.replay_window++;
402+
goto err;
403+
}
404+
405+
if (pos >= diff) {
406+
bitnr = (pos - diff) % replay_esn->replay_window;
407+
nr = bitnr >> 5;
408+
bitnr = bitnr & 0x1F;
409+
if (replay_esn->bmp[nr] & (1U << bitnr))
410+
goto err_replay;
411+
} else {
412+
bitnr = replay_esn->replay_window - (diff - pos);
413+
nr = bitnr >> 5;
414+
bitnr = bitnr & 0x1F;
415+
if (replay_esn->bmp[nr] & (1U << bitnr))
416+
goto err_replay;
417+
}
418+
return 0;
419+
420+
err_replay:
421+
x->stats.replay++;
422+
err:
423+
xfrm_audit_state_replay(x, skb, net_seq);
424+
return -EINVAL;
425+
}
426+
427+
static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
428+
{
429+
unsigned int bitnr, nr, i;
430+
int wrap;
431+
u32 diff, pos, seq, seq_hi;
432+
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
433+
434+
if (!replay_esn->replay_window)
435+
return;
436+
437+
seq = ntohl(net_seq);
438+
pos = (replay_esn->seq - 1) % replay_esn->replay_window;
439+
seq_hi = xfrm_replay_seqhi(x, net_seq);
440+
wrap = seq_hi - replay_esn->seq_hi;
441+
442+
if ((!wrap && seq > replay_esn->seq) || wrap > 0) {
443+
if (likely(!wrap))
444+
diff = seq - replay_esn->seq;
445+
else
446+
diff = ~replay_esn->seq + seq + 1;
447+
448+
if (diff < replay_esn->replay_window) {
449+
for (i = 1; i < diff; i++) {
450+
bitnr = (pos + i) % replay_esn->replay_window;
451+
nr = bitnr >> 5;
452+
bitnr = bitnr & 0x1F;
453+
replay_esn->bmp[nr] &= ~(1U << bitnr);
454+
}
455+
456+
bitnr = (pos + diff) % replay_esn->replay_window;
457+
nr = bitnr >> 5;
458+
bitnr = bitnr & 0x1F;
459+
replay_esn->bmp[nr] |= (1U << bitnr);
460+
} else {
461+
nr = replay_esn->replay_window >> 5;
462+
for (i = 0; i <= nr; i++)
463+
replay_esn->bmp[i] = 0;
464+
465+
bitnr = (pos + diff) % replay_esn->replay_window;
466+
nr = bitnr >> 5;
467+
bitnr = bitnr & 0x1F;
468+
replay_esn->bmp[nr] |= (1U << bitnr);
469+
}
470+
471+
replay_esn->seq = seq;
472+
473+
if (unlikely(wrap > 0))
474+
replay_esn->seq_hi++;
475+
} else {
476+
diff = replay_esn->seq - seq;
477+
478+
if (pos >= diff) {
479+
bitnr = (pos - diff) % replay_esn->replay_window;
480+
nr = bitnr >> 5;
481+
bitnr = bitnr & 0x1F;
482+
replay_esn->bmp[nr] |= (1U << bitnr);
483+
} else {
484+
bitnr = replay_esn->replay_window - (diff - pos);
485+
nr = bitnr >> 5;
486+
bitnr = bitnr & 0x1F;
487+
replay_esn->bmp[nr] |= (1U << bitnr);
488+
}
489+
}
490+
491+
if (xfrm_aevent_is_on(xs_net(x)))
492+
xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
493+
}
494+
316495
static struct xfrm_replay xfrm_replay_legacy = {
317496
.advance = xfrm_replay_advance,
318497
.check = xfrm_replay_check,
@@ -327,6 +506,13 @@ static struct xfrm_replay xfrm_replay_bmp = {
327506
.overflow = xfrm_replay_overflow_bmp,
328507
};
329508

509+
static struct xfrm_replay xfrm_replay_esn = {
510+
.advance = xfrm_replay_advance_esn,
511+
.check = xfrm_replay_check_esn,
512+
.notify = xfrm_replay_notify_bmp,
513+
.overflow = xfrm_replay_overflow_esn,
514+
};
515+
330516
int xfrm_init_replay(struct xfrm_state *x)
331517
{
332518
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
@@ -336,11 +522,13 @@ int xfrm_init_replay(struct xfrm_state *x)
336522
replay_esn->bmp_len * sizeof(__u32))
337523
return -EINVAL;
338524

525+
if ((x->props.flags & XFRM_STATE_ESN) && x->replay_esn)
526+
x->repl = &xfrm_replay_esn;
527+
else
339528
x->repl = &xfrm_replay_bmp;
340529
} else
341530
x->repl = &xfrm_replay_legacy;
342531

343-
344532
return 0;
345533
}
346534
EXPORT_SYMBOL(xfrm_init_replay);

0 commit comments

Comments
 (0)