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>
This commit is contained in:
Jakub Kicinski
2025-04-23 18:48:13 -07:00
4 changed files with 216 additions and 22 deletions

View File

@@ -509,7 +509,7 @@ static int
hws_definer_conv_outer(struct mlx5hws_definer_conv_data *cd,
u32 *match_param)
{
bool is_s_ipv6, is_d_ipv6, smac_set, dmac_set;
bool is_ipv6, smac_set, dmac_set, ip_addr_set, ip_ver_set;
struct mlx5hws_definer_fc *fc = cd->fc;
struct mlx5hws_definer_fc *curr_fc;
u32 *s_ipv6, *d_ipv6;
@@ -521,6 +521,20 @@ hws_definer_conv_outer(struct mlx5hws_definer_conv_data *cd,
return -EINVAL;
}
ip_addr_set = HWS_IS_FLD_SET_SZ(match_param,
outer_headers.src_ipv4_src_ipv6,
0x80) ||
HWS_IS_FLD_SET_SZ(match_param,
outer_headers.dst_ipv4_dst_ipv6, 0x80);
ip_ver_set = HWS_IS_FLD_SET(match_param, outer_headers.ip_version) ||
HWS_IS_FLD_SET(match_param, outer_headers.ethertype);
if (ip_addr_set && !ip_ver_set) {
mlx5hws_err(cd->ctx,
"Unsupported match on IP address without version or ethertype\n");
return -EINVAL;
}
/* L2 Check ethertype */
HWS_SET_HDR(fc, match_param, ETH_TYPE_O,
outer_headers.ethertype,
@@ -570,10 +584,16 @@ hws_definer_conv_outer(struct mlx5hws_definer_conv_data *cd,
outer_headers.dst_ipv4_dst_ipv6.ipv6_layout);
/* Assume IPv6 is used if ipv6 bits are set */
is_s_ipv6 = s_ipv6[0] || s_ipv6[1] || s_ipv6[2];
is_d_ipv6 = d_ipv6[0] || d_ipv6[1] || d_ipv6[2];
is_ipv6 = s_ipv6[0] || s_ipv6[1] || s_ipv6[2] ||
d_ipv6[0] || d_ipv6[1] || d_ipv6[2];
if (is_s_ipv6) {
/* IHL is an IPv4-specific field. */
if (is_ipv6 && HWS_IS_FLD_SET(match_param, outer_headers.ipv4_ihl)) {
mlx5hws_err(cd->ctx, "Unsupported match on IPv6 address and IPv4 IHL\n");
return -EINVAL;
}
if (is_ipv6) {
/* Handle IPv6 source address */
HWS_SET_HDR(fc, match_param, IPV6_SRC_127_96_O,
outer_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_127_96,
@@ -587,13 +607,6 @@ hws_definer_conv_outer(struct mlx5hws_definer_conv_data *cd,
HWS_SET_HDR(fc, match_param, IPV6_SRC_31_0_O,
outer_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0,
ipv6_src_outer.ipv6_address_31_0);
} else {
/* Handle IPv4 source address */
HWS_SET_HDR(fc, match_param, IPV4_SRC_O,
outer_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0,
ipv4_src_dest_outer.source_address);
}
if (is_d_ipv6) {
/* Handle IPv6 destination address */
HWS_SET_HDR(fc, match_param, IPV6_DST_127_96_O,
outer_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_127_96,
@@ -608,6 +621,10 @@ hws_definer_conv_outer(struct mlx5hws_definer_conv_data *cd,
outer_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_31_0,
ipv6_dst_outer.ipv6_address_31_0);
} else {
/* Handle IPv4 source address */
HWS_SET_HDR(fc, match_param, IPV4_SRC_O,
outer_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0,
ipv4_src_dest_outer.source_address);
/* Handle IPv4 destination address */
HWS_SET_HDR(fc, match_param, IPV4_DST_O,
outer_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_31_0,
@@ -665,7 +682,7 @@ static int
hws_definer_conv_inner(struct mlx5hws_definer_conv_data *cd,
u32 *match_param)
{
bool is_s_ipv6, is_d_ipv6, smac_set, dmac_set;
bool is_ipv6, smac_set, dmac_set, ip_addr_set, ip_ver_set;
struct mlx5hws_definer_fc *fc = cd->fc;
struct mlx5hws_definer_fc *curr_fc;
u32 *s_ipv6, *d_ipv6;
@@ -677,6 +694,20 @@ hws_definer_conv_inner(struct mlx5hws_definer_conv_data *cd,
return -EINVAL;
}
ip_addr_set = HWS_IS_FLD_SET_SZ(match_param,
inner_headers.src_ipv4_src_ipv6,
0x80) ||
HWS_IS_FLD_SET_SZ(match_param,
inner_headers.dst_ipv4_dst_ipv6, 0x80);
ip_ver_set = HWS_IS_FLD_SET(match_param, inner_headers.ip_version) ||
HWS_IS_FLD_SET(match_param, inner_headers.ethertype);
if (ip_addr_set && !ip_ver_set) {
mlx5hws_err(cd->ctx,
"Unsupported match on IP address without version or ethertype\n");
return -EINVAL;
}
/* L2 Check ethertype */
HWS_SET_HDR(fc, match_param, ETH_TYPE_I,
inner_headers.ethertype,
@@ -728,10 +759,16 @@ hws_definer_conv_inner(struct mlx5hws_definer_conv_data *cd,
inner_headers.dst_ipv4_dst_ipv6.ipv6_layout);
/* Assume IPv6 is used if ipv6 bits are set */
is_s_ipv6 = s_ipv6[0] || s_ipv6[1] || s_ipv6[2];
is_d_ipv6 = d_ipv6[0] || d_ipv6[1] || d_ipv6[2];
is_ipv6 = s_ipv6[0] || s_ipv6[1] || s_ipv6[2] ||
d_ipv6[0] || d_ipv6[1] || d_ipv6[2];
if (is_s_ipv6) {
/* IHL is an IPv4-specific field. */
if (is_ipv6 && HWS_IS_FLD_SET(match_param, inner_headers.ipv4_ihl)) {
mlx5hws_err(cd->ctx, "Unsupported match on IPv6 address and IPv4 IHL\n");
return -EINVAL;
}
if (is_ipv6) {
/* Handle IPv6 source address */
HWS_SET_HDR(fc, match_param, IPV6_SRC_127_96_I,
inner_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_127_96,
@@ -745,13 +782,6 @@ hws_definer_conv_inner(struct mlx5hws_definer_conv_data *cd,
HWS_SET_HDR(fc, match_param, IPV6_SRC_31_0_I,
inner_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0,
ipv6_src_inner.ipv6_address_31_0);
} else {
/* Handle IPv4 source address */
HWS_SET_HDR(fc, match_param, IPV4_SRC_I,
inner_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0,
ipv4_src_dest_inner.source_address);
}
if (is_d_ipv6) {
/* Handle IPv6 destination address */
HWS_SET_HDR(fc, match_param, IPV6_DST_127_96_I,
inner_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_127_96,
@@ -766,6 +796,10 @@ hws_definer_conv_inner(struct mlx5hws_definer_conv_data *cd,
inner_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_31_0,
ipv6_dst_inner.ipv6_address_31_0);
} else {
/* Handle IPv4 source address */
HWS_SET_HDR(fc, match_param, IPV4_SRC_I,
inner_headers.src_ipv4_src_ipv6.ipv6_simple_layout.ipv6_31_0,
ipv4_src_dest_inner.source_address);
/* Handle IPv4 destination address */
HWS_SET_HDR(fc, match_param, IPV4_DST_I,
inner_headers.dst_ipv4_dst_ipv6.ipv6_simple_layout.ipv6_31_0,

View File

@@ -385,6 +385,30 @@ static int hws_matcher_bind_at(struct mlx5hws_matcher *matcher)
return 0;
}
static void hws_matcher_set_ip_version_match(struct mlx5hws_matcher *matcher)
{
int i;
for (i = 0; i < matcher->mt->fc_sz; i++) {
switch (matcher->mt->fc[i].fname) {
case MLX5HWS_DEFINER_FNAME_ETH_TYPE_O:
matcher->matches_outer_ethertype = 1;
break;
case MLX5HWS_DEFINER_FNAME_ETH_L3_TYPE_O:
matcher->matches_outer_ip_version = 1;
break;
case MLX5HWS_DEFINER_FNAME_ETH_TYPE_I:
matcher->matches_inner_ethertype = 1;
break;
case MLX5HWS_DEFINER_FNAME_ETH_L3_TYPE_I:
matcher->matches_inner_ip_version = 1;
break;
default:
break;
}
}
}
static int hws_matcher_bind_mt(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
@@ -401,6 +425,8 @@ static int hws_matcher_bind_mt(struct mlx5hws_matcher *matcher)
}
}
hws_matcher_set_ip_version_match(matcher);
/* Create an STE pool per matcher*/
pool_attr.table_type = matcher->tbl->type;
pool_attr.pool_type = MLX5HWS_POOL_TYPE_STE;

View File

@@ -50,6 +50,12 @@ struct mlx5hws_matcher_match_ste {
struct mlx5hws_pool *pool;
};
enum {
MLX5HWS_MATCHER_IPV_UNSET = 0,
MLX5HWS_MATCHER_IPV_4 = 1,
MLX5HWS_MATCHER_IPV_6 = 2,
};
struct mlx5hws_matcher {
struct mlx5hws_table *tbl;
struct mlx5hws_matcher_attr attr;
@@ -61,6 +67,12 @@ struct mlx5hws_matcher {
u8 num_of_action_stes;
/* enum mlx5hws_matcher_flags */
u8 flags;
u8 matches_outer_ethertype:1;
u8 matches_outer_ip_version:1;
u8 matches_inner_ethertype:1;
u8 matches_inner_ip_version:1;
u8 outer_ip_version:2;
u8 inner_ip_version:2;
u32 end_ft_id;
struct mlx5hws_matcher *col_matcher;
struct mlx5hws_matcher *resize_dst;

View File

@@ -655,6 +655,124 @@ int mlx5hws_rule_move_hws_add(struct mlx5hws_rule *rule,
return 0;
}
static u8 hws_rule_ethertype_to_matcher_ipv(u32 ethertype)
{
switch (ethertype) {
case ETH_P_IP:
return MLX5HWS_MATCHER_IPV_4;
case ETH_P_IPV6:
return MLX5HWS_MATCHER_IPV_6;
default:
return MLX5HWS_MATCHER_IPV_UNSET;
}
}
static u8 hws_rule_ip_version_to_matcher_ipv(u32 ip_version)
{
switch (ip_version) {
case 4:
return MLX5HWS_MATCHER_IPV_4;
case 6:
return MLX5HWS_MATCHER_IPV_6;
default:
return MLX5HWS_MATCHER_IPV_UNSET;
}
}
static int hws_rule_check_outer_ip_version(struct mlx5hws_matcher *matcher,
u32 *match_param)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
u8 outer_ipv_ether = MLX5HWS_MATCHER_IPV_UNSET;
u8 outer_ipv_ip = MLX5HWS_MATCHER_IPV_UNSET;
u8 outer_ipv, ver;
if (matcher->matches_outer_ethertype) {
ver = MLX5_GET(fte_match_param, match_param,
outer_headers.ethertype);
outer_ipv_ether = hws_rule_ethertype_to_matcher_ipv(ver);
}
if (matcher->matches_outer_ip_version) {
ver = MLX5_GET(fte_match_param, match_param,
outer_headers.ip_version);
outer_ipv_ip = hws_rule_ip_version_to_matcher_ipv(ver);
}
if (outer_ipv_ether != MLX5HWS_MATCHER_IPV_UNSET &&
outer_ipv_ip != MLX5HWS_MATCHER_IPV_UNSET &&
outer_ipv_ether != outer_ipv_ip) {
mlx5hws_err(ctx, "Rule matches on inconsistent outer ethertype and ip version\n");
return -EINVAL;
}
outer_ipv = outer_ipv_ether != MLX5HWS_MATCHER_IPV_UNSET ?
outer_ipv_ether : outer_ipv_ip;
if (outer_ipv != MLX5HWS_MATCHER_IPV_UNSET &&
matcher->outer_ip_version != MLX5HWS_MATCHER_IPV_UNSET &&
outer_ipv != matcher->outer_ip_version) {
mlx5hws_err(ctx, "Matcher and rule disagree on outer IP version\n");
return -EINVAL;
}
matcher->outer_ip_version = outer_ipv;
return 0;
}
static int hws_rule_check_inner_ip_version(struct mlx5hws_matcher *matcher,
u32 *match_param)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
u8 inner_ipv_ether = MLX5HWS_MATCHER_IPV_UNSET;
u8 inner_ipv_ip = MLX5HWS_MATCHER_IPV_UNSET;
u8 inner_ipv, ver;
if (matcher->matches_inner_ethertype) {
ver = MLX5_GET(fte_match_param, match_param,
inner_headers.ethertype);
inner_ipv_ether = hws_rule_ethertype_to_matcher_ipv(ver);
}
if (matcher->matches_inner_ip_version) {
ver = MLX5_GET(fte_match_param, match_param,
inner_headers.ip_version);
inner_ipv_ip = hws_rule_ip_version_to_matcher_ipv(ver);
}
if (inner_ipv_ether != MLX5HWS_MATCHER_IPV_UNSET &&
inner_ipv_ip != MLX5HWS_MATCHER_IPV_UNSET &&
inner_ipv_ether != inner_ipv_ip) {
mlx5hws_err(ctx, "Rule matches on inconsistent inner ethertype and ip version\n");
return -EINVAL;
}
inner_ipv = inner_ipv_ether != MLX5HWS_MATCHER_IPV_UNSET ?
inner_ipv_ether : inner_ipv_ip;
if (inner_ipv != MLX5HWS_MATCHER_IPV_UNSET &&
matcher->inner_ip_version != MLX5HWS_MATCHER_IPV_UNSET &&
inner_ipv != matcher->inner_ip_version) {
mlx5hws_err(ctx, "Matcher and rule disagree on inner IP version\n");
return -EINVAL;
}
matcher->inner_ip_version = inner_ipv;
return 0;
}
static int hws_rule_check_ip_version(struct mlx5hws_matcher *matcher,
u32 *match_param)
{
int ret;
ret = hws_rule_check_outer_ip_version(matcher, match_param);
if (unlikely(ret))
return ret;
ret = hws_rule_check_inner_ip_version(matcher, match_param);
if (unlikely(ret))
return ret;
return 0;
}
int mlx5hws_rule_create(struct mlx5hws_matcher *matcher,
u8 mt_idx,
u32 *match_param,
@@ -665,6 +783,10 @@ int mlx5hws_rule_create(struct mlx5hws_matcher *matcher,
{
int ret;
ret = hws_rule_check_ip_version(matcher, match_param);
if (unlikely(ret))
return ret;
rule_handle->matcher = matcher;
ret = hws_rule_enqueue_precheck_create(rule_handle, attr);