@@ -1949,6 +1949,111 @@ u8 mlx5e_tc_get_ip_version(struct mlx5_flow_spec *spec, bool outer)
19491949 return ip_version ;
19501950}
19511951
1952+ /* Tunnel device follows RFC 6040, see include/net/inet_ecn.h.
1953+ * And changes inner ip_ecn depending on inner and outer ip_ecn as follows:
1954+ * +---------+----------------------------------------+
1955+ * |Arriving | Arriving Outer Header |
1956+ * | Inner +---------+---------+---------+----------+
1957+ * | Header | Not-ECT | ECT(0) | ECT(1) | CE |
1958+ * +---------+---------+---------+---------+----------+
1959+ * | Not-ECT | Not-ECT | Not-ECT | Not-ECT | <drop> |
1960+ * | ECT(0) | ECT(0) | ECT(0) | ECT(1) | CE* |
1961+ * | ECT(1) | ECT(1) | ECT(1) | ECT(1)* | CE* |
1962+ * | CE | CE | CE | CE | CE |
1963+ * +---------+---------+---------+---------+----------+
1964+ *
1965+ * Tc matches on inner after decapsulation on tunnel device, but hw offload matches
1966+ * the inner ip_ecn value before hardware decap action.
1967+ *
1968+ * Cells marked are changed from original inner packet ip_ecn value during decap, and
1969+ * so matching those values on inner ip_ecn before decap will fail.
1970+ *
1971+ * The following helper allows offload when inner ip_ecn won't be changed by outer ip_ecn,
1972+ * except for the outer ip_ecn = CE, where in all cases inner ip_ecn will be changed to CE,
1973+ * and such we can drop the inner ip_ecn=CE match.
1974+ */
1975+
1976+ static int mlx5e_tc_verify_tunnel_ecn (struct mlx5e_priv * priv ,
1977+ struct flow_cls_offload * f ,
1978+ bool * match_inner_ecn )
1979+ {
1980+ u8 outer_ecn_mask = 0 , outer_ecn_key = 0 , inner_ecn_mask = 0 , inner_ecn_key = 0 ;
1981+ struct flow_rule * rule = flow_cls_offload_flow_rule (f );
1982+ struct netlink_ext_ack * extack = f -> common .extack ;
1983+ struct flow_match_ip match ;
1984+
1985+ * match_inner_ecn = true;
1986+
1987+ if (flow_rule_match_key (rule , FLOW_DISSECTOR_KEY_ENC_IP )) {
1988+ flow_rule_match_enc_ip (rule , & match );
1989+ outer_ecn_key = match .key -> tos & INET_ECN_MASK ;
1990+ outer_ecn_mask = match .mask -> tos & INET_ECN_MASK ;
1991+ }
1992+
1993+ if (flow_rule_match_key (rule , FLOW_DISSECTOR_KEY_IP )) {
1994+ flow_rule_match_ip (rule , & match );
1995+ inner_ecn_key = match .key -> tos & INET_ECN_MASK ;
1996+ inner_ecn_mask = match .mask -> tos & INET_ECN_MASK ;
1997+ }
1998+
1999+ if (outer_ecn_mask != 0 && outer_ecn_mask != INET_ECN_MASK ) {
2000+ NL_SET_ERR_MSG_MOD (extack , "Partial match on enc_tos ecn bits isn't supported" );
2001+ netdev_warn (priv -> netdev , "Partial match on enc_tos ecn bits isn't supported" );
2002+ return - EOPNOTSUPP ;
2003+ }
2004+
2005+ if (!outer_ecn_mask ) {
2006+ if (!inner_ecn_mask )
2007+ return 0 ;
2008+
2009+ NL_SET_ERR_MSG_MOD (extack ,
2010+ "Matching on tos ecn bits without also matching enc_tos ecn bits isn't supported" );
2011+ netdev_warn (priv -> netdev ,
2012+ "Matching on tos ecn bits without also matching enc_tos ecn bits isn't supported" );
2013+ return - EOPNOTSUPP ;
2014+ }
2015+
2016+ if (inner_ecn_mask && inner_ecn_mask != INET_ECN_MASK ) {
2017+ NL_SET_ERR_MSG_MOD (extack ,
2018+ "Partial match on tos ecn bits with match on enc_tos ecn bits isn't supported" );
2019+ netdev_warn (priv -> netdev ,
2020+ "Partial match on tos ecn bits with match on enc_tos ecn bits isn't supported" );
2021+ return - EOPNOTSUPP ;
2022+ }
2023+
2024+ if (!inner_ecn_mask )
2025+ return 0 ;
2026+
2027+ /* Both inner and outer have full mask on ecn */
2028+
2029+ if (outer_ecn_key == INET_ECN_ECT_1 ) {
2030+ /* inner ecn might change by DECAP action */
2031+
2032+ NL_SET_ERR_MSG_MOD (extack , "Match on enc_tos ecn = ECT(1) isn't supported" );
2033+ netdev_warn (priv -> netdev , "Match on enc_tos ecn = ECT(1) isn't supported" );
2034+ return - EOPNOTSUPP ;
2035+ }
2036+
2037+ if (outer_ecn_key != INET_ECN_CE )
2038+ return 0 ;
2039+
2040+ if (inner_ecn_key != INET_ECN_CE ) {
2041+ /* Can't happen in software, as packet ecn will be changed to CE after decap */
2042+ NL_SET_ERR_MSG_MOD (extack ,
2043+ "Match on tos enc_tos ecn = CE while match on tos ecn != CE isn't supported" );
2044+ netdev_warn (priv -> netdev ,
2045+ "Match on tos enc_tos ecn = CE while match on tos ecn != CE isn't supported" );
2046+ return - EOPNOTSUPP ;
2047+ }
2048+
2049+ /* outer ecn = CE, inner ecn = CE, as decap will change inner ecn to CE in anycase,
2050+ * drop match on inner ecn
2051+ */
2052+ * match_inner_ecn = false;
2053+
2054+ return 0 ;
2055+ }
2056+
19522057static int parse_tunnel_attr (struct mlx5e_priv * priv ,
19532058 struct mlx5e_tc_flow * flow ,
19542059 struct mlx5_flow_spec * spec ,
@@ -2144,6 +2249,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
21442249 struct flow_rule * rule = flow_cls_offload_flow_rule (f );
21452250 struct flow_dissector * dissector = rule -> match .dissector ;
21462251 enum fs_flow_table_type fs_type ;
2252+ bool match_inner_ecn = true;
21472253 u16 addr_type = 0 ;
21482254 u8 ip_proto = 0 ;
21492255 u8 * match_level ;
@@ -2197,6 +2303,10 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
21972303 headers_c = get_match_inner_headers_criteria (spec );
21982304 headers_v = get_match_inner_headers_value (spec );
21992305 }
2306+
2307+ err = mlx5e_tc_verify_tunnel_ecn (priv , f , & match_inner_ecn );
2308+ if (err )
2309+ return err ;
22002310 }
22012311
22022312 err = mlx5e_flower_parse_meta (filter_dev , f );
@@ -2420,10 +2530,12 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
24202530 struct flow_match_ip match ;
24212531
24222532 flow_rule_match_ip (rule , & match );
2423- MLX5_SET (fte_match_set_lyr_2_4 , headers_c , ip_ecn ,
2424- match .mask -> tos & 0x3 );
2425- MLX5_SET (fte_match_set_lyr_2_4 , headers_v , ip_ecn ,
2426- match .key -> tos & 0x3 );
2533+ if (match_inner_ecn ) {
2534+ MLX5_SET (fte_match_set_lyr_2_4 , headers_c , ip_ecn ,
2535+ match .mask -> tos & 0x3 );
2536+ MLX5_SET (fte_match_set_lyr_2_4 , headers_v , ip_ecn ,
2537+ match .key -> tos & 0x3 );
2538+ }
24272539
24282540 MLX5_SET (fte_match_set_lyr_2_4 , headers_c , ip_dscp ,
24292541 match .mask -> tos >> 2 );
0 commit comments