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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-mlx5-hws-improve-ip-version-handling'

Mark Bloch says:

====================
net/mlx5: HWS, Improve IP version handling

This small series hardens our checks against a single matcher containing
rules that match on IPv4 and IPv6. This scenario is not supported by
hardware steering and the implementation now signals this instead of
failing silently.

Patches:
* Patch 1 forbids a single definer to match on mixed IP versions for
source and destination address.
* Patch 2 reproduces a couple of firmware checks: it forbids creating
a definer that matches on IP address without matching on IP version,
and also disallows matching on IPv6 addresses and the IPv4 IHL fields
in the same definer.
* Patch 3 forbids mixing rules that match on IPv4 and IPv6 addresses in
the same matcher. The underlying definer mechanism does not support
that.
====================

Link: https://patch.msgid.link/20250422092540.182091-1-mbloch@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+216 -22
+56 -22
drivers/net/ethernet/mellanox/mlx5/core/steering/hws/definer.c
··· 509 509 hws_definer_conv_outer(struct mlx5hws_definer_conv_data *cd, 510 510 u32 *match_param) 511 511 { 512 - bool is_s_ipv6, is_d_ipv6, smac_set, dmac_set; 512 + bool is_ipv6, smac_set, dmac_set, ip_addr_set, ip_ver_set; 513 513 struct mlx5hws_definer_fc *fc = cd->fc; 514 514 struct mlx5hws_definer_fc *curr_fc; 515 515 u32 *s_ipv6, *d_ipv6; ··· 518 518 HWS_IS_FLD_SET_SZ(match_param, outer_headers.reserved_at_c2, 0xe) || 519 519 HWS_IS_FLD_SET_SZ(match_param, outer_headers.reserved_at_c4, 0x4)) { 520 520 mlx5hws_err(cd->ctx, "Unsupported outer parameters set\n"); 521 + return -EINVAL; 522 + } 523 + 524 + ip_addr_set = HWS_IS_FLD_SET_SZ(match_param, 525 + outer_headers.src_ipv4_src_ipv6, 526 + 0x80) || 527 + HWS_IS_FLD_SET_SZ(match_param, 528 + outer_headers.dst_ipv4_dst_ipv6, 0x80); 529 + ip_ver_set = HWS_IS_FLD_SET(match_param, outer_headers.ip_version) || 530 + HWS_IS_FLD_SET(match_param, outer_headers.ethertype); 531 + 532 + if (ip_addr_set && !ip_ver_set) { 533 + mlx5hws_err(cd->ctx, 534 + "Unsupported match on IP address without version or ethertype\n"); 521 535 return -EINVAL; 522 536 } 523 537 ··· 584 570 outer_headers.dst_ipv4_dst_ipv6.ipv6_layout); 585 571 586 572 /* Assume IPv6 is used if ipv6 bits are set */ 587 - is_s_ipv6 = s_ipv6[0] || s_ipv6[1] || s_ipv6[2]; 588 - is_d_ipv6 = d_ipv6[0] || d_ipv6[1] || d_ipv6[2]; 573 + is_ipv6 = s_ipv6[0] || s_ipv6[1] || s_ipv6[2] || 574 + d_ipv6[0] || d_ipv6[1] || d_ipv6[2]; 589 575 590 - if (is_s_ipv6) { 576 + /* IHL is an IPv4-specific field. */ 577 + if (is_ipv6 && HWS_IS_FLD_SET(match_param, outer_headers.ipv4_ihl)) { 578 + mlx5hws_err(cd->ctx, "Unsupported match on IPv6 address and IPv4 IHL\n"); 579 + return -EINVAL; 580 + } 581 + 582 + if (is_ipv6) { 591 583 /* Handle IPv6 source address */ 592 584 HWS_SET_HDR(fc, match_param, IPV6_SRC_127_96_O, 593 585 outer_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_127_96, ··· 607 587 HWS_SET_HDR(fc, match_param, IPV6_SRC_31_0_O, 608 588 outer_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0, 609 589 ipv6_src_outer.ipv6_address_31_0); 610 - } else { 611 - /* Handle IPv4 source address */ 612 - HWS_SET_HDR(fc, match_param, IPV4_SRC_O, 613 - outer_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0, 614 - ipv4_src_dest_outer.source_address); 615 - } 616 - if (is_d_ipv6) { 617 590 /* Handle IPv6 destination address */ 618 591 HWS_SET_HDR(fc, match_param, IPV6_DST_127_96_O, 619 592 outer_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_127_96, ··· 621 608 outer_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_31_0, 622 609 ipv6_dst_outer.ipv6_address_31_0); 623 610 } else { 611 + /* Handle IPv4 source address */ 612 + HWS_SET_HDR(fc, match_param, IPV4_SRC_O, 613 + outer_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0, 614 + ipv4_src_dest_outer.source_address); 624 615 /* Handle IPv4 destination address */ 625 616 HWS_SET_HDR(fc, match_param, IPV4_DST_O, 626 617 outer_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_31_0, ··· 682 665 hws_definer_conv_inner(struct mlx5hws_definer_conv_data *cd, 683 666 u32 *match_param) 684 667 { 685 - bool is_s_ipv6, is_d_ipv6, smac_set, dmac_set; 668 + bool is_ipv6, smac_set, dmac_set, ip_addr_set, ip_ver_set; 686 669 struct mlx5hws_definer_fc *fc = cd->fc; 687 670 struct mlx5hws_definer_fc *curr_fc; 688 671 u32 *s_ipv6, *d_ipv6; ··· 691 674 HWS_IS_FLD_SET_SZ(match_param, inner_headers.reserved_at_c2, 0xe) || 692 675 HWS_IS_FLD_SET_SZ(match_param, inner_headers.reserved_at_c4, 0x4)) { 693 676 mlx5hws_err(cd->ctx, "Unsupported inner parameters set\n"); 677 + return -EINVAL; 678 + } 679 + 680 + ip_addr_set = HWS_IS_FLD_SET_SZ(match_param, 681 + inner_headers.src_ipv4_src_ipv6, 682 + 0x80) || 683 + HWS_IS_FLD_SET_SZ(match_param, 684 + inner_headers.dst_ipv4_dst_ipv6, 0x80); 685 + ip_ver_set = HWS_IS_FLD_SET(match_param, inner_headers.ip_version) || 686 + HWS_IS_FLD_SET(match_param, inner_headers.ethertype); 687 + 688 + if (ip_addr_set && !ip_ver_set) { 689 + mlx5hws_err(cd->ctx, 690 + "Unsupported match on IP address without version or ethertype\n"); 694 691 return -EINVAL; 695 692 } 696 693 ··· 759 728 inner_headers.dst_ipv4_dst_ipv6.ipv6_layout); 760 729 761 730 /* Assume IPv6 is used if ipv6 bits are set */ 762 - is_s_ipv6 = s_ipv6[0] || s_ipv6[1] || s_ipv6[2]; 763 - is_d_ipv6 = d_ipv6[0] || d_ipv6[1] || d_ipv6[2]; 731 + is_ipv6 = s_ipv6[0] || s_ipv6[1] || s_ipv6[2] || 732 + d_ipv6[0] || d_ipv6[1] || d_ipv6[2]; 764 733 765 - if (is_s_ipv6) { 734 + /* IHL is an IPv4-specific field. */ 735 + if (is_ipv6 && HWS_IS_FLD_SET(match_param, inner_headers.ipv4_ihl)) { 736 + mlx5hws_err(cd->ctx, "Unsupported match on IPv6 address and IPv4 IHL\n"); 737 + return -EINVAL; 738 + } 739 + 740 + if (is_ipv6) { 766 741 /* Handle IPv6 source address */ 767 742 HWS_SET_HDR(fc, match_param, IPV6_SRC_127_96_I, 768 743 inner_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_127_96, ··· 782 745 HWS_SET_HDR(fc, match_param, IPV6_SRC_31_0_I, 783 746 inner_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0, 784 747 ipv6_src_inner.ipv6_address_31_0); 785 - } else { 786 - /* Handle IPv4 source address */ 787 - HWS_SET_HDR(fc, match_param, IPV4_SRC_I, 788 - inner_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0, 789 - ipv4_src_dest_inner.source_address); 790 - } 791 - if (is_d_ipv6) { 792 748 /* Handle IPv6 destination address */ 793 749 HWS_SET_HDR(fc, match_param, IPV6_DST_127_96_I, 794 750 inner_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_127_96, ··· 796 766 inner_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_31_0, 797 767 ipv6_dst_inner.ipv6_address_31_0); 798 768 } else { 769 + /* Handle IPv4 source address */ 770 + HWS_SET_HDR(fc, match_param, IPV4_SRC_I, 771 + inner_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0, 772 + ipv4_src_dest_inner.source_address); 799 773 /* Handle IPv4 destination address */ 800 774 HWS_SET_HDR(fc, match_param, IPV4_DST_I, 801 775 inner_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_31_0,
+26
drivers/net/ethernet/mellanox/mlx5/core/steering/hws/matcher.c
··· 385 385 return 0; 386 386 } 387 387 388 + static void hws_matcher_set_ip_version_match(struct mlx5hws_matcher *matcher) 389 + { 390 + int i; 391 + 392 + for (i = 0; i < matcher->mt->fc_sz; i++) { 393 + switch (matcher->mt->fc[i].fname) { 394 + case MLX5HWS_DEFINER_FNAME_ETH_TYPE_O: 395 + matcher->matches_outer_ethertype = 1; 396 + break; 397 + case MLX5HWS_DEFINER_FNAME_ETH_L3_TYPE_O: 398 + matcher->matches_outer_ip_version = 1; 399 + break; 400 + case MLX5HWS_DEFINER_FNAME_ETH_TYPE_I: 401 + matcher->matches_inner_ethertype = 1; 402 + break; 403 + case MLX5HWS_DEFINER_FNAME_ETH_L3_TYPE_I: 404 + matcher->matches_inner_ip_version = 1; 405 + break; 406 + default: 407 + break; 408 + } 409 + } 410 + } 411 + 388 412 static int hws_matcher_bind_mt(struct mlx5hws_matcher *matcher) 389 413 { 390 414 struct mlx5hws_context *ctx = matcher->tbl->ctx; ··· 424 400 return ret; 425 401 } 426 402 } 403 + 404 + hws_matcher_set_ip_version_match(matcher); 427 405 428 406 /* Create an STE pool per matcher*/ 429 407 pool_attr.table_type = matcher->tbl->type;
+12
drivers/net/ethernet/mellanox/mlx5/core/steering/hws/matcher.h
··· 50 50 struct mlx5hws_pool *pool; 51 51 }; 52 52 53 + enum { 54 + MLX5HWS_MATCHER_IPV_UNSET = 0, 55 + MLX5HWS_MATCHER_IPV_4 = 1, 56 + MLX5HWS_MATCHER_IPV_6 = 2, 57 + }; 58 + 53 59 struct mlx5hws_matcher { 54 60 struct mlx5hws_table *tbl; 55 61 struct mlx5hws_matcher_attr attr; ··· 67 61 u8 num_of_action_stes; 68 62 /* enum mlx5hws_matcher_flags */ 69 63 u8 flags; 64 + u8 matches_outer_ethertype:1; 65 + u8 matches_outer_ip_version:1; 66 + u8 matches_inner_ethertype:1; 67 + u8 matches_inner_ip_version:1; 68 + u8 outer_ip_version:2; 69 + u8 inner_ip_version:2; 70 70 u32 end_ft_id; 71 71 struct mlx5hws_matcher *col_matcher; 72 72 struct mlx5hws_matcher *resize_dst;
+122
drivers/net/ethernet/mellanox/mlx5/core/steering/hws/rule.c
··· 655 655 return 0; 656 656 } 657 657 658 + static u8 hws_rule_ethertype_to_matcher_ipv(u32 ethertype) 659 + { 660 + switch (ethertype) { 661 + case ETH_P_IP: 662 + return MLX5HWS_MATCHER_IPV_4; 663 + case ETH_P_IPV6: 664 + return MLX5HWS_MATCHER_IPV_6; 665 + default: 666 + return MLX5HWS_MATCHER_IPV_UNSET; 667 + } 668 + } 669 + 670 + static u8 hws_rule_ip_version_to_matcher_ipv(u32 ip_version) 671 + { 672 + switch (ip_version) { 673 + case 4: 674 + return MLX5HWS_MATCHER_IPV_4; 675 + case 6: 676 + return MLX5HWS_MATCHER_IPV_6; 677 + default: 678 + return MLX5HWS_MATCHER_IPV_UNSET; 679 + } 680 + } 681 + 682 + static int hws_rule_check_outer_ip_version(struct mlx5hws_matcher *matcher, 683 + u32 *match_param) 684 + { 685 + struct mlx5hws_context *ctx = matcher->tbl->ctx; 686 + u8 outer_ipv_ether = MLX5HWS_MATCHER_IPV_UNSET; 687 + u8 outer_ipv_ip = MLX5HWS_MATCHER_IPV_UNSET; 688 + u8 outer_ipv, ver; 689 + 690 + if (matcher->matches_outer_ethertype) { 691 + ver = MLX5_GET(fte_match_param, match_param, 692 + outer_headers.ethertype); 693 + outer_ipv_ether = hws_rule_ethertype_to_matcher_ipv(ver); 694 + } 695 + if (matcher->matches_outer_ip_version) { 696 + ver = MLX5_GET(fte_match_param, match_param, 697 + outer_headers.ip_version); 698 + outer_ipv_ip = hws_rule_ip_version_to_matcher_ipv(ver); 699 + } 700 + 701 + if (outer_ipv_ether != MLX5HWS_MATCHER_IPV_UNSET && 702 + outer_ipv_ip != MLX5HWS_MATCHER_IPV_UNSET && 703 + outer_ipv_ether != outer_ipv_ip) { 704 + mlx5hws_err(ctx, "Rule matches on inconsistent outer ethertype and ip version\n"); 705 + return -EINVAL; 706 + } 707 + 708 + outer_ipv = outer_ipv_ether != MLX5HWS_MATCHER_IPV_UNSET ? 709 + outer_ipv_ether : outer_ipv_ip; 710 + if (outer_ipv != MLX5HWS_MATCHER_IPV_UNSET && 711 + matcher->outer_ip_version != MLX5HWS_MATCHER_IPV_UNSET && 712 + outer_ipv != matcher->outer_ip_version) { 713 + mlx5hws_err(ctx, "Matcher and rule disagree on outer IP version\n"); 714 + return -EINVAL; 715 + } 716 + matcher->outer_ip_version = outer_ipv; 717 + 718 + return 0; 719 + } 720 + 721 + static int hws_rule_check_inner_ip_version(struct mlx5hws_matcher *matcher, 722 + u32 *match_param) 723 + { 724 + struct mlx5hws_context *ctx = matcher->tbl->ctx; 725 + u8 inner_ipv_ether = MLX5HWS_MATCHER_IPV_UNSET; 726 + u8 inner_ipv_ip = MLX5HWS_MATCHER_IPV_UNSET; 727 + u8 inner_ipv, ver; 728 + 729 + if (matcher->matches_inner_ethertype) { 730 + ver = MLX5_GET(fte_match_param, match_param, 731 + inner_headers.ethertype); 732 + inner_ipv_ether = hws_rule_ethertype_to_matcher_ipv(ver); 733 + } 734 + if (matcher->matches_inner_ip_version) { 735 + ver = MLX5_GET(fte_match_param, match_param, 736 + inner_headers.ip_version); 737 + inner_ipv_ip = hws_rule_ip_version_to_matcher_ipv(ver); 738 + } 739 + 740 + if (inner_ipv_ether != MLX5HWS_MATCHER_IPV_UNSET && 741 + inner_ipv_ip != MLX5HWS_MATCHER_IPV_UNSET && 742 + inner_ipv_ether != inner_ipv_ip) { 743 + mlx5hws_err(ctx, "Rule matches on inconsistent inner ethertype and ip version\n"); 744 + return -EINVAL; 745 + } 746 + 747 + inner_ipv = inner_ipv_ether != MLX5HWS_MATCHER_IPV_UNSET ? 748 + inner_ipv_ether : inner_ipv_ip; 749 + if (inner_ipv != MLX5HWS_MATCHER_IPV_UNSET && 750 + matcher->inner_ip_version != MLX5HWS_MATCHER_IPV_UNSET && 751 + inner_ipv != matcher->inner_ip_version) { 752 + mlx5hws_err(ctx, "Matcher and rule disagree on inner IP version\n"); 753 + return -EINVAL; 754 + } 755 + matcher->inner_ip_version = inner_ipv; 756 + 757 + return 0; 758 + } 759 + 760 + static int hws_rule_check_ip_version(struct mlx5hws_matcher *matcher, 761 + u32 *match_param) 762 + { 763 + int ret; 764 + 765 + ret = hws_rule_check_outer_ip_version(matcher, match_param); 766 + if (unlikely(ret)) 767 + return ret; 768 + 769 + ret = hws_rule_check_inner_ip_version(matcher, match_param); 770 + if (unlikely(ret)) 771 + return ret; 772 + 773 + return 0; 774 + } 775 + 658 776 int mlx5hws_rule_create(struct mlx5hws_matcher *matcher, 659 777 u8 mt_idx, 660 778 u32 *match_param, ··· 782 664 struct mlx5hws_rule *rule_handle) 783 665 { 784 666 int ret; 667 + 668 + ret = hws_rule_check_ip_version(matcher, match_param); 669 + if (unlikely(ret)) 670 + return ret; 785 671 786 672 rule_handle->matcher = matcher; 787 673