nfp: flower: support ct merging when mangle action exists

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>
This commit is contained in:
Yinjun Zhang 2022-05-18 09:50:55 +02:00 committed by Paolo Abeni
parent b885aab3d3
commit e43d940f48
2 changed files with 189 additions and 105 deletions

View 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;

View 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];
}
}