@@ -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 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1,
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;
@@ -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];
}
}