diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c
index 1edcd9f86c9c..443a5d6eb57b 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c
@@ -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;
 
diff --git a/drivers/net/ethernet/netronome/nfp/flower/match.c b/drivers/net/ethernet/netronome/nfp/flower/match.c
index 9d86eea4dc16..193a167a6762 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/match.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/match.c
@@ -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];
 		}
 	}
