Commit 2cd084678fc1eb75aec4f7ae3d339d232c00ec61
Committed by
David S. Miller
1 parent
97e15c3a85
Exists in
master
and in
39 other branches
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
include/net/xfrm.h
... | ... | @@ -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 | } |