mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-07 19:49:33 -04:00
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:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user