[][[Kernel][common][hnat][Add delete entry by IP command]

[Description]
Add delete entry by IP
Delete downlink and uplink HNAT entry according to the STA IP.

[Release-log]
NA


Change-Id: I4894e594a74cb1defbeeb61f09c8917b10d36222
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/9404592
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
index 24f7e7d..892bd61 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
@@ -44,6 +44,9 @@
 int (*ppe_del_entry_by_mac)(unsigned char *mac) = NULL;
 EXPORT_SYMBOL(ppe_del_entry_by_mac);
 
+int (*ppe_del_entry_by_ip)(bool is_ipv4, void *addr) = NULL;
+EXPORT_SYMBOL(ppe_del_entry_by_ip);
+
 void (*ppe_dev_register_hook)(struct net_device *dev) = NULL;
 EXPORT_SYMBOL(ppe_dev_register_hook);
 void (*ppe_dev_unregister_hook)(struct net_device *dev) = NULL;
@@ -260,6 +263,79 @@
 	return ret;
 }
 
+static int entry_ip_cmp(struct foe_entry *entry, bool is_ipv4, void *addr)
+{
+	u32 *tmp_ipv4, *sipv6_0, *dipv6_0, ipv4;
+	struct in6_addr *tmp_ipv6, ipv6;
+	struct in6_addr foe_sipv6, foe_dipv6;
+	int ret = 0;
+
+	if (is_ipv4) {
+		tmp_ipv4 = (u32 *)addr;
+		ipv4 = ntohl(*tmp_ipv4);
+
+		switch ((int)entry->bfib1.pkt_type) {
+		case IPV4_HNAPT:
+		case IPV4_HNAT:
+			if (entry->ipv4_hnapt.sip == ipv4 ||
+			    entry->ipv4_hnapt.new_dip == ipv4)
+				ret = 1;
+			break;
+		case IPV4_DSLITE:
+		case IPV4_MAP_E:
+			if (entry->ipv4_dslite.sip == ipv4 ||
+			    entry->ipv4_dslite.dip == ipv4)
+				ret = 1;
+			break;
+		default:
+			break;
+		}
+	} else {
+		memset(&foe_sipv6, 0, sizeof(struct in6_addr));
+		memset(&foe_dipv6, 0, sizeof(struct in6_addr));
+		memset(&ipv6, 0, sizeof(struct in6_addr));
+
+		tmp_ipv6 = (struct in6_addr *)addr;
+		ipv6.s6_addr32[0] = ntohl(tmp_ipv6->s6_addr32[0]);
+		ipv6.s6_addr32[1] = ntohl(tmp_ipv6->s6_addr32[1]);
+		ipv6.s6_addr32[2] = ntohl(tmp_ipv6->s6_addr32[2]);
+		ipv6.s6_addr32[3] = ntohl(tmp_ipv6->s6_addr32[3]);
+
+		switch ((int)entry->bfib1.pkt_type) {
+		case IPV6_3T_ROUTE:
+		case IPV6_5T_ROUTE:
+		case IPV6_6RD:
+			sipv6_0 = &(entry->ipv6_3t_route.ipv6_sip0);
+			dipv6_0 = &(entry->ipv6_3t_route.ipv6_dip0);
+			break;
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+		case IPV6_HNAT:
+		case IPV6_HNAPT:
+			sipv6_0 = &(entry->ipv6_hnapt.ipv6_sip0);
+			dipv6_0 = &(entry->ipv6_hnapt.new_ipv6_ip0);
+			break;
+#endif
+		default:
+			break;
+		}
+
+		memcpy(&foe_sipv6, sipv6_0, sizeof(struct in6_addr));
+		memcpy(&foe_dipv6, dipv6_0, sizeof(struct in6_addr));
+		if (!memcmp(&foe_sipv6, &ipv6, sizeof(struct in6_addr)) ||
+		    !memcmp(&foe_dipv6, &ipv6, sizeof(struct in6_addr)))
+			ret = 1;
+	}
+
+	if (ret && debug_level >= 2) {
+		if (is_ipv4)
+			pr_info("ipv4=%pI4\n", tmp_ipv4);
+		else
+			pr_info("ipv6=%pI6\n", tmp_ipv6);
+	}
+
+	return ret;
+}
+
 int entry_delete_by_mac(u8 *mac)
 {
 	struct foe_entry *entry = NULL;
@@ -284,6 +360,30 @@
 	return ret;
 }
 
+int entry_delete_by_ip(bool is_ipv4, void *addr)
+{
+	struct foe_entry *entry = NULL;
+	int index, i, ret = 0;
+
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		entry = hnat_priv->foe_table_cpu[i];
+		for (index = 0; index < DEF_ETRY_NUM; entry++, index++) {
+			if (entry->bfib1.state == BIND && entry_ip_cmp(entry, is_ipv4, addr)) {
+				memset(entry, 0, sizeof(*entry));
+				hnat_cache_ebl(1);
+				if (debug_level >= 2)
+					pr_info("delete entry idx = %d\n", index);
+				ret++;
+			}
+		}
+	}
+
+	if (!ret && debug_level >= 2)
+		pr_info("entry not found\n");
+
+	return ret;
+}
+
 static void hnat_roam_handler(struct work_struct *work)
 {
 	struct kvec iov;
@@ -687,6 +787,7 @@
 		return -1;
 
 	ppe_del_entry_by_mac = entry_delete_by_mac;
+	ppe_del_entry_by_ip = entry_delete_by_ip;
 	hook_toggle = 1;
 
 	return 0;
@@ -724,6 +825,7 @@
 
 	mod_timer(&hnat_priv->hnat_sma_build_entry_timer, jiffies + 3 * HZ);
 	ppe_del_entry_by_mac = NULL;
+	ppe_del_entry_by_ip = NULL;
 	hook_toggle = 0;
 
 	return 0;
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
index f7ca61c..6413de6 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
@@ -1350,6 +1350,7 @@
 void set_gmac_ppe_fwd(int gmac_no, int enable);
 int entry_detail(u32 ppe_id, int index);
 int entry_delete_by_mac(u8 *mac);
+int entry_delete_by_ip(bool is_ipv4, void *addr);
 int entry_delete(u32 ppe_id, int index);
 int hnat_warm_init(void);
 u32 hnat_get_ppe_hash(struct foe_entry *entry);
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index a5da044..53f4d10 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -325,6 +325,8 @@
 	pr_info("              7   <entry_idx>  Delete PPE2 specific foe entry of assigned <entry_idx>\n");
 	pr_info("                               When entry_idx is -1, clear all entries\n");
 	pr_info("              8   <mac>        Delete all PPEs foe entry of assinged smac and dmac\n");
+	pr_info("              9   <ip>         Delete all PPEs foe entry of assinged IP(DL and UL)\n");
+	pr_info("              10  <ipV6>       Delete all PPEs foe entry of assinged IPv6(DL and UL)\n");
 
 	return 0;
 }
@@ -779,6 +781,31 @@
 	return 0;
 }
 
+int delete_entry_by_ip(bool is_ipv4, char *str)
+{
+	struct in6_addr ipv6;
+	void *addr = NULL;
+	u32 ipv4;
+
+	if (is_ipv4) {
+		if (!in4_pton(str, -1, (u8 *)&ipv4, -1, NULL)) {
+			pr_info("Invalid IPv4 address.\n");
+			return -EINVAL;
+		}
+		addr = (void *)(&ipv4);
+	} else {
+		if (!in6_pton(str, -1, (u8 *)&ipv6, -1, NULL)) {
+			pr_info("Invalid IPv6 address.\n");
+			return -EINVAL;
+		}
+		addr = (void *)(&ipv6);
+	}
+
+	entry_delete_by_ip(is_ipv4, addr);
+
+	return 0;
+}
+
 int cr_set_usage(int level)
 {
 	debug_level = level;
@@ -1929,6 +1956,7 @@
 	char *p_token = NULL;
 	char *p_delimiter = " \t";
 	int ret;
+	bool is_ipv4 = true;
 
 	if (len >= sizeof(buf)) {
 		pr_info("input handling fail!\n");
@@ -1966,12 +1994,21 @@
 		break;
 	case 8:
 		p_token = strsep(&p_buf, p_delimiter);
-
 		if (!p_token)
 			break;
 
 		delete_entry_by_mac(p_token);
 		break;
+	case 9:
+	case 10:
+		p_token = strsep(&p_buf, p_delimiter);
+		if (!p_token)
+			break;
+
+		if (arg0 == 10)
+			is_ipv4 = false;
+		delete_entry_by_ip(is_ipv4, p_token);
+		break;
 	default:
 		pr_info("no handler defined for command id(0x%08lx)\n\r", arg0);
 		entry_set_usage(debug_level);