[][MAC80211][hnat][Add WiFi Roaming feature]

[Description]
Add WiFi Roaming feature.

Without this patch, the traffic may stop over 30 seconds when the
WiFi roams.

[Release-log]
N/A


Change-Id: I22d8b003b538d5cb4bfd063d864da409875827e2
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/8915195
diff --git a/21.02/files/target/linux/mediatek/mt7981/config-5.4 b/21.02/files/target/linux/mediatek/mt7981/config-5.4
index 045b2d0..6ca512d 100644
--- a/21.02/files/target/linux/mediatek/mt7981/config-5.4
+++ b/21.02/files/target/linux/mediatek/mt7981/config-5.4
@@ -308,6 +308,7 @@
 CONFIG_NEED_DMA_MAP_STATE=y
 CONFIG_NEED_SG_DMA_LENGTH=y
 CONFIG_NETFILTER=y
+CONFIG_NETFILTER_INGRESS=y
 CONFIG_NETFILTER_XTABLES=m
 CONFIG_NETFILTER_XT_NAT=m
 CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m
@@ -321,6 +322,8 @@
 CONFIG_NET_MEDIATEK_SOC=y
 CONFIG_NET_SWITCHDEV=y
 CONFIG_NET_VENDOR_MEDIATEK=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_FLOW_TABLE=y
 CONFIG_NLS=y
 CONFIG_NMBM=y
 # CONFIG_NMBM_LOG_LEVEL_DEBUG is not set
diff --git a/21.02/files/target/linux/mediatek/mt7986/config-5.4 b/21.02/files/target/linux/mediatek/mt7986/config-5.4
index 880e675..fa956d1 100644
--- a/21.02/files/target/linux/mediatek/mt7986/config-5.4
+++ b/21.02/files/target/linux/mediatek/mt7986/config-5.4
@@ -351,6 +351,7 @@
 CONFIG_NEED_DMA_MAP_STATE=y
 CONFIG_NEED_SG_DMA_LENGTH=y
 CONFIG_NETFILTER=y
+CONFIG_NETFILTER_INGRESS=y
 CONFIG_NETFILTER_XTABLES=m
 CONFIG_NETFILTER_XT_NAT=m
 CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m
@@ -367,6 +368,7 @@
 CONFIG_NET_VENDOR_MEDIATEK=y
 CONFIG_NF_CONNTRACK=y
 CONFIG_NF_DEFRAG_IPV4=y
+CONFIG_NF_FLOW_TABLE=y
 CONFIG_NF_NAT=m
 CONFIG_NF_NAT_MASQUERADE=y
 CONFIG_NLS=y
diff --git a/21.02/files/target/linux/mediatek/mt7988/config-5.4 b/21.02/files/target/linux/mediatek/mt7988/config-5.4
index 747c9d2..dcd85c1 100644
--- a/21.02/files/target/linux/mediatek/mt7988/config-5.4
+++ b/21.02/files/target/linux/mediatek/mt7988/config-5.4
@@ -328,6 +328,11 @@
 CONFIG_MUTEX_SPIN_ON_OWNER=y
 CONFIG_NEED_DMA_MAP_STATE=y
 CONFIG_NEED_SG_DMA_LENGTH=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_INGRESS=y
+CONFIG_NETFILTER_XTABLES=m
+CONFIG_NETFILTER_XT_NAT=m
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m
 CONFIG_NET_DEVLINK=y
 CONFIG_NET_DSA=y
 # CONFIG_NET_DSA_AN8855 is not set
@@ -338,6 +343,10 @@
 CONFIG_NET_MEDIATEK_SOC=y
 CONFIG_NET_SWITCHDEV=y
 CONFIG_NET_VENDOR_MEDIATEK=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_FLOW_TABLE=y
+CONFIG_NF_NAT=m
+CONFIG_NF_NAT_MASQUERADE=y
 CONFIG_NLS=y
 CONFIG_NMBM=y
 # CONFIG_NMBM_LOG_LEVEL_DEBUG is not set
diff --git a/autobuild_mac80211_release/target/linux/mediatek/patches-5.4/999-3025-flow-offload-add-mtkhnat-roaming.patch b/autobuild_mac80211_release/target/linux/mediatek/patches-5.4/999-3025-flow-offload-add-mtkhnat-roaming.patch
new file mode 100644
index 0000000..c4056d8
--- /dev/null
+++ b/autobuild_mac80211_release/target/linux/mediatek/patches-5.4/999-3025-flow-offload-add-mtkhnat-roaming.patch
@@ -0,0 +1,259 @@
+From c48cf95d7a02fe00bcc5b761901a587653eda7ac Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Tue, 9 Apr 2024 18:06:36 +0800
+Subject: [PATCH] 999-3025-flow-offload-add-mtkhnat-roaming
+
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |   8 +
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h |   3 +
+ drivers/net/ethernet/mediatek/mtk_ppe.c     | 169 ++++++++++++++++++++
+ drivers/net/ethernet/mediatek/mtk_ppe.h     |   2 +
+ 4 files changed, 182 insertions(+)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 755a5c9..a508fa9 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4042,6 +4042,12 @@ static int mtk_open(struct net_device *dev)
+ 		if (err)
+ 			return err;
+ 
++		if (eth->soc->offload_version) {
++			err = mtk_ppe_roaming_start(eth);
++			if (err)
++				netdev_err(dev, "%s: could not start ppe roaming work: %d\n",
++					   __func__, err);
++		}
+ 
+ 		/* Indicates CDM to parse the MTK special tag from CPU */
+ 		if (netdev_uses_dsa(dev)) {
+@@ -4220,6 +4226,8 @@ static int mtk_stop(struct net_device *dev)
+ 	if (eth->soc->offload_version) {
+ 		for (i = 0; i < eth->ppe_num; i++)
+ 			mtk_ppe_stop(eth->ppe[i]);
++
++		mtk_ppe_roaming_stop(eth);
+ 	}
+ 
+ 	return 0;
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index d958aec..811ee58 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1963,6 +1963,9 @@ struct mtk_eth {
+ 	u8				ppe_num;
+ 	struct mtk_ppe			*ppe[MTK_MAX_PPE_NUM];
+ 	struct rhashtable		flow_table;
++	struct socket			*ppe_roam_sock;
++	struct work_struct		ppe_roam_work;
++	unsigned char			ppe_roam_buf[1024];
+ };
+ 
+ /* struct mtk_mac -	the structure that holds the info about the MACs of the
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
+index c054df0..bc30a1a 100644
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -11,6 +11,7 @@
+ #include <linux/ip.h>
+ #include <net/dsa.h>
+ #include <net/route.h>
++#include <net/netfilter/nf_flow_table.h>
+ #include "mtk_eth_soc.h"
+ #include "mtk_ppe.h"
+ #include "mtk_ppe_regs.h"
+@@ -582,6 +583,28 @@ bool mtk_foe_entry_match(struct mtk_foe_entry *entry, struct mtk_foe_entry *data
+ 	return !memcmp(&entry->data, &data->data, len - 4);
+ }
+ 
++static bool mtk_foe_mac_match(struct mtk_foe_entry *entry, u8 *mac)
++{
++	int type;
++
++	type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++	if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++		if(((swab32(entry->ipv6.l2.dest_mac_hi) == *(u32 *)mac) &&
++		    (swab16(entry->ipv6.l2.dest_mac_lo) == *(u16 *)&mac[4])) ||
++		   ((swab32(entry->ipv6.l2.src_mac_hi) == *(u32 *)mac) &&
++		    (swab16(entry->ipv6.l2.src_mac_lo) == *(u16 *)&mac[4])))
++			return true;
++	} else {
++		if(((swab32(entry->ipv4.l2.dest_mac_hi) == *(u32 *)mac) &&
++		    (swab16(entry->ipv4.l2.dest_mac_lo) == *(u16 *)&mac[4])) ||
++		   ((swab32(entry->ipv4.l2.src_mac_hi) == *(u32 *)mac) &&
++		    (swab16(entry->ipv4.l2.src_mac_lo) == *(u16 *)&mac[4])))
++			return true;
++	}
++
++	return false;
++}
++
+ int mtk_foe_entry_set_sp(struct mtk_ppe *ppe, struct mtk_foe_entry *entry)
+ {
+ 	struct mtk_foe_entry *hwe;
+@@ -1184,3 +1207,149 @@ int mtk_ppe_stop(struct mtk_ppe *ppe)
+ 
+ 	return 0;
+ }
++
++int mtk_flow_offload_teardown_by_mac(struct mtk_ppe *ppe, u8 *mac)
++{
++	int i, j, count = 0;
++
++	for (i = 0; i < MTK_PPE_ENTRIES; i++) {
++		struct mtk_foe_entry *entry = &ppe->foe_table[i];
++		struct flow_offload_tuple tuple;
++		int type, state;
++
++		state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
++		if (state != MTK_FOE_STATE_BIND || !mtk_foe_mac_match(entry, mac))
++			continue;
++
++		memset(&tuple, 0, sizeof(tuple));
++
++		if (entry->ib1 & MTK_FOE_IB1_UDP)
++			tuple.l4proto = IPPROTO_UDP;
++		else
++			tuple.l4proto = IPPROTO_TCP;
++
++		type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++		if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++			tuple.l3proto = NFPROTO_IPV6;
++			tuple.src_port = htons(entry->ipv6.src_port);
++			tuple.dst_port = htons(entry->ipv6.src_port);
++			for (j = 0; j < 4; j ++) {
++				tuple.src_v6.s6_addr32[j] = htonl(entry->ipv6.src_ip[j]);
++				tuple.dst_v6.s6_addr32[j] = htonl(entry->ipv6.dest_ip[j]);
++			}
++		} else {
++			tuple.l3proto = NFPROTO_IPV4;
++			tuple.src_port = htons(entry->ipv4.orig.src_port);
++			tuple.dst_port = htons(entry->ipv4.orig.dest_port);
++			tuple.src_v4.s_addr = htonl(entry->ipv4.orig.src_ip);
++			tuple.dst_v4.s_addr = htonl(entry->ipv4.orig.dest_ip);
++		}
++
++		flow_offload_teardown_by_tuple(&tuple);
++		count++;
++
++		pr_info("mtk_ppe: the roamiing entry (%x) has been deleted\n", i);
++	}
++
++	if (!count)
++		pr_warn("mtk_ppe: the roaming entry is not found\n");
++
++	return count;
++}
++
++static void mtk_ppe_roam_handler(struct work_struct *work)
++{
++	struct mtk_eth *eth = container_of(work, struct mtk_eth, ppe_roam_work);
++	struct kvec iov;
++	struct msghdr msg;
++	struct nlmsghdr *nlh;
++	struct ndmsg *ndm;
++	struct nlattr *nla;
++	int len, ifindex, i;
++	u8 mac[ETH_ALEN];
++
++	if (!eth->ppe_roam_sock)
++		return;
++
++	iov.iov_base = eth->ppe_roam_buf;
++	iov.iov_len = sizeof(eth->ppe_roam_buf);
++	memset(&msg, 0, sizeof(msg));
++	msg.msg_namelen = sizeof(struct sockaddr_nl);
++
++	len = kernel_recvmsg(eth->ppe_roam_sock, &msg, &iov, 1, iov.iov_len, 0);
++	if (len <= 0)
++		goto out;
++
++	nlh = (struct nlmsghdr*)eth->ppe_roam_buf;
++	if (!NLMSG_OK(nlh, len) || nlh->nlmsg_type != RTM_NEWNEIGH)
++		goto out;
++
++	len = nlh->nlmsg_len - NLMSG_HDRLEN;
++	ndm = (struct ndmsg *)NLMSG_DATA(nlh);
++	if (ndm->ndm_family != PF_BRIDGE)
++		goto out;
++
++	ifindex = ndm->ndm_ifindex;
++	nla = (struct nlattr *)((unsigned char *)ndm + sizeof(struct ndmsg));
++	len -= NLMSG_LENGTH(sizeof(struct ndmsg));
++	while (nla_ok(nla, len)) {
++		if (nla_type(nla) == NDA_LLADDR) {
++			memcpy(mac, nla_data(nla), ETH_ALEN);
++			for (i = 0; i < eth->ppe_num; i++)
++				mtk_flow_offload_teardown_by_mac(eth->ppe[i], mac);
++			pr_info("mtk_ppe: the neighbor (%pM) has been updated\n", mac);
++		}
++		nla = nla_next(nla, &len);
++	}
++
++out:
++	schedule_work(&eth->ppe_roam_work);
++}
++
++int mtk_ppe_roaming_start(struct mtk_eth *eth)
++{
++	struct socket *sock = NULL;
++	struct sockaddr_nl addr;
++	int ret;
++
++	INIT_WORK(&eth->ppe_roam_work, mtk_ppe_roam_handler);
++
++	ret = sock_create_kern(&init_net, AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, &sock);
++	if (ret < 0)
++		goto out;
++
++	eth->ppe_roam_sock = sock;
++
++	addr.nl_family = AF_NETLINK;
++	addr.nl_pad = 0;
++	addr.nl_pid = 65534;
++	addr.nl_groups = 1 << (RTNLGRP_NEIGH - 1);
++	ret = kernel_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
++	if (ret < 0)
++		goto out;
++
++	schedule_work(&eth->ppe_roam_work);
++
++	pr_info("mtk_ppe: roaming work has been activated\n");
++
++	return 0;
++
++out:
++	if (sock)
++		sock_release(sock);
++
++	return ret;
++}
++
++int mtk_ppe_roaming_stop(struct mtk_eth *eth)
++{
++	if (!eth->ppe_roam_sock)
++		return -ENOENT;
++
++	sock_release(eth->ppe_roam_sock);
++	eth->ppe_roam_sock = NULL;
++
++	pr_info("mtk_ppe: roaming work has been deactivated\n");
++
++	return 0;
++}
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
+index bafbffc..68295b0 100644
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -396,6 +396,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int versio
+ 			     int accounting);
+ int mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
++int mtk_ppe_roaming_start(struct mtk_eth *eth);
++int mtk_ppe_roaming_stop(struct mtk_eth *eth);
+ 
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
+ 
+-- 
+2.18.0
+