net/mlx5e: Fix matching on modified inner ip_ecn bits
[ Upstream commitb6dfff21a1] Tunnel device follows RFC 6040, and during decapsulation inner ip_ecn might change depending on inner and outer ip_ecn as follows: +---------+----------------------------------------+ |Arriving | Arriving Outer Header | | Inner +---------+---------+---------+----------+ | Header | Not-ECT | ECT(0) | ECT(1) | CE | +---------+---------+---------+---------+----------+ | Not-ECT | Not-ECT | Not-ECT | Not-ECT | <drop> | | ECT(0) | ECT(0) | ECT(0) | ECT(1) | CE* | | ECT(1) | ECT(1) | ECT(1) | ECT(1)* | CE* | | CE | CE | CE | CE | CE | +---------+---------+---------+---------+----------+ Cells marked above are changed from original inner packet ip_ecn value. Tc then matches on the modified inner ip_ecn, but hw offload which matches the inner ip_ecn value before decap, will fail. Fix that by mapping all the cases of outer and inner ip_ecn matching, and only supporting cases where we know inner wouldn't be changed by decap, or in the outer ip_ecn=CE case, inner ip_ecn didn't matter. Fixes:bcef735c59("net/mlx5e: Offload TC matching on tos/ttl for ip tunnels") Signed-off-by: Paul Blakey <paulb@nvidia.com> Reviewed-by: Oz Shlomo <ozsh@nvidia.com> Reviewed-by: Eli Cohen <elic@nvidia.com> Reviewed-by: Roi Dayan <roid@nvidia.com> Signed-off-by: Saeed Mahameed <saeedm@nvidia.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
46a8a5a429
commit
7c1f9797e1
@@ -1874,6 +1874,111 @@ u8 mlx5e_tc_get_ip_version(struct mlx5_flow_spec *spec, bool outer)
|
||||
return ip_version;
|
||||
}
|
||||
|
||||
/* Tunnel device follows RFC 6040, see include/net/inet_ecn.h.
|
||||
* And changes inner ip_ecn depending on inner and outer ip_ecn as follows:
|
||||
* +---------+----------------------------------------+
|
||||
* |Arriving | Arriving Outer Header |
|
||||
* | Inner +---------+---------+---------+----------+
|
||||
* | Header | Not-ECT | ECT(0) | ECT(1) | CE |
|
||||
* +---------+---------+---------+---------+----------+
|
||||
* | Not-ECT | Not-ECT | Not-ECT | Not-ECT | <drop> |
|
||||
* | ECT(0) | ECT(0) | ECT(0) | ECT(1) | CE* |
|
||||
* | ECT(1) | ECT(1) | ECT(1) | ECT(1)* | CE* |
|
||||
* | CE | CE | CE | CE | CE |
|
||||
* +---------+---------+---------+---------+----------+
|
||||
*
|
||||
* Tc matches on inner after decapsulation on tunnel device, but hw offload matches
|
||||
* the inner ip_ecn value before hardware decap action.
|
||||
*
|
||||
* Cells marked are changed from original inner packet ip_ecn value during decap, and
|
||||
* so matching those values on inner ip_ecn before decap will fail.
|
||||
*
|
||||
* The following helper allows offload when inner ip_ecn won't be changed by outer ip_ecn,
|
||||
* except for the outer ip_ecn = CE, where in all cases inner ip_ecn will be changed to CE,
|
||||
* and such we can drop the inner ip_ecn=CE match.
|
||||
*/
|
||||
|
||||
static int mlx5e_tc_verify_tunnel_ecn(struct mlx5e_priv *priv,
|
||||
struct flow_cls_offload *f,
|
||||
bool *match_inner_ecn)
|
||||
{
|
||||
u8 outer_ecn_mask = 0, outer_ecn_key = 0, inner_ecn_mask = 0, inner_ecn_key = 0;
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(f);
|
||||
struct netlink_ext_ack *extack = f->common.extack;
|
||||
struct flow_match_ip match;
|
||||
|
||||
*match_inner_ecn = true;
|
||||
|
||||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IP)) {
|
||||
flow_rule_match_enc_ip(rule, &match);
|
||||
outer_ecn_key = match.key->tos & INET_ECN_MASK;
|
||||
outer_ecn_mask = match.mask->tos & INET_ECN_MASK;
|
||||
}
|
||||
|
||||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
|
||||
flow_rule_match_ip(rule, &match);
|
||||
inner_ecn_key = match.key->tos & INET_ECN_MASK;
|
||||
inner_ecn_mask = match.mask->tos & INET_ECN_MASK;
|
||||
}
|
||||
|
||||
if (outer_ecn_mask != 0 && outer_ecn_mask != INET_ECN_MASK) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Partial match on enc_tos ecn bits isn't supported");
|
||||
netdev_warn(priv->netdev, "Partial match on enc_tos ecn bits isn't supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!outer_ecn_mask) {
|
||||
if (!inner_ecn_mask)
|
||||
return 0;
|
||||
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Matching on tos ecn bits without also matching enc_tos ecn bits isn't supported");
|
||||
netdev_warn(priv->netdev,
|
||||
"Matching on tos ecn bits without also matching enc_tos ecn bits isn't supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (inner_ecn_mask && inner_ecn_mask != INET_ECN_MASK) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Partial match on tos ecn bits with match on enc_tos ecn bits isn't supported");
|
||||
netdev_warn(priv->netdev,
|
||||
"Partial match on tos ecn bits with match on enc_tos ecn bits isn't supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!inner_ecn_mask)
|
||||
return 0;
|
||||
|
||||
/* Both inner and outer have full mask on ecn */
|
||||
|
||||
if (outer_ecn_key == INET_ECN_ECT_1) {
|
||||
/* inner ecn might change by DECAP action */
|
||||
|
||||
NL_SET_ERR_MSG_MOD(extack, "Match on enc_tos ecn = ECT(1) isn't supported");
|
||||
netdev_warn(priv->netdev, "Match on enc_tos ecn = ECT(1) isn't supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (outer_ecn_key != INET_ECN_CE)
|
||||
return 0;
|
||||
|
||||
if (inner_ecn_key != INET_ECN_CE) {
|
||||
/* Can't happen in software, as packet ecn will be changed to CE after decap */
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Match on tos enc_tos ecn = CE while match on tos ecn != CE isn't supported");
|
||||
netdev_warn(priv->netdev,
|
||||
"Match on tos enc_tos ecn = CE while match on tos ecn != CE isn't supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* outer ecn = CE, inner ecn = CE, as decap will change inner ecn to CE in anycase,
|
||||
* drop match on inner ecn
|
||||
*/
|
||||
*match_inner_ecn = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_tunnel_attr(struct mlx5e_priv *priv,
|
||||
struct mlx5e_tc_flow *flow,
|
||||
struct mlx5_flow_spec *spec,
|
||||
@@ -2067,6 +2172,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(f);
|
||||
struct flow_dissector *dissector = rule->match.dissector;
|
||||
enum fs_flow_table_type fs_type;
|
||||
bool match_inner_ecn = true;
|
||||
u16 addr_type = 0;
|
||||
u8 ip_proto = 0;
|
||||
u8 *match_level;
|
||||
@@ -2120,6 +2226,10 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
|
||||
headers_c = get_match_inner_headers_criteria(spec);
|
||||
headers_v = get_match_inner_headers_value(spec);
|
||||
}
|
||||
|
||||
err = mlx5e_tc_verify_tunnel_ecn(priv, f, &match_inner_ecn);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mlx5e_flower_parse_meta(filter_dev, f);
|
||||
@@ -2341,10 +2451,12 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
|
||||
struct flow_match_ip match;
|
||||
|
||||
flow_rule_match_ip(rule, &match);
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn,
|
||||
match.mask->tos & 0x3);
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn,
|
||||
match.key->tos & 0x3);
|
||||
if (match_inner_ecn) {
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn,
|
||||
match.mask->tos & 0x3);
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn,
|
||||
match.key->tos & 0x3);
|
||||
}
|
||||
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp,
|
||||
match.mask->tos >> 2);
|
||||
|
||||
Reference in New Issue
Block a user