[][Suppport MAPE binding HNAT]

[Description]
Fix software to support MAPE binding HNAT

[Release-log]
N/A

Change-Id: Ia47c403b42417641f7b719ebc717d4e60f200bac
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5936389
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 69f1c21..d4b9b63 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -1950,20 +1950,35 @@
 				      size_t count, loff_t *data)
 {
 	char buf = 0;
-	int len = count;
-
-	if (copy_from_user(&buf, buffer, len))
+	int i;
+	u32 ppe_cfg;
+	
+	if ((count < 1) || copy_from_user(&buf, buffer, sizeof(buf)))
 		return -EFAULT;
 
-	if (buf == '1' && !mape_toggle) {
+	if (buf == '1') {
 		pr_info("mape is going to be enabled, ds-lite is going to be disabled !\n");
 		mape_toggle = 1;
-	} else if (buf == '0' && mape_toggle) {
+	} else if (buf == '0') {
 		pr_info("ds-lite is going to be enabled, mape is going to be disabled !\n");
 		mape_toggle = 0;
+	} else {
+		pr_info("Invalid parameter.\n");
+		return -EFAULT;
+	}
+
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		ppe_cfg = readl(hnat_priv->ppe_base[i] + PPE_FLOW_CFG);
+
+		if (mape_toggle)
+			ppe_cfg &= ~BIT_IPV4_DSL_EN;
+		else
+			ppe_cfg |= BIT_IPV4_DSL_EN;
+
+		writel(ppe_cfg, hnat_priv->ppe_base[i] + PPE_FLOW_CFG);
 	}
 
-	return len;
+	return count;
 }
 
 static const struct file_operations hnat_mape_toggle_fops = {
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index 2df6d14..794f55b 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
@@ -608,6 +608,13 @@
 	}
 }
 
+static void ppe_fill_flow_lbl(struct foe_entry *entry, struct ipv6hdr *ip6h)
+{
+	entry->ipv4_dslite.flow_lbl[0] = ip6h->flow_lbl[2];
+	entry->ipv4_dslite.flow_lbl[1] = ip6h->flow_lbl[1];
+	entry->ipv4_dslite.flow_lbl[2] = ip6h->flow_lbl[0];
+}
+
 unsigned int do_hnat_mape_w2l_fast(struct sk_buff *skb, const struct net_device *in,
 				   const char *func)
 {
@@ -660,6 +667,74 @@
 	return -1;
 }
 
+unsigned int do_hnat_mape_wan2lan(struct sk_buff *skb, const struct net_device *in,
+				   const char *func)
+{
+	struct ipv6hdr *ip6h = ipv6_hdr(skb);
+	struct iphdr _iphdr;
+	struct iphdr *iph;
+	struct foe_entry *entry;
+	struct tcpudphdr _ports;
+	const struct tcpudphdr *pptr;
+	int udp = 0;
+
+	/* WAN -> LAN/WLAN MapE learn info(include innner IPv4 header info). */
+	if (ip6h->nexthdr == NEXTHDR_IPIP) {
+		entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
+
+		entry->ipv4_dslite.tunnel_sipv6_0 =
+			ntohl(ip6h->saddr.s6_addr32[0]);
+		entry->ipv4_dslite.tunnel_sipv6_1 =
+			ntohl(ip6h->saddr.s6_addr32[1]);
+		entry->ipv4_dslite.tunnel_sipv6_2 =
+			ntohl(ip6h->saddr.s6_addr32[2]);
+		entry->ipv4_dslite.tunnel_sipv6_3 =
+			ntohl(ip6h->saddr.s6_addr32[3]);
+
+		entry->ipv4_dslite.tunnel_dipv6_0 =
+			ntohl(ip6h->daddr.s6_addr32[0]);
+		entry->ipv4_dslite.tunnel_dipv6_1 =
+			ntohl(ip6h->daddr.s6_addr32[1]);
+		entry->ipv4_dslite.tunnel_dipv6_2 =
+			ntohl(ip6h->daddr.s6_addr32[2]);
+		entry->ipv4_dslite.tunnel_dipv6_3 =
+			ntohl(ip6h->daddr.s6_addr32[3]);
+
+		ppe_fill_flow_lbl(entry, ip6h);
+
+		iph = skb_header_pointer(skb, IPV6_HDR_LEN,
+					 sizeof(_iphdr), &_iphdr);
+		if (unlikely(!iph))
+			return NF_ACCEPT;
+
+		switch (iph->protocol) {
+		case IPPROTO_UDP:
+			udp = 1;
+		case IPPROTO_TCP:
+		break;
+
+		default:
+			return NF_ACCEPT;
+		}
+
+		pptr = skb_header_pointer(skb, IPV6_HDR_LEN + iph->ihl * 4,
+					  sizeof(_ports), &_ports);
+		if (unlikely(!pptr))
+			return NF_ACCEPT;
+
+		entry->bfib1.udp = udp;
+
+		entry->ipv4_dslite.new_sip = ntohl(iph->saddr);
+		entry->ipv4_dslite.new_dip = ntohl(iph->daddr);
+		entry->ipv4_dslite.new_sport = ntohs(pptr->src);
+		entry->ipv4_dslite.new_dport = ntohs(pptr->dst);
+
+		return 0;
+	}
+	return -1;
+}
+
+
 static unsigned int is_ppe_support_type(struct sk_buff *skb)
 {
 	struct ethhdr *eth = NULL;
@@ -744,10 +819,15 @@
 
 	/* MapE need remove ipv6 header and pingpong. */
 	if (do_mape_w2l_fast(state->in, skb)) {
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+		if (mape_toggle && do_hnat_mape_wan2lan(skb, state->in, __func__))
+			return NF_ACCEPT;
+#else
 		if (!do_hnat_mape_w2l_fast(skb, state->in, __func__))
 			return NF_STOLEN;
 		else
 			return NF_ACCEPT;
+#endif
 	}
 
 	if (is_from_mape(skb))
@@ -1079,13 +1159,6 @@
 	return entry;
 }
 
-static void ppe_fill_flow_lbl(struct foe_entry *entry, struct ipv6hdr *ip6h)
-{
-	entry->ipv4_dslite.flow_lbl[0] = ip6h->flow_lbl[2];
-	entry->ipv4_dslite.flow_lbl[1] = ip6h->flow_lbl[1];
-	entry->ipv4_dslite.flow_lbl[2] = ip6h->flow_lbl[0];
-}
-
 static unsigned int skb_to_hnat_info(struct sk_buff *skb,
 				     const struct net_device *dev,
 				     struct foe_entry *foe,
@@ -1341,6 +1414,16 @@
 				entry.ipv4_dslite.vlan1 = hw_path->vlan_id;
 				if (hnat_priv->data->per_flow_accounting)
 					entry.ipv4_dslite.iblk2.mibf = 1;
+				/* Map-E LAN->WAN record inner IPv4 header info. */
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+				if (mape_toggle) {
+					entry.ipv4_dslite.iblk2.dscp = foe->ipv4_dslite.iblk2.dscp;
+					entry.ipv4_dslite.new_sip = foe->ipv4_dslite.new_sip;
+					entry.ipv4_dslite.new_dip = foe->ipv4_dslite.new_dip;
+					entry.ipv4_dslite.new_sport = foe->ipv4_dslite.new_sport;
+					entry.ipv4_dslite.new_dport = foe->ipv4_dslite.new_dport;
+				}
+#endif
 			} else if (mape_toggle &&
 				   entry.bfib1.pkt_type == IPV4_HNAPT) {
 				/* MapE LAN -> WAN */
@@ -2025,6 +2108,7 @@
 
 				entry->bfib1.udp = udp;
 
+				/* Map-E LAN->WAN record inner IPv4 header info. */
 #if defined(CONFIG_MEDIATEK_NETSYS_V2)
 				entry->bfib1.pkt_type = IPV4_MAP_E;
 				entry->ipv4_dslite.iblk2.dscp = iph->tos;
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
index 73de3e9..75c3a75 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
@@ -40,7 +40,8 @@
 
 	switch (eth_proto) {
 	case ETH_P_IP:
-		if (entry->ipv4_hnapt.bfib1.pkt_type == IPV4_DSLITE)
+		if (entry->ipv4_hnapt.bfib1.pkt_type == IPV4_DSLITE
+			|| (entry->ipv4_hnapt.bfib1.pkt_type == IPV4_MAP_E))
 			entry->ipv4_dslite.etype = sp_tag;
 		else
 			entry->ipv4_hnapt.etype = sp_tag;