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 | |
| 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>
| -rw-r--r-- | include/net/xfrm.h | 1 | ||||
| -rw-r--r-- | net/xfrm/xfrm_input.c | 4 | ||||
| -rw-r--r-- | net/xfrm/xfrm_replay.c | 190 |
3 files changed, 194 insertions, 1 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 41def092b824..42a8c32a10e2 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
| @@ -1427,6 +1427,7 @@ extern int xfrm_state_delete(struct xfrm_state *x); | |||
| 1427 | extern int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info); | 1427 | extern int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info); |
| 1428 | extern void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); | 1428 | extern void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); |
| 1429 | extern void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); | 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 | extern int xfrm_init_replay(struct xfrm_state *x); | 1431 | extern int xfrm_init_replay(struct xfrm_state *x); |
| 1431 | extern int xfrm_state_mtu(struct xfrm_state *x, int mtu); | 1432 | extern int xfrm_state_mtu(struct xfrm_state *x, int mtu); |
| 1432 | extern int xfrm_init_state(struct xfrm_state *x); | 1433 | extern int xfrm_init_state(struct xfrm_state *x); |
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); |
