Commit 2cd084678fc1eb75aec4f7ae3d339d232c00ec61

Authored by Steffen Klassert
Committed by David S. Miller
1 parent 97e15c3a85

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 <steffen.klassert@secunet.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 3 changed files with 194 additions and 1 deletions Side-by-side Diff

... ... @@ -1427,6 +1427,7 @@
1427 1427 extern int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info);
1428 1428 extern void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
1429 1429 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);
1430 1431 extern int xfrm_init_replay(struct xfrm_state *x);
1431 1432 extern int xfrm_state_mtu(struct xfrm_state *x, int mtu);
1432 1433 extern int xfrm_init_state(struct xfrm_state *x);
net/xfrm/xfrm_input.c
... ... @@ -107,6 +107,7 @@
107 107 struct net *net = dev_net(skb->dev);
108 108 int err;
109 109 __be32 seq;
  110 + __be32 seq_hi;
110 111 struct xfrm_state *x;
111 112 xfrm_address_t *daddr;
112 113 struct xfrm_mode *inner_mode;
113 114  
... ... @@ -184,7 +185,10 @@
184 185  
185 186 spin_unlock(&x->lock);
186 187  
  188 + seq_hi = htonl(xfrm_replay_seqhi(x, seq));
  189 +
187 190 XFRM_SKB_CB(skb)->seq.input.low = seq;
  191 + XFRM_SKB_CB(skb)->seq.input.hi = seq_hi;
188 192  
189 193 nexthdr = x->type->input(x, skb);
190 194  
net/xfrm/xfrm_replay.c
... ... @@ -20,6 +20,31 @@
20 20  
21 21 #include <net/xfrm.h>
22 22  
  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 +
23 48 static void xfrm_replay_notify(struct xfrm_state *x, int event)
24 49 {
25 50 struct km_event c;
... ... @@ -313,6 +338,160 @@
313 338 x->xflags &= ~XFRM_TIME_DEFER;
314 339 }
315 340  
  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 +
316 495 static struct xfrm_replay xfrm_replay_legacy = {
317 496 .advance = xfrm_replay_advance,
318 497 .check = xfrm_replay_check,
... ... @@ -327,6 +506,13 @@
327 506 .overflow = xfrm_replay_overflow_bmp,
328 507 };
329 508  
  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 +
330 516 int xfrm_init_replay(struct xfrm_state *x)
331 517 {
332 518 struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
333 519  
... ... @@ -336,10 +522,12 @@
336 522 replay_esn->bmp_len * sizeof(__u32))
337 523 return -EINVAL;
338 524  
  525 + if ((x->props.flags & XFRM_STATE_ESN) && x->replay_esn)
  526 + x->repl = &xfrm_replay_esn;
  527 + else
339 528 x->repl = &xfrm_replay_bmp;
340 529 } else
341 530 x->repl = &xfrm_replay_legacy;
342   -
343 531  
344 532 return 0;
345 533 }