diff options
author | Steffen Klassert <steffen.klassert@secunet.com> | 2011-03-07 19:09:51 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-13 23:22:31 -0400 |
commit | 2cd084678fc1eb75aec4f7ae3d339d232c00ec61 (patch) | |
tree | ac6413e56d1189f57bb0f84920dfa3257a11d3d2 /net/xfrm | |
parent | 97e15c3a8504ea39a209778d7dcdbdf440404a91 (diff) |
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>
Diffstat (limited to 'net/xfrm')
-rw-r--r-- | net/xfrm/xfrm_input.c | 4 | ||||
-rw-r--r-- | net/xfrm/xfrm_replay.c | 190 |
2 files changed, 193 insertions, 1 deletions
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 55d5f5c3d119..872065ca7f8c 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c | |||
@@ -107,6 +107,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) | |||
107 | struct net *net = dev_net(skb->dev); | 107 | struct net *net = dev_net(skb->dev); |
108 | int err; | 108 | int err; |
109 | __be32 seq; | 109 | __be32 seq; |
110 | __be32 seq_hi; | ||
110 | struct xfrm_state *x; | 111 | struct xfrm_state *x; |
111 | xfrm_address_t *daddr; | 112 | xfrm_address_t *daddr; |
112 | struct xfrm_mode *inner_mode; | 113 | struct xfrm_mode *inner_mode; |
@@ -184,7 +185,10 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) | |||
184 | 185 | ||
185 | spin_unlock(&x->lock); | 186 | spin_unlock(&x->lock); |
186 | 187 | ||
188 | seq_hi = htonl(xfrm_replay_seqhi(x, seq)); | ||
189 | |||
187 | XFRM_SKB_CB(skb)->seq.input.low = seq; | 190 | XFRM_SKB_CB(skb)->seq.input.low = seq; |
191 | XFRM_SKB_CB(skb)->seq.input.hi = seq_hi; | ||
188 | 192 | ||
189 | nexthdr = x->type->input(x, skb); | 193 | nexthdr = x->type->input(x, skb); |
190 | 194 | ||
diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 50589ea9d6a8..2f5be5b15740 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c | |||
@@ -20,6 +20,31 @@ | |||
20 | 20 | ||
21 | #include <net/xfrm.h> | 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 | static void xfrm_replay_notify(struct xfrm_state *x, int event) | 48 | static void xfrm_replay_notify(struct xfrm_state *x, int event) |
24 | { | 49 | { |
25 | struct km_event c; | 50 | struct km_event c; |
@@ -313,6 +338,160 @@ static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event) | |||
313 | x->xflags &= ~XFRM_TIME_DEFER; | 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 | static struct xfrm_replay xfrm_replay_legacy = { | 495 | static struct xfrm_replay xfrm_replay_legacy = { |
317 | .advance = xfrm_replay_advance, | 496 | .advance = xfrm_replay_advance, |
318 | .check = xfrm_replay_check, | 497 | .check = xfrm_replay_check, |
@@ -327,6 +506,13 @@ static struct xfrm_replay xfrm_replay_bmp = { | |||
327 | .overflow = xfrm_replay_overflow_bmp, | 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 | int xfrm_init_replay(struct xfrm_state *x) | 516 | int xfrm_init_replay(struct xfrm_state *x) |
331 | { | 517 | { |
332 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; | 518 | struct xfrm_replay_state_esn *replay_esn = x->replay_esn; |
@@ -336,11 +522,13 @@ int xfrm_init_replay(struct xfrm_state *x) | |||
336 | replay_esn->bmp_len * sizeof(__u32)) | 522 | replay_esn->bmp_len * sizeof(__u32)) |
337 | return -EINVAL; | 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 | x->repl = &xfrm_replay_bmp; | 528 | x->repl = &xfrm_replay_bmp; |
340 | } else | 529 | } else |
341 | x->repl = &xfrm_replay_legacy; | 530 | x->repl = &xfrm_replay_legacy; |
342 | 531 | ||
343 | |||
344 | return 0; | 532 | return 0; |
345 | } | 533 | } |
346 | EXPORT_SYMBOL(xfrm_init_replay); | 534 | EXPORT_SYMBOL(xfrm_init_replay); |