nfp: flower: support ct merging when mangle action exists
authorYinjun Zhang <yinjun.zhang@corigine.com>
Wed, 18 May 2022 07:50:55 +0000 (09:50 +0200)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 19 May 2022 09:48:30 +0000 (11:48 +0200)
Current implementation of ct merging doesn't support the case
that the fields mangling in pre_ct rules are matched in post_ct
rules.

This change is to support merging when mangling mac address,
ip address, tos, ttl and l4 port. VLAN and MPLS mangling is
not involved yet.

Signed-off-by: Yinjun Zhang <yinjun.zhang@corigine.com>
Signed-off-by: Louis Peens <louis.peens@corigine.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
Link: https://lore.kernel.org/r/20220518075055.130649-1-simon.horman@corigine.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/netronome/nfp/flower/conntrack.c
drivers/net/ethernet/netronome/nfp/flower/match.c

index 1edcd9f86c9c572b568b790cf057399fad4e845a..443a5d6eb57be6be7a99c7730654df35e47f9d69 100644 (file)
@@ -76,12 +76,119 @@ bool is_post_ct_flow(struct flow_cls_offload *flow)
        return false;
 }
 
+/**
+ * get_mangled_key() - Mangle the key if mangle act exists
+ * @rule:      rule that carries the actions
+ * @buf:       pointer to key to be mangled
+ * @offset:    used to adjust mangled offset in L2/L3/L4 header
+ * @key_sz:    key size
+ * @htype:     mangling type
+ *
+ * Returns buf where the mangled key stores.
+ */
+static void *get_mangled_key(struct flow_rule *rule, void *buf,
+                            u32 offset, size_t key_sz,
+                            enum flow_action_mangle_base htype)
+{
+       struct flow_action_entry *act;
+       u32 *val = (u32 *)buf;
+       u32 off, msk, key;
+       int i;
+
+       flow_action_for_each(i, act, &rule->action) {
+               if (act->id == FLOW_ACTION_MANGLE &&
+                   act->mangle.htype == htype) {
+                       off = act->mangle.offset - offset;
+                       msk = act->mangle.mask;
+                       key = act->mangle.val;
+
+                       /* Mangling is supposed to be u32 aligned */
+                       if (off % 4 || off >= key_sz)
+                               continue;
+
+                       val[off >> 2] &= msk;
+                       val[off >> 2] |= key;
+               }
+       }
+
+       return buf;
+}
+
+/* Only tos and ttl are involved in flow_match_ip structure, which
+ * doesn't conform to the layout of ip/ipv6 header definition. So
+ * they need particular process here: fill them into the ip/ipv6
+ * header, so that mangling actions can work directly.
+ */
+#define NFP_IPV4_TOS_MASK      GENMASK(23, 16)
+#define NFP_IPV4_TTL_MASK      GENMASK(31, 24)
+#define NFP_IPV6_TCLASS_MASK   GENMASK(27, 20)
+#define NFP_IPV6_HLIMIT_MASK   GENMASK(7, 0)
+static void *get_mangled_tos_ttl(struct flow_rule *rule, void *buf,
+                                bool is_v6)
+{
+       struct flow_match_ip match;
+       /* IPv4's ttl field is in third dword. */
+       __be32 ip_hdr[3];
+       u32 tmp, hdr_len;
+
+       flow_rule_match_ip(rule, &match);
+
+       if (is_v6) {
+               tmp = FIELD_PREP(NFP_IPV6_TCLASS_MASK, match.key->tos);
+               ip_hdr[0] = cpu_to_be32(tmp);
+               tmp = FIELD_PREP(NFP_IPV6_HLIMIT_MASK, match.key->ttl);
+               ip_hdr[1] = cpu_to_be32(tmp);
+               hdr_len = 2 * sizeof(__be32);
+       } else {
+               tmp = FIELD_PREP(NFP_IPV4_TOS_MASK, match.key->tos);
+               ip_hdr[0] = cpu_to_be32(tmp);
+               tmp = FIELD_PREP(NFP_IPV4_TTL_MASK, match.key->ttl);
+               ip_hdr[2] = cpu_to_be32(tmp);
+               hdr_len = 3 * sizeof(__be32);
+       }
+
+       get_mangled_key(rule, ip_hdr, 0, hdr_len,
+                       is_v6 ? FLOW_ACT_MANGLE_HDR_TYPE_IP6 :
+                               FLOW_ACT_MANGLE_HDR_TYPE_IP4);
+
+       match.key = buf;
+
+       if (is_v6) {
+               tmp = be32_to_cpu(ip_hdr[0]);
+               match.key->tos = FIELD_GET(NFP_IPV6_TCLASS_MASK, tmp);
+               tmp = be32_to_cpu(ip_hdr[1]);
+               match.key->ttl = FIELD_GET(NFP_IPV6_HLIMIT_MASK, tmp);
+       } else {
+               tmp = be32_to_cpu(ip_hdr[0]);
+               match.key->tos = FIELD_GET(NFP_IPV4_TOS_MASK, tmp);
+               tmp = be32_to_cpu(ip_hdr[2]);
+               match.key->ttl = FIELD_GET(NFP_IPV4_TTL_MASK, tmp);
+       }
+
+       return buf;
+}
+
+/* Note entry1 and entry2 are not swappable, entry1 should be
+ * the former flow whose mangle action need be taken into account
+ * if existed, and entry2 should be the latter flow whose action
+ * we don't care.
+ */
 static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1,
                              struct nfp_fl_ct_flow_entry *entry2)
 {
        unsigned int ovlp_keys = entry1->rule->match.dissector->used_keys &
                                 entry2->rule->match.dissector->used_keys;
-       bool out;
+       bool out, is_v6 = false;
+       u8 ip_proto = 0;
+       /* Temporary buffer for mangling keys, 64 is enough to cover max
+        * struct size of key in various fields that may be mangled.
+        * Supported fileds to mangle:
+        * mac_src/mac_dst(struct flow_match_eth_addrs, 12B)
+        * nw_tos/nw_ttl(struct flow_match_ip, 2B)
+        * nw_src/nw_dst(struct flow_match_ipv4/6_addrs, 32B)
+        * tp_src/tp_dst(struct flow_match_ports, 4B)
+        */
+       char buf[64];
 
        if (entry1->netdev && entry2->netdev &&
            entry1->netdev != entry2->netdev)
@@ -105,6 +212,14 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1,
 
                flow_rule_match_basic(entry1->rule, &match1);
                flow_rule_match_basic(entry2->rule, &match2);
+
+               /* n_proto field is a must in ct-related flows,
+                * it should be either ipv4 or ipv6.
+                */
+               is_v6 = match1.key->n_proto == htons(ETH_P_IPV6);
+               /* ip_proto field is a must when port field is cared */
+               ip_proto = match1.key->ip_proto;
+
                COMPARE_UNMASKED_FIELDS(match1, match2, &out);
                if (out)
                        goto check_failed;
@@ -115,6 +230,13 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1,
 
                flow_rule_match_ipv4_addrs(entry1->rule, &match1);
                flow_rule_match_ipv4_addrs(entry2->rule, &match2);
+
+               memcpy(buf, match1.key, sizeof(*match1.key));
+               match1.key = get_mangled_key(entry1->rule, buf,
+                                            offsetof(struct iphdr, saddr),
+                                            sizeof(*match1.key),
+                                            FLOW_ACT_MANGLE_HDR_TYPE_IP4);
+
                COMPARE_UNMASKED_FIELDS(match1, match2, &out);
                if (out)
                        goto check_failed;
@@ -125,16 +247,34 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1,
 
                flow_rule_match_ipv6_addrs(entry1->rule, &match1);
                flow_rule_match_ipv6_addrs(entry2->rule, &match2);
+
+               memcpy(buf, match1.key, sizeof(*match1.key));
+               match1.key = get_mangled_key(entry1->rule, buf,
+                                            offsetof(struct ipv6hdr, saddr),
+                                            sizeof(*match1.key),
+                                            FLOW_ACT_MANGLE_HDR_TYPE_IP6);
+
                COMPARE_UNMASKED_FIELDS(match1, match2, &out);
                if (out)
                        goto check_failed;
        }
 
        if (ovlp_keys & BIT(FLOW_DISSECTOR_KEY_PORTS)) {
+               enum flow_action_mangle_base htype = FLOW_ACT_MANGLE_UNSPEC;
                struct flow_match_ports match1, match2;
 
                flow_rule_match_ports(entry1->rule, &match1);
                flow_rule_match_ports(entry2->rule, &match2);
+
+               if (ip_proto == IPPROTO_UDP)
+                       htype = FLOW_ACT_MANGLE_HDR_TYPE_UDP;
+               else if (ip_proto == IPPROTO_TCP)
+                       htype = FLOW_ACT_MANGLE_HDR_TYPE_TCP;
+
+               memcpy(buf, match1.key, sizeof(*match1.key));
+               match1.key = get_mangled_key(entry1->rule, buf, 0,
+                                            sizeof(*match1.key), htype);
+
                COMPARE_UNMASKED_FIELDS(match1, match2, &out);
                if (out)
                        goto check_failed;
@@ -145,6 +285,12 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1,
 
                flow_rule_match_eth_addrs(entry1->rule, &match1);
                flow_rule_match_eth_addrs(entry2->rule, &match2);
+
+               memcpy(buf, match1.key, sizeof(*match1.key));
+               match1.key = get_mangled_key(entry1->rule, buf, 0,
+                                            sizeof(*match1.key),
+                                            FLOW_ACT_MANGLE_HDR_TYPE_ETH);
+
                COMPARE_UNMASKED_FIELDS(match1, match2, &out);
                if (out)
                        goto check_failed;
@@ -185,6 +331,8 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1,
 
                flow_rule_match_ip(entry1->rule, &match1);
                flow_rule_match_ip(entry2->rule, &match2);
+
+               match1.key = get_mangled_tos_ttl(entry1->rule, buf, is_v6);
                COMPARE_UNMASKED_FIELDS(match1, match2, &out);
                if (out)
                        goto check_failed;
@@ -256,98 +404,16 @@ check_failed:
        return -EINVAL;
 }
 
-static int nfp_ct_check_mangle_merge(struct flow_action_entry *a_in,
-                                    struct flow_rule *rule)
-{
-       enum flow_action_mangle_base htype = a_in->mangle.htype;
-       u32 offset = a_in->mangle.offset;
-
-       switch (htype) {
-       case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
-               if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS))
-                       return -EOPNOTSUPP;
-               break;
-       case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
-               if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
-                       struct flow_match_ip match;
-
-                       flow_rule_match_ip(rule, &match);
-                       if (offset == offsetof(struct iphdr, ttl) &&
-                           match.mask->ttl)
-                               return -EOPNOTSUPP;
-                       if (offset == round_down(offsetof(struct iphdr, tos), 4) &&
-                           match.mask->tos)
-                               return -EOPNOTSUPP;
-               }
-               if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
-                       struct flow_match_ipv4_addrs match;
-
-                       flow_rule_match_ipv4_addrs(rule, &match);
-                       if (offset == offsetof(struct iphdr, saddr) &&
-                           match.mask->src)
-                               return -EOPNOTSUPP;
-                       if (offset == offsetof(struct iphdr, daddr) &&
-                           match.mask->dst)
-                               return -EOPNOTSUPP;
-               }
-               break;
-       case FLOW_ACT_MANGLE_HDR_TYPE_IP6:
-               if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
-                       struct flow_match_ip match;
-
-                       flow_rule_match_ip(rule, &match);
-                       if (offset == round_down(offsetof(struct ipv6hdr, hop_limit), 4) &&
-                           match.mask->ttl)
-                               return -EOPNOTSUPP;
-                       /* for ipv6, tos and flow_lbl are in the same word */
-                       if (offset == round_down(offsetof(struct ipv6hdr, flow_lbl), 4) &&
-                           match.mask->tos)
-                               return -EOPNOTSUPP;
-               }
-               if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
-                       struct flow_match_ipv6_addrs match;
-
-                       flow_rule_match_ipv6_addrs(rule, &match);
-                       if (offset >= offsetof(struct ipv6hdr, saddr) &&
-                           offset < offsetof(struct ipv6hdr, daddr) &&
-                           memchr_inv(&match.mask->src, 0, sizeof(match.mask->src)))
-                               return -EOPNOTSUPP;
-                       if (offset >= offsetof(struct ipv6hdr, daddr) &&
-                           offset < sizeof(struct ipv6hdr) &&
-                           memchr_inv(&match.mask->dst, 0, sizeof(match.mask->dst)))
-                               return -EOPNOTSUPP;
-               }
-               break;
-       case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
-       case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
-               /* currently only can modify ports */
-               if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS))
-                       return -EOPNOTSUPP;
-               break;
-       default:
-               break;
-       }
-       return 0;
-}
-
 static int nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry *pre_ct_entry,
                                  struct nfp_fl_ct_flow_entry *post_ct_entry,
                                  struct nfp_fl_ct_flow_entry *nft_entry)
 {
        struct flow_action_entry *act;
-       int err, i;
+       int i;
 
        /* Check for pre_ct->action conflicts */
        flow_action_for_each(i, act, &pre_ct_entry->rule->action) {
                switch (act->id) {
-               case FLOW_ACTION_MANGLE:
-                       err = nfp_ct_check_mangle_merge(act, nft_entry->rule);
-                       if (err)
-                               return err;
-                       err = nfp_ct_check_mangle_merge(act, post_ct_entry->rule);
-                       if (err)
-                               return err;
-                       break;
                case FLOW_ACTION_VLAN_PUSH:
                case FLOW_ACTION_VLAN_POP:
                case FLOW_ACTION_VLAN_MANGLE:
@@ -363,11 +429,6 @@ static int nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry *pre_ct_entry,
        /* Check for nft->action conflicts */
        flow_action_for_each(i, act, &nft_entry->rule->action) {
                switch (act->id) {
-               case FLOW_ACTION_MANGLE:
-                       err = nfp_ct_check_mangle_merge(act, post_ct_entry->rule);
-                       if (err)
-                               return err;
-                       break;
                case FLOW_ACTION_VLAN_PUSH:
                case FLOW_ACTION_VLAN_POP:
                case FLOW_ACTION_VLAN_MANGLE:
@@ -924,7 +985,7 @@ static int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt,
        err = nfp_ct_merge_check(pre_ct_entry, nft_entry);
        if (err)
                return err;
-       err = nfp_ct_merge_check(post_ct_entry, nft_entry);
+       err = nfp_ct_merge_check(nft_entry, post_ct_entry);
        if (err)
                return err;
        err = nfp_ct_check_meta(post_ct_entry, nft_entry);
@@ -1009,7 +1070,7 @@ static int nfp_ct_do_tc_merge(struct nfp_fl_ct_zone_entry *zt,
        if (post_ct_entry->chain_index != pre_ct_entry->chain_index)
                return -EINVAL;
 
-       err = nfp_ct_merge_check(post_ct_entry, pre_ct_entry);
+       err = nfp_ct_merge_check(pre_ct_entry, post_ct_entry);
        if (err)
                return err;
 
index 9d86eea4dc169910d6c20e309b43ec5deb0d06d6..193a167a6762e81f42878f6858ca3769f2e120c8 100644 (file)
@@ -98,16 +98,18 @@ nfp_flower_compile_mac(struct nfp_flower_mac_mpls *ext,
 {
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
                struct flow_match_eth_addrs match;
+               u8 tmp;
                int i;
 
                flow_rule_match_eth_addrs(rule, &match);
                /* Populate mac frame. */
                for (i = 0; i < ETH_ALEN; i++) {
-                       ext->mac_dst[i] |= match.key->dst[i] &
-                                          match.mask->dst[i];
+                       tmp = match.key->dst[i] & match.mask->dst[i];
+                       ext->mac_dst[i] |= tmp & (~msk->mac_dst[i]);
                        msk->mac_dst[i] |= match.mask->dst[i];
-                       ext->mac_src[i] |= match.key->src[i] &
-                                          match.mask->src[i];
+
+                       tmp = match.key->src[i] & match.mask->src[i];
+                       ext->mac_src[i] |= tmp & (~msk->mac_src[i]);
                        msk->mac_src[i] |= match.mask->src[i];
                }
        }
@@ -189,11 +191,16 @@ nfp_flower_compile_tport(struct nfp_flower_tp_ports *ext,
 {
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
                struct flow_match_ports match;
+               __be16 tmp;
 
                flow_rule_match_ports(rule, &match);
-               ext->port_src |= match.key->src & match.mask->src;
-               ext->port_dst |= match.key->dst & match.mask->dst;
+
+               tmp = match.key->src & match.mask->src;
+               ext->port_src |= tmp & (~msk->port_src);
                msk->port_src |= match.mask->src;
+
+               tmp = match.key->dst & match.mask->dst;
+               ext->port_dst |= tmp & (~msk->port_dst);
                msk->port_dst |= match.mask->dst;
        }
 }
@@ -212,11 +219,16 @@ nfp_flower_compile_ip_ext(struct nfp_flower_ip_ext *ext,
 
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
                struct flow_match_ip match;
+               u8 tmp;
 
                flow_rule_match_ip(rule, &match);
-               ext->tos |= match.key->tos & match.mask->tos;
-               ext->ttl |= match.key->ttl & match.mask->ttl;
+
+               tmp = match.key->tos & match.mask->tos;
+               ext->tos |= tmp & (~msk->tos);
                msk->tos |= match.mask->tos;
+
+               tmp = match.key->ttl & match.mask->ttl;
+               ext->ttl |= tmp & (~msk->ttl);
                msk->ttl |= match.mask->ttl;
        }
 
@@ -325,11 +337,16 @@ nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *ext,
 {
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
                struct flow_match_ipv4_addrs match;
+               __be32 tmp;
 
                flow_rule_match_ipv4_addrs(rule, &match);
-               ext->ipv4_src |= match.key->src & match.mask->src;
-               ext->ipv4_dst |= match.key->dst & match.mask->dst;
+
+               tmp = match.key->src & match.mask->src;
+               ext->ipv4_src |= tmp & (~msk->ipv4_src);
                msk->ipv4_src |= match.mask->src;
+
+               tmp = match.key->dst & match.mask->dst;
+               ext->ipv4_dst |= tmp & (~msk->ipv4_dst);
                msk->ipv4_dst |= match.mask->dst;
        }
 
@@ -342,15 +359,21 @@ nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *ext,
 {
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
                struct flow_match_ipv6_addrs match;
+               u8 tmp;
                int i;
 
                flow_rule_match_ipv6_addrs(rule, &match);
                for (i = 0; i < sizeof(ext->ipv6_src); i++) {
-                       ext->ipv6_src.s6_addr[i] |= match.key->src.s6_addr[i] &
-                                                   match.mask->src.s6_addr[i];
-                       ext->ipv6_dst.s6_addr[i] |= match.key->dst.s6_addr[i] &
-                                                   match.mask->dst.s6_addr[i];
+                       tmp = match.key->src.s6_addr[i] &
+                             match.mask->src.s6_addr[i];
+                       ext->ipv6_src.s6_addr[i] |= tmp &
+                                                   (~msk->ipv6_src.s6_addr[i]);
                        msk->ipv6_src.s6_addr[i] |= match.mask->src.s6_addr[i];
+
+                       tmp = match.key->dst.s6_addr[i] &
+                             match.mask->dst.s6_addr[i];
+                       ext->ipv6_dst.s6_addr[i] |= tmp &
+                                                   (~msk->ipv6_dst.s6_addr[i]);
                        msk->ipv6_dst.s6_addr[i] |= match.mask->dst.s6_addr[i];
                }
        }