Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfrm: Add xfrm_replay_overflow functions for offloading

This patch adds functions that handles IPsec sequence
numbers for GSO segments and TSO offloading. We need
to calculate and update the sequence numbers based
on the segments that GSO/TSO will generate. We need
this to keep software and hardware sequence number
counter in sync.

Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

+157 -2
+157 -2
net/xfrm/xfrm_replay.c
··· 559 559 x->repl->notify(x, XFRM_REPLAY_UPDATE); 560 560 } 561 561 562 + #ifdef CONFIG_XFRM_OFFLOAD 563 + static int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *skb) 564 + { 565 + int err = 0; 566 + struct net *net = xs_net(x); 567 + struct xfrm_offload *xo = xfrm_offload(skb); 568 + __u32 oseq = x->replay.oseq; 569 + 570 + if (!xo) 571 + return xfrm_replay_overflow(x, skb); 572 + 573 + if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 574 + if (!skb_is_gso(skb)) { 575 + XFRM_SKB_CB(skb)->seq.output.low = ++oseq; 576 + xo->seq.low = oseq; 577 + } else { 578 + XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; 579 + xo->seq.low = oseq + 1; 580 + oseq += skb_shinfo(skb)->gso_segs; 581 + } 582 + 583 + XFRM_SKB_CB(skb)->seq.output.hi = 0; 584 + xo->seq.hi = 0; 585 + if (unlikely(oseq < x->replay.oseq)) { 586 + xfrm_audit_state_replay_overflow(x, skb); 587 + err = -EOVERFLOW; 588 + 589 + return err; 590 + } 591 + 592 + x->replay.oseq = oseq; 593 + 594 + if (xfrm_aevent_is_on(net)) 595 + x->repl->notify(x, XFRM_REPLAY_UPDATE); 596 + } 597 + 598 + return err; 599 + } 600 + 601 + static int xfrm_replay_overflow_offload_bmp(struct xfrm_state *x, struct sk_buff *skb) 602 + { 603 + int err = 0; 604 + struct xfrm_offload *xo = xfrm_offload(skb); 605 + struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 606 + struct net *net = xs_net(x); 607 + __u32 oseq = replay_esn->oseq; 608 + 609 + if (!xo) 610 + return xfrm_replay_overflow_bmp(x, skb); 611 + 612 + if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 613 + if (!skb_is_gso(skb)) { 614 + XFRM_SKB_CB(skb)->seq.output.low = ++oseq; 615 + xo->seq.low = oseq; 616 + } else { 617 + XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; 618 + xo->seq.low = oseq + 1; 619 + oseq += skb_shinfo(skb)->gso_segs; 620 + } 621 + 622 + XFRM_SKB_CB(skb)->seq.output.hi = 0; 623 + xo->seq.hi = 0; 624 + if (unlikely(oseq < replay_esn->oseq)) { 625 + xfrm_audit_state_replay_overflow(x, skb); 626 + err = -EOVERFLOW; 627 + 628 + return err; 629 + } else { 630 + replay_esn->oseq = oseq; 631 + } 632 + 633 + if (xfrm_aevent_is_on(net)) 634 + x->repl->notify(x, XFRM_REPLAY_UPDATE); 635 + } 636 + 637 + return err; 638 + } 639 + 640 + static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff *skb) 641 + { 642 + int err = 0; 643 + struct xfrm_offload *xo = xfrm_offload(skb); 644 + struct xfrm_replay_state_esn *replay_esn = x->replay_esn; 645 + struct net *net = xs_net(x); 646 + __u32 oseq = replay_esn->oseq; 647 + __u32 oseq_hi = replay_esn->oseq_hi; 648 + 649 + if (!xo) 650 + return xfrm_replay_overflow_esn(x, skb); 651 + 652 + if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { 653 + if (!skb_is_gso(skb)) { 654 + XFRM_SKB_CB(skb)->seq.output.low = ++oseq; 655 + XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi; 656 + xo->seq.low = oseq; 657 + xo->seq.hi = oseq_hi; 658 + } else { 659 + XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; 660 + XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi; 661 + xo->seq.low = oseq = oseq + 1; 662 + xo->seq.hi = oseq_hi; 663 + oseq += skb_shinfo(skb)->gso_segs; 664 + } 665 + 666 + if (unlikely(oseq < replay_esn->oseq)) { 667 + XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi; 668 + xo->seq.hi = oseq_hi; 669 + 670 + if (replay_esn->oseq_hi == 0) { 671 + replay_esn->oseq--; 672 + replay_esn->oseq_hi--; 673 + xfrm_audit_state_replay_overflow(x, skb); 674 + err = -EOVERFLOW; 675 + 676 + return err; 677 + } 678 + } 679 + 680 + replay_esn->oseq = oseq; 681 + replay_esn->oseq_hi = oseq_hi; 682 + 683 + if (xfrm_aevent_is_on(net)) 684 + x->repl->notify(x, XFRM_REPLAY_UPDATE); 685 + } 686 + 687 + return err; 688 + } 689 + 690 + static const struct xfrm_replay xfrm_replay_legacy = { 691 + .advance = xfrm_replay_advance, 692 + .check = xfrm_replay_check, 693 + .recheck = xfrm_replay_check, 694 + .notify = xfrm_replay_notify, 695 + .overflow = xfrm_replay_overflow_offload, 696 + }; 697 + 698 + static const struct xfrm_replay xfrm_replay_bmp = { 699 + .advance = xfrm_replay_advance_bmp, 700 + .check = xfrm_replay_check_bmp, 701 + .recheck = xfrm_replay_check_bmp, 702 + .notify = xfrm_replay_notify_bmp, 703 + .overflow = xfrm_replay_overflow_offload_bmp, 704 + }; 705 + 706 + static const struct xfrm_replay xfrm_replay_esn = { 707 + .advance = xfrm_replay_advance_esn, 708 + .check = xfrm_replay_check_esn, 709 + .recheck = xfrm_replay_recheck_esn, 710 + .notify = xfrm_replay_notify_esn, 711 + .overflow = xfrm_replay_overflow_offload_esn, 712 + }; 713 + #else 562 714 static const struct xfrm_replay xfrm_replay_legacy = { 563 715 .advance = xfrm_replay_advance, 564 716 .check = xfrm_replay_check, ··· 734 582 .notify = xfrm_replay_notify_esn, 735 583 .overflow = xfrm_replay_overflow_esn, 736 584 }; 585 + #endif 737 586 738 587 int xfrm_init_replay(struct xfrm_state *x) 739 588 { ··· 749 596 if (replay_esn->replay_window == 0) 750 597 return -EINVAL; 751 598 x->repl = &xfrm_replay_esn; 752 - } else 599 + } else { 753 600 x->repl = &xfrm_replay_bmp; 754 - } else 601 + } 602 + } else { 755 603 x->repl = &xfrm_replay_legacy; 604 + } 756 605 757 606 return 0; 758 607 }