Commit e43d940f authored by Yinjun Zhang's avatar Yinjun Zhang Committed by Paolo Abeni
Browse files

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: default avatarYinjun Zhang <yinjun.zhang@corigine.com>
Signed-off-by: default avatarLouis Peens <louis.peens@corigine.com>
Signed-off-by: default avatarSimon Horman <simon.horman@corigine.com>
Link: https://lore.kernel.org/r/20220518075055.130649-1-simon.horman@corigine.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent b885aab3
Loading
Loading
Loading
Loading
+152 −91
Original line number Diff line number Diff line
@@ -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;

+37 −14
Original line number Diff line number Diff line
@@ -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] &
			tmp = 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];
			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];
		}
	}