sfc: support TC decap rules matching on enc_src_port
authorEdward Cree <ecree.xilinx@gmail.com>
Thu, 11 May 2023 19:47:31 +0000 (20:47 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 May 2023 09:37:02 +0000 (10:37 +0100)
Allow efx_tc_encap_match entries to include a udp_sport and a
 udp_sport_mask.  As with enc_ip_tos, use pseudos to enforce that all
 encap matches within a given <src_ip,dst_ip,udp_dport> tuple have
 the same udp_sport_mask.
Note that since we use a single layer of pseudos for both fields, two
 matches that differ in (say) udp_sport value aren't permitted to have
 different ip_tos_mask, even though this would technically be safe.
Current userland TC does not support setting enc_src_port; this patch
 was tested with an iproute2 patched to support it.

Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/sfc/mae.c
drivers/net/ethernet/sfc/mae.h
drivers/net/ethernet/sfc/tc.c
drivers/net/ethernet/sfc/tc.h

index 8f4bb5d..37a4c69 100644 (file)
@@ -485,7 +485,7 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
  * MAE.  All the fields are exact-match, except possibly ENC_IP_TOS.
  */
 int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
-                                  u8 ip_tos_mask,
+                                  u8 ip_tos_mask, __be16 udp_sport_mask,
                                   struct netlink_ext_ack *extack)
 {
        u8 *supported_fields = efx->tc->caps->outer_rule_fields;
@@ -506,6 +506,14 @@ int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
        if (CHECK(ENC_L4_DPORT) ||
            CHECK(ENC_IP_PROTO))
                return rc;
+       typ = classify_mask((const u8 *)&udp_sport_mask, sizeof(udp_sport_mask));
+       rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_L4_SPORT],
+                                        typ);
+       if (rc) {
+               NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field %s",
+                                      mask_type_name(typ), "enc_src_port");
+               return rc;
+       }
        typ = classify_mask(&ip_tos_mask, sizeof(ip_tos_mask));
        rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_IP_TOS],
                                         typ);
@@ -1011,6 +1019,10 @@ int efx_mae_register_encap_match(struct efx_nic *efx,
                                encap->udp_dport);
        MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
                                ~(__be16)0);
+       MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE,
+                               encap->udp_sport);
+       MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
+                               encap->udp_sport_mask);
        MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO, IPPROTO_UDP);
        MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK, ~0);
        MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS,
index cec61bf..1cf8dfe 100644 (file)
@@ -82,7 +82,7 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
                             const struct efx_tc_match_fields *mask,
                             struct netlink_ext_ack *extack);
 int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
-                                  u8 ip_tos_mask,
+                                  u8 ip_tos_mask, __be16 udp_sport_mask,
                                   struct netlink_ext_ack *extack);
 int efx_mae_check_encap_type_supported(struct efx_nic *efx,
                                       enum efx_encap_type typ);
index 8e1769d..da684b4 100644 (file)
@@ -377,6 +377,7 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
                                            enum efx_encap_type type,
                                            enum efx_tc_em_pseudo_type em_type,
                                            u8 child_ip_tos_mask,
+                                           __be16 child_udp_sport_mask,
                                            struct netlink_ext_ack *extack)
 {
        struct efx_tc_encap_match *encap, *old, *pseudo = NULL;
@@ -425,11 +426,7 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
                NL_SET_ERR_MSG_MOD(extack, "Egress encap match is not exact on dst UDP port");
                return -EOPNOTSUPP;
        }
-       if (match->mask.enc_sport) {
-               NL_SET_ERR_MSG_MOD(extack, "Egress encap match on src UDP port not supported");
-               return -EOPNOTSUPP;
-       }
-       if (match->mask.enc_ip_tos) {
+       if (match->mask.enc_sport || match->mask.enc_ip_tos) {
                struct efx_tc_match pmatch = *match;
 
                if (em_type == EFX_TC_EM_PSEUDO_MASK) { /* can't happen */
@@ -438,9 +435,12 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
                }
                pmatch.value.enc_ip_tos = 0;
                pmatch.mask.enc_ip_tos = 0;
+               pmatch.value.enc_sport = 0;
+               pmatch.mask.enc_sport = 0;
                rc = efx_tc_flower_record_encap_match(efx, &pmatch, type,
                                                      EFX_TC_EM_PSEUDO_MASK,
                                                      match->mask.enc_ip_tos,
+                                                     match->mask.enc_sport,
                                                      extack);
                if (rc)
                        return rc;
@@ -452,7 +452,8 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
                goto fail_pseudo;
        }
 
-       rc = efx_mae_check_encap_match_caps(efx, ipv6, match->mask.enc_ip_tos, extack);
+       rc = efx_mae_check_encap_match_caps(efx, ipv6, match->mask.enc_ip_tos,
+                                           match->mask.enc_sport, extack);
        if (rc)
                goto fail_pseudo;
 
@@ -472,6 +473,9 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
        encap->ip_tos = match->value.enc_ip_tos;
        encap->ip_tos_mask = match->mask.enc_ip_tos;
        encap->child_ip_tos_mask = child_ip_tos_mask;
+       encap->udp_sport = match->value.enc_sport;
+       encap->udp_sport_mask = match->mask.enc_sport;
+       encap->child_udp_sport_mask = child_udp_sport_mask;
        encap->type = em_type;
        encap->pseudo = pseudo;
        old = rhashtable_lookup_get_insert_fast(&efx->tc->encap_match_ht,
@@ -493,9 +497,9 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
                        NL_SET_ERR_MSG_MOD(extack, "Pseudo encap match conflicts with existing direct entry");
                        return -EEXIST;
                case EFX_TC_EM_PSEUDO_MASK:
-                       /* old EM is protecting a ToS-qualified filter, so may
-                        * only be shared with another pseudo for the same
-                        * ToS mask.
+                       /* old EM is protecting a ToS- or src port-qualified
+                        * filter, so may only be shared with another pseudo
+                        * for the same ToS and src port masks.
                         */
                        if (em_type != EFX_TC_EM_PSEUDO_MASK) {
                                NL_SET_ERR_MSG_FMT_MOD(extack,
@@ -510,6 +514,13 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
                                                       old->child_ip_tos_mask);
                                return -EEXIST;
                        }
+                       if (child_udp_sport_mask != old->child_udp_sport_mask) {
+                               NL_SET_ERR_MSG_FMT_MOD(extack,
+                                                      "Pseudo encap match for UDP src port mask %#x conflicts with existing pseudo(MASK) entry for mask %#x",
+                                                      child_udp_sport_mask,
+                                                      old->child_udp_sport_mask);
+                               return -EEXIST;
+                       }
                        break;
                default: /* Unrecognised pseudo-type.  Just say no */
                        NL_SET_ERR_MSG_FMT_MOD(extack,
@@ -704,7 +715,7 @@ static int efx_tc_flower_replace_foreign(struct efx_nic *efx,
                }
 
                rc = efx_tc_flower_record_encap_match(efx, &match, type,
-                                                     EFX_TC_EM_DIRECT, 0,
+                                                     EFX_TC_EM_DIRECT, 0, 0,
                                                      extack);
                if (rc)
                        goto release;
index 0f14481..24e9640 100644 (file)
@@ -84,11 +84,11 @@ static inline bool efx_tc_match_is_encap(const struct efx_tc_match_fields *mask)
  * @EFX_TC_EM_DIRECT: real HW entry in Outer Rule table; not a pseudo.
  *     Hardware index in &struct efx_tc_encap_match.fw_id is valid.
  * @EFX_TC_EM_PSEUDO_MASK: registered by an encap match which includes a
- *     match on an optional field (currently only ip_tos), to prevent an
- *     overlapping encap match _without_ optional fields.
+ *     match on an optional field (currently ip_tos and/or udp_sport),
+ *     to prevent an overlapping encap match _without_ optional fields.
  *     The pseudo encap match may be referenced again by an encap match
- *     with a different ip_tos value, but all ip_tos_mask must match the
- *     first (stored in our child_ip_tos_mask).
+ *     with different values for these fields, but all masks must match the
+ *     first (stored in our child_* fields).
  */
 enum efx_tc_em_pseudo_type {
        EFX_TC_EM_DIRECT,
@@ -99,10 +99,12 @@ struct efx_tc_encap_match {
        __be32 src_ip, dst_ip;
        struct in6_addr src_ip6, dst_ip6;
        __be16 udp_dport;
+       __be16 udp_sport, udp_sport_mask;
        u8 ip_tos, ip_tos_mask;
        struct rhash_head linkage;
        enum efx_encap_type tun_type;
        u8 child_ip_tos_mask;
+       __be16 child_udp_sport_mask;
        refcount_t ref;
        enum efx_tc_em_pseudo_type type;
        u32 fw_id; /* index of this entry in firmware encap match table */