Skip to content

Commit

Permalink
nfp: flower: support ct merging when mangle action exists
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yinjun Zhang authored and Paolo Abeni committed May 19, 2022
1 parent b885aab commit e43d940
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 105 deletions.
243 changes: 152 additions & 91 deletions drivers/net/ethernet/netronome/nfp/flower/conntrack.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down
Loading

0 comments on commit e43d940

Please sign in to comment.