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:
parent
b885aab3d3
commit
e43d940f48
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue