From 74db64645e1d55b9708dfc80811e2f81a05ba4c7 Mon Sep 17 00:00:00 2001 From: Ycarus Date: Mon, 24 Sep 2018 12:08:26 +0200 Subject: [PATCH] Add mt7623 HNAT support --- .../0237-mt7623-add-HNAT-support.patch | 1824 +++++++++++++++++ 1 file changed, 1824 insertions(+) create mode 100644 root/target/linux/mediatek/patches-4.14/0237-mt7623-add-HNAT-support.patch diff --git a/root/target/linux/mediatek/patches-4.14/0237-mt7623-add-HNAT-support.patch b/root/target/linux/mediatek/patches-4.14/0237-mt7623-add-HNAT-support.patch new file mode 100644 index 00000000..5f71df06 --- /dev/null +++ b/root/target/linux/mediatek/patches-4.14/0237-mt7623-add-HNAT-support.patch @@ -0,0 +1,1824 @@ +From b3ba09aea285ecdac5228805411f1e7a6c68382a Mon Sep 17 00:00:00 2001 +From: Frank Wunderlich +Date: Sun, 8 Jul 2018 17:27:58 +0200 +Subject: [PATCH] [HNAT] applied full Patch from hnat-branch + +--- + arch/arm/boot/dts/mt7623.dtsi | 9 + + arch/arm/configs/mt7623n_evb_fwu_defconfig | 4 +- + drivers/net/ethernet/mediatek/Kconfig | 7 + + drivers/net/ethernet/mediatek/Makefile | 1 + + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 22 + + .../net/ethernet/mediatek/mtk_hnat/Makefile | 4 + + drivers/net/ethernet/mediatek/mtk_hnat/hnat.c | 329 ++++++++++++ + drivers/net/ethernet/mediatek/mtk_hnat/hnat.h | 427 +++++++++++++++ + .../ethernet/mediatek/mtk_hnat/hnat_debugfs.c | 489 ++++++++++++++++++ + .../ethernet/mediatek/mtk_hnat/hnat_nf_hook.c | 312 +++++++++++ + .../ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h | 44 ++ + net/netfilter/nf_conntrack_proto_tcp.c | 19 + + 12 files changed, 1666 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/ethernet/mediatek/mtk_hnat/Makefile + create mode 100644 drivers/net/ethernet/mediatek/mtk_hnat/hnat.c + create mode 100644 drivers/net/ethernet/mediatek/mtk_hnat/hnat.h + create mode 100644 drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c + create mode 100644 drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c + create mode 100644 drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h + +diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi +index 44831a813a48f..1633d19564ca8 100644 +--- a/arch/arm/boot/dts/mt7623.dtsi ++++ b/arch/arm/boot/dts/mt7623.dtsi +@@ -1207,6 +1207,15 @@ + status = "disabled"; + }; + ++ hnat: hnat@1b000000 { ++ compatible = "mediatek,mt7623-hnat"; ++ reg = <0 0x1b100000 0 0x3000>; ++ mtketh-wan = "wan"; ++ resets = <ðsys 0>; ++ reset-names = "mtketh"; ++ }; ++ ++ + crypto: crypto@1b240000 { + compatible = "mediatek,mt7623-crypto"; + reg = <0 0x1b240000 0 0x20000>; +diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig +index f9149d2a46940..314c6fe670f81 100644 +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -14,4 +14,11 @@ config NET_MEDIATEK_SOC + This driver supports the gigabit ethernet MACs in the + MediaTek SoC family. + ++config NET_MEDIATEK_HNAT ++ tristate "MediaTek MT7623 hardware NAT support" ++ depends on NET_MEDIATEK_SOC && NF_CONNTRACK && NF_CONNTRACK_IPV4 && IP_NF_NAT && IP_NF_TARGET_MASQUERADE ++ ---help--- ++ This driver supports the hardware NAT in the ++ MediaTek MT2701/MT7623 chipset family. ++ + endif #NET_VENDOR_MEDIATEK +diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile +index aa3f1c8ccd4ab..355c928f4700e 100644 +--- a/drivers/net/ethernet/mediatek/Makefile ++++ b/drivers/net/ethernet/mediatek/Makefile +@@ -3,3 +3,4 @@ + # + + obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o ++obj-$(CONFIG_NET_MEDIATEK_HNAT) += mtk_hnat/ +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index e6b6596e6a4a9..de4969cc672c4 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -24,6 +24,10 @@ + #include + #include + ++#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) ++#include "mtk_hnat/nf_hnat_mtk.h" ++#endif ++ + #include "mtk_eth_soc.h" + + static int mtk_msg_level = -1; +@@ -699,6 +703,11 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, + return -ENOMEM; + + /* set the forward port */ ++#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) ++ if (HNAT_SKB_CB2(skb)->magic == 0x78681415) ++ fport |= 0x4 << TX_DMA_FPORT_SHIFT; ++ else ++#endif + fport = (mac->id + 1) << TX_DMA_FPORT_SHIFT; + txd4 |= fport; + +@@ -1063,6 +1072,10 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, + RX_DMA_VID(trxd.rxd3)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + RX_DMA_VID(trxd.rxd3)); ++#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) ++ *(u32 *)(skb->head) = trxd.rxd4; ++ skb_hnat_alg(skb) = 0; ++#endif + skb_record_rx_queue(skb, 0); + napi_gro_receive(napi, skb); + +@@ -2000,6 +2013,12 @@ static int mtk_hw_init(struct mtk_eth *eth) + val = mtk_r32(eth, MTK_CDMP_IG_CTRL); + mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL); + ++ /* Indicates CDM to parse the MTK special tag from CPU ++ * which also is working out for untag packets. ++ */ ++ val = mtk_r32(eth, MTK_CDMQ_IG_CTRL); ++ mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL); ++ + /* Enable RX VLan Offloading */ + if (MTK_HW_FEATURES & NETIF_F_HW_VLAN_CTAG_RX) + mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); +@@ -2033,6 +2052,9 @@ static int mtk_hw_init(struct mtk_eth *eth) + /* Enable RX checksum */ + val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN; + ++ /* Enable special tag */ ++ val |= BIT(24); ++ + /* setup the mac dma */ + mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); + } +diff --git a/drivers/net/ethernet/mediatek/mtk_hnat/Makefile b/drivers/net/ethernet/mediatek/mtk_hnat/Makefile +new file mode 100644 +index 0000000000000..e052abcd43020 +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_hnat/Makefile +@@ -0,0 +1,4 @@ ++ccflags-y=-Werror -Wno-missing-braces ++ ++obj-$(CONFIG_NET_MEDIATEK_HNAT) += mtkhnat.o ++mtkhnat-objs := hnat.o hnat_nf_hook.o hnat_debugfs.o +diff --git a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c +new file mode 100644 +index 0000000000000..22a822737934f +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c +@@ -0,0 +1,329 @@ ++/* This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Copyright (C) 2014-2016 Sean Wang ++ * Copyright (C) 2016-2017 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hnat.h" ++ ++struct hnat_priv *host; ++ ++static void cr_set_bits(void __iomem * reg, u32 bs) ++{ ++ u32 val = readl(reg); ++ ++ val |= bs; ++ writel(val, reg); ++} ++ ++static void cr_clr_bits(void __iomem * reg, u32 bs) ++{ ++ u32 val = readl(reg); ++ ++ val &= ~bs; ++ writel(val, reg); ++} ++ ++static void cr_set_field(void __iomem * reg, u32 field, u32 val) ++{ ++ unsigned int tv = readl(reg); ++ ++ tv &= ~field; ++ tv |= ((val) << (ffs((unsigned int)field) - 1)); ++ writel(tv, reg); ++} ++ ++static int hnat_start(void) ++{ ++ u32 foe_table_sz; ++ ++ /* mapp the FOE table */ ++ foe_table_sz = FOE_4TB_SIZ * sizeof(struct foe_entry); ++ host->foe_table_cpu = ++ dma_alloc_coherent(host->dev, foe_table_sz, &host->foe_table_dev, ++ GFP_KERNEL); ++ if (!host->foe_table_cpu) ++ return -1; ++ ++ writel(host->foe_table_dev, host->ppe_base + PPE_TB_BASE); ++ memset(host->foe_table_cpu, 0, foe_table_sz); ++ ++ /* setup hashing */ ++ cr_set_field(host->ppe_base + PPE_TB_CFG, TB_ETRY_NUM, TABLE_4K); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, HASH_MODE, HASH_MODE_1); ++ writel(HASH_SEED_KEY, host->ppe_base + PPE_HASH_SEED); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, XMODE, 0); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, TB_ENTRY_SIZE, ENTRY_64B); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, SMA, SMA_FWD_CPU_BUILD_ENTRY); ++ ++ /* set ip proto */ ++ writel(0xFFFFFFFF, host->ppe_base + PPE_IP_PROT_CHK); ++ ++ /* setup caching */ ++ cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 1); ++ cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 0); ++ cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_EN, 1); ++ ++ /* enable FOE */ ++ cr_set_bits(host->ppe_base + PPE_FLOW_CFG, ++ BIT_IPV4_NAT_EN | BIT_IPV4_NAPT_EN | ++ BIT_IPV4_NAT_FRAG_EN | BIT_IPV4_HASH_GREK); ++ ++ /* setup FOE aging */ ++ cr_set_field(host->ppe_base + PPE_TB_CFG, NTU_AGE, 1); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, UNBD_AGE, 1); ++ cr_set_field(host->ppe_base + PPE_UNB_AGE, UNB_MNP, 1000); ++ cr_set_field(host->ppe_base + PPE_UNB_AGE, UNB_DLTA, 3); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, TCP_AGE, 1); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, UDP_AGE, 1); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, FIN_AGE, 1); ++ cr_set_field(host->ppe_base + PPE_BND_AGE_0, UDP_DLTA, 5); ++ cr_set_field(host->ppe_base + PPE_BND_AGE_0, NTU_DLTA, 5); ++ cr_set_field(host->ppe_base + PPE_BND_AGE_1, FIN_DLTA, 5); ++ cr_set_field(host->ppe_base + PPE_BND_AGE_1, TCP_DLTA, 5); ++ ++ /* setup FOE ka */ ++ cr_set_field(host->ppe_base + PPE_TB_CFG, KA_CFG, 3); ++ cr_set_field(host->ppe_base + PPE_KA, KA_T, 1); ++ cr_set_field(host->ppe_base + PPE_KA, TCP_KA, 1); ++ cr_set_field(host->ppe_base + PPE_KA, UDP_KA, 1); ++ cr_set_field(host->ppe_base + PPE_BIND_LMT_1, NTU_KA, 1); ++ ++ /* setup FOE rate limit */ ++ cr_set_field(host->ppe_base + PPE_BIND_LMT_0, QURT_LMT, 16383); ++ cr_set_field(host->ppe_base + PPE_BIND_LMT_0, HALF_LMT, 16383); ++ cr_set_field(host->ppe_base + PPE_BIND_LMT_1, FULL_LMT, 16383); ++ cr_set_field(host->ppe_base + PPE_BNDR, BIND_RATE, 1); ++ ++ /* setup FOE cf gen */ ++ cr_set_field(host->ppe_base + PPE_GLO_CFG, PPE_EN, 1); ++ writel(0, host->ppe_base + PPE_DFT_CPORT); // pdma ++ //writel(0x55555555, host->ppe_base + PPE_DFT_CPORT); //qdma ++ cr_set_field(host->ppe_base + PPE_GLO_CFG, TTL0_DRP, 1); ++ ++ /* fwd packets from gmac to PPE */ ++ cr_clr_bits(host->fe_base + GDMA1_FWD_CFG, GDM1_ALL_FRC_MASK); ++ cr_set_bits(host->fe_base + GDMA1_FWD_CFG, ++ BITS_GDM1_ALL_FRC_P_PPE); ++ cr_clr_bits(host->fe_base + GDMA2_FWD_CFG, GDM2_ALL_FRC_MASK); ++ cr_set_bits(host->fe_base + GDMA2_FWD_CFG, ++ BITS_GDM2_ALL_FRC_P_PPE); ++ ++ dev_info(host->dev, "hwnat start\n"); ++ ++ return 0; ++} ++ ++static int ppe_busy_wait(void) ++{ ++ unsigned long t_start = jiffies; ++ u32 r = 0; ++ ++ while (1) { ++ r = readl((host->ppe_base + 0x0)); ++ if (!(r & BIT(31))) ++ return 0; ++ if (time_after(jiffies, t_start + HZ)) ++ break; ++ usleep_range(10, 20); ++ } ++ ++ dev_err(host->dev, "ppe:%s timeout\n", __func__); ++ ++ return -1; ++} ++ ++static void hnat_stop(void) ++{ ++ u32 foe_table_sz; ++ struct foe_entry *entry, *end; ++ u32 r1 = 0, r2 = 0; ++ ++ /* discard all traffic while we disable the PPE */ ++ cr_clr_bits(host->fe_base + GDMA1_FWD_CFG, GDM1_ALL_FRC_MASK); ++ cr_set_bits(host->fe_base + GDMA1_FWD_CFG, ++ BITS_GDM1_ALL_FRC_P_DISCARD); ++ cr_clr_bits(host->fe_base + GDMA2_FWD_CFG, GDM2_ALL_FRC_MASK); ++ cr_set_bits(host->fe_base + GDMA2_FWD_CFG, ++ BITS_GDM2_ALL_FRC_P_DISCARD); ++ ++ if (ppe_busy_wait()) { ++#if 0 ++ reset_control_reset(host->rstc); ++#endif ++ msleep(2000); ++ return; ++ } ++ ++ entry = host->foe_table_cpu; ++ end = host->foe_table_cpu + FOE_4TB_SIZ; ++ while (entry < end) { ++ entry->bfib1.state = INVALID; ++ entry++; ++ } ++ ++ /* disable caching */ ++ cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 1); ++ cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 0); ++ cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_EN, 0); ++ ++ /* flush cache has to be ahead of hnat diable --*/ ++ cr_set_field(host->ppe_base + PPE_GLO_CFG, PPE_EN, 0); ++ ++ /* disable FOE */ ++ cr_clr_bits(host->ppe_base + PPE_FLOW_CFG, ++ BIT_IPV4_NAPT_EN | BIT_IPV4_NAT_EN | ++ BIT_IPV4_NAT_FRAG_EN | ++ BIT_FUC_FOE | BIT_FMC_FOE | BIT_FUC_FOE); ++ ++ /* disable FOE aging */ ++ cr_set_field(host->ppe_base + PPE_TB_CFG, NTU_AGE, 0); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, UNBD_AGE, 0); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, TCP_AGE, 0); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, UDP_AGE, 0); ++ cr_set_field(host->ppe_base + PPE_TB_CFG, FIN_AGE, 0); ++ ++ r1 = readl(host->fe_base + 0x100); ++ r2 = readl(host->fe_base + 0x10c); ++ ++ dev_info(host->dev, "0x100 = 0x%x, 0x10c = 0x%x\n", r1, r2); ++ ++ if (((r1 & 0xff00) >> 0x8) >= (r1 & 0xff) || ++ ((r1 & 0xff00) >> 0x8) >= (r2 & 0xff)) { ++ dev_info(host->dev, "reset pse\n"); ++ writel(0x1, host->fe_base + 0x4); ++ } ++ ++ /* free the FOE table */ ++ foe_table_sz = FOE_4TB_SIZ * sizeof(struct foe_entry); ++ dma_free_coherent(NULL, foe_table_sz, host->foe_table_cpu, ++ host->foe_table_dev); ++ writel(0, host->ppe_base + PPE_TB_BASE); ++ ++ if (ppe_busy_wait()) { ++#if 0 ++ reset_control_reset(host->rstc); ++#endif ++ msleep(2000); ++ return; ++ } ++ ++ /* send all traffic back to the DMA engine */ ++ cr_clr_bits(host->fe_base + GDMA1_FWD_CFG, GDM1_ALL_FRC_MASK); ++ cr_set_bits(host->fe_base + GDMA1_FWD_CFG, ++ BITS_GDM1_ALL_FRC_P_CPU_PDMA); ++ cr_clr_bits(host->fe_base + GDMA2_FWD_CFG, GDM2_ALL_FRC_MASK); ++ cr_set_bits(host->fe_base + GDMA2_FWD_CFG, ++ BITS_GDM2_ALL_FRC_P_CPU_PDMA); ++} ++ ++static int hnat_probe(struct platform_device *pdev) ++{ ++ struct net *net; ++ int ret; ++ int err = 0; ++ struct resource *res ; ++ const char *name; ++ struct device_node *np; ++ ++ host = devm_kzalloc(&pdev->dev, sizeof(struct hnat_priv), GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ host->dev = &pdev->dev; ++ np = host->dev->of_node; ++ ++ err = of_property_read_string(np, "mtketh-wan", &name); ++ if (err < 0) ++ return -EINVAL; ++ ++ memcpy(host->wan, (char *)name, IFNAMSIZ); ++ dev_info(&pdev->dev, "wan = %s\n", host->wan); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -ENOENT; ++ ++ host->fe_base = devm_ioremap_nocache(&pdev->dev, res->start, ++ res->end - res->start + 1); ++ if (!host->fe_base) ++ return -EADDRNOTAVAIL; ++ ++ host->ppe_base = host->fe_base + 0xe00; ++ err = hnat_init_debugfs(host); ++ if (err) ++ return err; ++#if 0 ++ host->rstc = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(host->rstc)) ++ return PTR_ERR(host->rstc); ++#endif ++ err = hnat_start(); ++ if (err) ++ goto err_out; ++ ++ //err = hnat_register_nf_hooks(); //how to call the function with net-param?? ++ for_each_net(net) { ++ ret=hnat_register_nf_hooks(net); ++ //if (err) ++ if (ret && ret != -ENOENT) ++ goto err_out; ++ } ++ return 0; ++ ++err_out: ++ hnat_stop(); ++ hnat_deinit_debugfs(host); ++ return err; ++} ++ ++static int hnat_remove(struct platform_device *pdev) ++{ ++ struct net *net; ++ //hnat_unregister_nf_hooks(); //how to call the function with net-param?? ++ for_each_net(net) { ++ hnat_unregister_nf_hooks(net); ++ } ++ ++ hnat_stop(); ++ hnat_deinit_debugfs(host); ++ ++ return 0; ++} ++ ++const struct of_device_id of_hnat_match[] = { ++ { .compatible = "mediatek,mt7623-hnat" }, ++ {}, ++}; ++ ++static struct platform_driver hnat_driver = { ++ .probe = hnat_probe, ++ .remove = hnat_remove, ++ .driver = { ++ .name = "mediatek_soc_hnat", ++ .of_match_table = of_hnat_match, ++ }, ++}; ++ ++module_platform_driver(hnat_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Sean Wang "); ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("Mediatek Hardware NAT"); +diff --git a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h +new file mode 100644 +index 0000000000000..336ebb34c325d +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h +@@ -0,0 +1,427 @@ ++/* This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Copyright (C) 2014-2016 Sean Wang ++ * Copyright (C) 2016-2017 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++/*--------------------------------------------------------------------------*/ ++/* Register Offset*/ ++/*--------------------------------------------------------------------------*/ ++#define PPE_GLO_CFG 0x00 ++#define PPE_FLOW_CFG 0x04 ++#define PPE_IP_PROT_CHK 0x08 ++#define PPE_IP_PROT_0 0x0C ++#define PPE_IP_PROT_1 0x10 ++#define PPE_IP_PROT_2 0x14 ++#define PPE_IP_PROT_3 0x18 ++#define PPE_TB_CFG 0x1C ++#define PPE_TB_BASE 0x20 ++#define PPE_TB_USED 0x24 ++#define PPE_BNDR 0x28 ++#define PPE_BIND_LMT_0 0x2C ++#define PPE_BIND_LMT_1 0x30 ++#define PPE_KA 0x34 ++#define PPE_UNB_AGE 0x38 ++#define PPE_BND_AGE_0 0x3C ++#define PPE_BND_AGE_1 0x40 ++#define PPE_HASH_SEED 0x44 ++#define PPE_DFT_CPORT 0x48 ++#define PPE_MCAST_PPSE 0x84 ++#define PPE_MCAST_L_0 0x88 ++#define PPE_MCAST_H_0 0x8C ++#define PPE_MCAST_L_1 0x90 ++#define PPE_MCAST_H_1 0x94 ++#define PPE_MCAST_L_2 0x98 ++#define PPE_MCAST_H_2 0x9C ++#define PPE_MCAST_L_3 0xA0 ++#define PPE_MCAST_H_3 0xA4 ++#define PPE_MCAST_L_4 0xA8 ++#define PPE_MCAST_H_4 0xAC ++#define PPE_MCAST_L_5 0xB0 ++#define PPE_MCAST_H_5 0xB4 ++#define PPE_MCAST_L_6 0xBC ++#define PPE_MCAST_H_6 0xC0 ++#define PPE_MCAST_L_7 0xC4 ++#define PPE_MCAST_H_7 0xC8 ++#define PPE_MCAST_L_8 0xCC ++#define PPE_MCAST_H_8 0xD0 ++#define PPE_MCAST_L_9 0xD4 ++#define PPE_MCAST_H_9 0xD8 ++#define PPE_MCAST_L_A 0xDC ++#define PPE_MCAST_H_A 0xE0 ++#define PPE_MCAST_L_B 0xE4 ++#define PPE_MCAST_H_B 0xE8 ++#define PPE_MCAST_L_C 0xEC ++#define PPE_MCAST_H_C 0xF0 ++#define PPE_MCAST_L_D 0xF4 ++#define PPE_MCAST_H_D 0xF8 ++#define PPE_MCAST_L_E 0xFC ++#define PPE_MCAST_H_E 0xE0 ++#define PPE_MCAST_L_F 0x100 ++#define PPE_MCAST_H_F 0x104 ++#define PPE_MTU_DRP 0x108 ++#define PPE_MTU_VLYR_0 0x10C ++#define PPE_MTU_VLYR_1 0x110 ++#define PPE_MTU_VLYR_2 0x114 ++#define PPE_VPM_TPID 0x118 ++#define PPE_CAH_CTRL 0x120 ++#define PPE_CAH_TAG_SRH 0x124 ++#define PPE_CAH_LINE_RW 0x128 ++#define PPE_CAH_WDATA 0x12C ++#define PPE_CAH_RDATA 0x130 ++ ++#define GDMA1_FWD_CFG 0x500 ++#define GDMA2_FWD_CFG 0x1500 ++/*--------------------------------------------------------------------------*/ ++/* Register Mask*/ ++/*--------------------------------------------------------------------------*/ ++/* PPE_TB_CFG mask */ ++#define TB_ETRY_NUM (0x7 << 0) /* RW */ ++#define TB_ENTRY_SIZE (0x1 << 3) /* RW */ ++#define SMA (0x3 << 4) /* RW */ ++#define NTU_AGE (0x1 << 7) /* RW */ ++#define UNBD_AGE (0x1 << 8) /* RW */ ++#define TCP_AGE (0x1 << 9) /* RW */ ++#define UDP_AGE (0x1 << 10) /* RW */ ++#define FIN_AGE (0x1 << 11) /* RW */ ++#define KA_CFG (0x3<< 12) ++#define HASH_MODE (0x3 << 14) /* RW */ ++#define XMODE (0x3 << 18) /* RW */ ++ ++/*PPE_CAH_CTRL mask*/ ++#define CAH_EN (0x1 << 0) /* RW */ ++#define CAH_X_MODE (0x1 << 9) /* RW */ ++ ++/*PPE_UNB_AGE mask*/ ++#define UNB_DLTA (0xff << 0) /* RW */ ++#define UNB_MNP (0xffff << 16) /* RW */ ++ ++/*PPE_BND_AGE_0 mask*/ ++#define UDP_DLTA (0xffff << 0) /* RW */ ++#define NTU_DLTA (0xffff << 16) /* RW */ ++ ++/*PPE_BND_AGE_1 mask*/ ++#define TCP_DLTA (0xffff << 0) /* RW */ ++#define FIN_DLTA (0xffff << 16) /* RW */ ++ ++/*PPE_KA mask*/ ++#define KA_T (0xffff << 0) /* RW */ ++#define TCP_KA (0xff << 16) /* RW */ ++#define UDP_KA (0xff << 24) /* RW */ ++ ++/*PPE_BIND_LMT_0 mask*/ ++#define QURT_LMT (0x3ff << 0) /* RW */ ++#define HALF_LMT (0x3ff << 16) /* RW */ ++ ++/*PPE_BIND_LMT_1 mask*/ ++#define FULL_LMT (0x3fff << 0) /* RW */ ++#define NTU_KA (0xff << 16) /* RW */ ++ ++/*PPE_BNDR mask*/ ++#define BIND_RATE (0xffff << 0) /* RW */ ++#define PBND_RD_PRD (0xffff << 16) /* RW */ ++ ++/*PPE_GLO_CFG mask*/ ++#define PPE_EN (0x1 << 0) /* RW */ ++#define TTL0_DRP (0x1 << 4) /* RW */ ++ ++/*GDMA1_FWD_CFG mask */ ++#define GDM1_UFRC_MASK (0x7 << 12) /* RW */ ++#define GDM1_BFRC_MASK (0x7 << 8) /*RW*/ ++#define GDM1_MFRC_MASK (0x7 << 4) /*RW*/ ++#define GDM1_OFRC_MASK (0x7 << 0) /*RW*/ ++#define GDM1_ALL_FRC_MASK (GDM1_UFRC_MASK | GDM1_BFRC_MASK | GDM1_MFRC_MASK | GDM1_OFRC_MASK) ++ ++#define GDM2_UFRC_MASK (0x7 << 12) /* RW */ ++#define GDM2_BFRC_MASK (0x7 << 8) /*RW*/ ++#define GDM2_MFRC_MASK (0x7 << 4) /*RW*/ ++#define GDM2_OFRC_MASK (0x7 << 0) /*RW*/ ++#define GDM2_ALL_FRC_MASK (GDM2_UFRC_MASK | GDM2_BFRC_MASK | GDM2_MFRC_MASK | GDM2_OFRC_MASK) ++ ++/*--------------------------------------------------------------------------*/ ++/* Descriptor Structure */ ++/*--------------------------------------------------------------------------*/ ++#define HNAT_SKB_CB(__skb) ((struct hnat_skb_cb *)&((__skb)->cb[40])) ++struct hnat_skb_cb { ++ __u16 iif; ++}; ++ ++struct hnat_unbind_info_blk { ++ u32 time_stamp:8; ++ u32 pcnt:16; /* packet count */ ++ u32 preb:1; ++ u32 pkt_type:3; ++ u32 state:2; ++ u32 udp:1; ++ u32 sta:1; /* static entry */ ++} __attribute__ ((packed)); ++ ++struct hnat_bind_info_blk { ++ u32 time_stamp:15; ++ u32 ka:1; /* keep alive */ ++ u32 vlan_layer:3; ++ u32 psn:1; /* egress packet has PPPoE session */ ++ u32 vpm:1; /* 0:ethertype remark, 1:0x8100(CR default) */ ++ u32 ps:1; /* packet sampling */ ++ u32 cah:1; /* cacheable flag */ ++ u32 rmt:1; /* remove tunnel ip header (6rd/dslite only) */ ++ u32 ttl:1; ++ u32 pkt_type:3; ++ u32 state:2; ++ u32 udp:1; ++ u32 sta:1; /* static entry */ ++} __attribute__ ((packed)); ++ ++struct hnat_info_blk2 { ++ u32 qid:4; /* QID in Qos Port */ ++ u32 fqos:1; /* force to PSE QoS port */ ++ u32 dp:3; /* force to PSE port x ++ 0:PSE,1:GSW, 2:GMAC,4:PPE,5:QDMA,7=DROP */ ++ u32 mcast:1; /* multicast this packet to CPU */ ++ u32 pcpl:1; /* OSBN */ ++ u32 mlen:1; /* 0:post 1:pre packet length in meter */ ++ u32 alen:1; /* 0:post 1:pre packet length in accounting */ ++ u32 port_mg:6; /* port meter group */ ++ u32 port_ag:6; /* port account group */ ++ u32 dscp:8; /* DSCP value */ ++} __attribute__ ((packed)); ++ ++struct hnat_ipv4_hnapt { ++ union { ++ struct hnat_bind_info_blk bfib1; ++ struct hnat_unbind_info_blk udib1; ++ u32 info_blk1; ++ }; ++ u32 sip; ++ u32 dip; ++ u16 dport; ++ u16 sport; ++ union { ++ struct hnat_info_blk2 iblk2; ++ u32 info_blk2; ++ }; ++ u32 new_sip; ++ u32 new_dip; ++ u16 new_dport; ++ u16 new_sport; ++ u32 resv1; ++ u32 resv2; ++ u32 resv3:26; ++ u32 act_dp:6; /* UDF */ ++ u16 vlan1; ++ u16 etype; ++ u32 dmac_hi; ++ u16 vlan2; ++ u16 dmac_lo; ++ u32 smac_hi; ++ u16 pppoe_id; ++ u16 smac_lo; ++} __attribute__ ((packed)); ++ ++struct foe_entry { ++ union { ++ struct hnat_unbind_info_blk udib1; ++ struct hnat_bind_info_blk bfib1; ++ struct hnat_ipv4_hnapt ipv4_hnapt; ++ }; ++}; ++ ++#define HNAT_AC_BYTE_LO(x) (0x2000 + (x * 16)) ++#define HNAT_AC_BYTE_HI(x) (0x2004 + (x * 16)) ++#define HNAT_AC_PACKET(x) (0x2008 + (x * 16)) ++#define HNAT_COUNTER_MAX 64 ++#define HNAT_AC_TIMER_INTERVAL (HZ) ++ ++struct hnat_accounting { ++ u64 bytes; ++ u64 packets; ++}; ++ ++struct hnat_priv { ++ struct device *dev; ++ void __iomem *fe_base; ++ void __iomem *ppe_base; ++ struct foe_entry *foe_table_cpu; ++ dma_addr_t foe_table_dev; ++ u8 enable; ++ u8 enable1; ++ struct dentry *root; ++ struct debugfs_regset32 *regset; ++ ++ struct timer_list ac_timer; ++ struct hnat_accounting acct[HNAT_COUNTER_MAX]; ++ ++ /*devices we plays for*/ ++ char wan[IFNAMSIZ]; ++ ++ struct reset_control *rstc; ++}; ++ ++enum FoeEntryState { ++ INVALID = 0, ++ UNBIND = 1, ++ BIND = 2, ++ FIN = 3 ++}; ++/*--------------------------------------------------------------------------*/ ++/* Common Definition*/ ++/*--------------------------------------------------------------------------*/ ++ ++#define FOE_4TB_SIZ 4096 ++#define HASH_SEED_KEY 0x12345678 ++ ++/*PPE_TB_CFG value*/ ++#define ENTRY_80B 1 ++#define ENTRY_64B 0 ++#define TABLE_1K 0 ++#define TABLE_2K 1 ++#define TABLE_4K 2 ++#define TABLE_8K 3 ++#define TABLE_16K 4 ++#define SMA_DROP 0 /* Drop the packet */ ++#define SMA_DROP2 1 /* Drop the packet */ ++#define SMA_ONLY_FWD_CPU 2 /* Only Forward to CPU */ ++#define SMA_FWD_CPU_BUILD_ENTRY 3 /* Forward to CPU and build new FOE entry */ ++#define HASH_MODE_0 0 ++#define HASH_MODE_1 1 ++#define HASH_MODE_2 2 ++#define HASH_MODE_3 3 ++ ++/*PPE_FLOW_CFG*/ ++#define BIT_FUC_FOE BIT(2) ++#define BIT_FMC_FOE BIT(1) ++#define BIT_FBC_FOE BIT(0) ++#define BIT_IPV4_NAT_EN BIT(12) ++#define BIT_IPV4_NAPT_EN BIT(13) ++#define BIT_IPV4_NAT_FRAG_EN BIT(17) ++#define BIT_IPV4_HASH_GREK BIT(19) ++ ++/*GDMA1_FWD_CFG value */ ++#define BITS_GDM1_UFRC_P_PPE (NR_PPE_PORT << 12) ++#define BITS_GDM1_BFRC_P_PPE (NR_PPE_PORT << 8) ++#define BITS_GDM1_MFRC_P_PPE (NR_PPE_PORT << 4) ++#define BITS_GDM1_OFRC_P_PPE (NR_PPE_PORT << 0) ++#define BITS_GDM1_ALL_FRC_P_PPE (BITS_GDM1_UFRC_P_PPE | BITS_GDM1_BFRC_P_PPE | BITS_GDM1_MFRC_P_PPE | BITS_GDM1_OFRC_P_PPE) ++ ++#define BITS_GDM1_UFRC_P_CPU_PDMA (NR_PDMA_PORT << 12) ++#define BITS_GDM1_BFRC_P_CPU_PDMA (NR_PDMA_PORT << 8) ++#define BITS_GDM1_MFRC_P_CPU_PDMA (NR_PDMA_PORT << 4) ++#define BITS_GDM1_OFRC_P_CPU_PDMA (NR_PDMA_PORT << 0) ++#define BITS_GDM1_ALL_FRC_P_CPU_PDMA (BITS_GDM1_UFRC_P_CPU_PDMA | BITS_GDM1_BFRC_P_CPU_PDMA | BITS_GDM1_MFRC_P_CPU_PDMA | BITS_GDM1_OFRC_P_CPU_PDMA) ++ ++#define BITS_GDM1_UFRC_P_CPU_QDMA (NR_QDMA_PORT << 12) ++#define BITS_GDM1_BFRC_P_CPU_QDMA (NR_QDMA_PORT << 8) ++#define BITS_GDM1_MFRC_P_CPU_QDMA (NR_QDMA_PORT << 4) ++#define BITS_GDM1_OFRC_P_CPU_QDMA (NR_QDMA_PORT << 0) ++#define BITS_GDM1_ALL_FRC_P_CPU_QDMA (BITS_GDM1_UFRC_P_CPU_QDMA | BITS_GDM1_BFRC_P_CPU_QDMA | BITS_GDM1_MFRC_P_CPU_QDMA | BITS_GDM1_OFRC_P_CPU_QDMA) ++ ++#define BITS_GDM1_UFRC_P_DISCARD (NR_DISCARD << 12) ++#define BITS_GDM1_BFRC_P_DISCARD (NR_DISCARD << 8) ++#define BITS_GDM1_MFRC_P_DISCARD (NR_DISCARD << 4) ++#define BITS_GDM1_OFRC_P_DISCARD (NR_DISCARD << 0) ++#define BITS_GDM1_ALL_FRC_P_DISCARD (BITS_GDM1_UFRC_P_DISCARD | BITS_GDM1_BFRC_P_DISCARD | BITS_GDM1_MFRC_P_DISCARD | BITS_GDM1_OFRC_P_DISCARD) ++ ++#define BITS_GDM2_UFRC_P_PPE (NR_PPE_PORT << 12) ++#define BITS_GDM2_BFRC_P_PPE (NR_PPE_PORT << 8) ++#define BITS_GDM2_MFRC_P_PPE (NR_PPE_PORT << 4) ++#define BITS_GDM2_OFRC_P_PPE (NR_PPE_PORT << 0) ++#define BITS_GDM2_ALL_FRC_P_PPE (BITS_GDM2_UFRC_P_PPE | BITS_GDM2_BFRC_P_PPE | BITS_GDM2_MFRC_P_PPE | BITS_GDM2_OFRC_P_PPE) ++ ++#define BITS_GDM2_UFRC_P_CPU_PDMA (NR_PDMA_PORT << 12) ++#define BITS_GDM2_BFRC_P_CPU_PDMA (NR_PDMA_PORT << 8) ++#define BITS_GDM2_MFRC_P_CPU_PDMA (NR_PDMA_PORT << 4) ++#define BITS_GDM2_OFRC_P_CPU_PDMA (NR_PDMA_PORT << 0) ++#define BITS_GDM2_ALL_FRC_P_CPU_PDMA (BITS_GDM2_UFRC_P_CPU_PDMA | BITS_GDM2_BFRC_P_CPU_PDMA | BITS_GDM2_MFRC_P_CPU_PDMA | BITS_GDM2_OFRC_P_CPU_PDMA) ++ ++#define BITS_GDM2_UFRC_P_CPU_QDMA (NR_QDMA_PORT << 12) ++#define BITS_GDM2_BFRC_P_CPU_QDMA (NR_QDMA_PORT << 8) ++#define BITS_GDM2_MFRC_P_CPU_QDMA (NR_QDMA_PORT << 4) ++#define BITS_GDM2_OFRC_P_CPU_QDMA (NR_QDMA_PORT << 0) ++#define BITS_GDM2_ALL_FRC_P_CPU_QDMA (BITS_GDM2_UFRC_P_CPU_QDMA | BITS_GDM2_BFRC_P_CPU_QDMA | BITS_GDM2_MFRC_P_CPU_QDMA | BITS_GDM2_OFRC_P_CPU_QDMA) ++ ++#define BITS_GDM2_UFRC_P_DISCARD (NR_DISCARD << 12) ++#define BITS_GDM2_BFRC_P_DISCARD (NR_DISCARD << 8) ++#define BITS_GDM2_MFRC_P_DISCARD (NR_DISCARD << 4) ++#define BITS_GDM2_OFRC_P_DISCARD (NR_DISCARD << 0) ++#define BITS_GDM2_ALL_FRC_P_DISCARD (BITS_GDM2_UFRC_P_DISCARD | BITS_GDM2_BFRC_P_DISCARD | BITS_GDM2_MFRC_P_DISCARD | BITS_GDM2_OFRC_P_DISCARD) ++ ++#define hnat_is_enabled(host) (host->enable) ++#define hnat_enabled(host) (host->enable = 1) ++#define hnat_disabled(host) (host->enable = 0) ++#define hnat_is_enabled1(host) (host->enable1) ++#define hnat_enabled1(host) (host->enable1 = 1) ++#define hnat_disabled1(host) (host->enable1 = 0) ++ ++#define entry_hnat_is_bound(e) (e->bfib1.state == BIND) ++#define entry_hnat_state(e) (e->bfib1.state) ++ ++#define skb_hnat_is_hashed(skb) (skb_hnat_entry(skb)!=0x3fff && skb_hnat_entry(skb)< FOE_4TB_SIZ) ++#define FROM_GE_LAN(skb) (HNAT_SKB_CB(skb)->iif == FOE_MAGIC_GE_LAN) ++#define FROM_GE_WAN(skb) (HNAT_SKB_CB(skb)->iif == FOE_MAGIC_GE_WAN) ++#define FROM_GE_PPD(skb) (HNAT_SKB_CB(skb)->iif == FOE_MAGIC_GE_PPD) ++#define FOE_MAGIC_GE_WAN 0x7273 ++#define FOE_MAGIC_GE_LAN 0x7272 ++#define FOE_INVALID 0xffff ++ ++#define TCP_FIN_SYN_RST 0x0C /* Ingress packet is TCP fin/syn/rst (for IPv4 NAPT/DS-Lite or IPv6 5T-route/6RD) */ ++#define UN_HIT 0x0D/* FOE Un-hit */ ++#define HIT_UNBIND 0x0E/* FOE Hit unbind */ ++#define HIT_UNBIND_RATE_REACH 0xf ++#define HNAT_HIT_BIND_OLD_DUP_HDR 0x15 ++#define HNAT_HIT_BIND_FORCE_TO_CPU 0x16 ++ ++#define HIT_BIND_KEEPALIVE_MC_NEW_HDR 0x14 ++#define HIT_BIND_KEEPALIVE_DUP_OLD_HDR 0x15 ++#define IPV4_HNAPT 0 ++#define IPV4_HNAT 1 ++#define IP_FORMAT(addr) \ ++ ((unsigned char *)&addr)[3], \ ++ ((unsigned char *)&addr)[2], \ ++ ((unsigned char *)&addr)[1], \ ++ ((unsigned char *)&addr)[0] ++ ++/*PSE Ports*/ ++#define NR_PDMA_PORT 0 ++#define NR_GMAC1_PORT 1 ++#define NR_GMAC2_PORT 2 ++#define NR_PPE_PORT 4 ++#define NR_QDMA_PORT 5 ++#define NR_DISCARD 7 ++#define IS_LAN(dev) (!strncmp(dev->name, "lan", 3)) ++#define IS_WAN(dev) (!strcmp(dev->name, host->wan)) ++#define IS_BR(dev) (!strncmp(dev->name, "br", 2)) ++#define IS_IPV4_HNAPT(x) (((x)->bfib1.pkt_type == IPV4_HNAPT) ? 1: 0) ++#define IS_IPV4_HNAT(x) (((x)->bfib1.pkt_type == IPV4_HNAT) ? 1 : 0) ++#define IS_IPV4_GRP(x) (IS_IPV4_HNAPT(x) | IS_IPV4_HNAT(x)) ++ ++#define es(entry) (entry_state[entry->bfib1.state]) ++#define ei(entry, end) (FOE_4TB_SIZ - (int)(end - entry)) ++#define pt(entry) (packet_type[entry->ipv4_hnapt.bfib1.pkt_type]) ++#define ipv4_smac(mac,e) ({mac[0]=e->ipv4_hnapt.smac_hi[3]; mac[1]=e->ipv4_hnapt.smac_hi[2];\ ++ mac[2]=e->ipv4_hnapt.smac_hi[1]; mac[3]=e->ipv4_hnapt.smac_hi[0];\ ++ mac[4]=e->ipv4_hnapt.smac_lo[1]; mac[5]=e->ipv4_hnapt.smac_lo[0];}) ++#define ipv4_dmac(mac,e) ({mac[0]=e->ipv4_hnapt.dmac_hi[3]; mac[1]=e->ipv4_hnapt.dmac_hi[2];\ ++ mac[2]=e->ipv4_hnapt.dmac_hi[1]; mac[3]=e->ipv4_hnapt.dmac_hi[0];\ ++ mac[4]=e->ipv4_hnapt.dmac_lo[1]; mac[5]=e->ipv4_hnapt.dmac_lo[0];}) ++ ++extern struct hnat_priv *host; ++ ++extern void hnat_deinit_debugfs(struct hnat_priv *h); ++extern int hnat_init_debugfs(struct hnat_priv *h); ++//extern int hnat_register_nf_hooks(void); ++//extern void hnat_unregister_nf_hooks(void); ++extern int hnat_register_nf_hooks(struct net *net); ++extern void hnat_unregister_nf_hooks(struct net *net); ++ +diff --git a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c +new file mode 100644 +index 0000000000000..fbb0380ea0d92 +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c +@@ -0,0 +1,489 @@ ++/* This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Copyright (C) 2014-2016 Sean Wang ++ * Copyright (C) 2016-2017 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include "hnat.h" ++ ++static const char *entry_state[] = { ++ "INVALID", ++ "UNBIND", ++ "BIND", ++ "FIN" ++}; ++ ++static const char *packet_type[] = { ++ "IPV4_HNAPT", ++ "IPV4_HNAT", ++ "IPV6_1T_ROUTE", ++ "IPV4_DSLITE", ++ "IPV6_3T_ROUTE", ++ "IPV6_5T_ROUTE", ++ "IPV6_6RD", ++}; ++ ++static int hnat_debug_show(struct seq_file *m, void *private) ++{ ++ struct hnat_priv *h = host; ++ struct foe_entry *entry, *end; ++ ++ entry = h->foe_table_cpu; ++ end = h->foe_table_cpu + FOE_4TB_SIZ; ++ while (entry < end) { ++ if (!entry->bfib1.state) { ++ entry++; ++ continue; ++ } ++ ++ if (IS_IPV4_HNAPT(entry)) { ++ __be32 saddr = htonl(entry->ipv4_hnapt.sip); ++ __be32 daddr = htonl(entry->ipv4_hnapt.dip); ++ __be32 nsaddr = htonl(entry->ipv4_hnapt.new_sip); ++ __be32 ndaddr = htonl(entry->ipv4_hnapt.new_dip); ++ unsigned char h_dest[ETH_ALEN]; ++ unsigned char h_source[ETH_ALEN]; ++ ++ *((u32*) h_source) = swab32(entry->ipv4_hnapt.smac_hi); ++ *((u16*) &h_source[4]) = swab16(entry->ipv4_hnapt.smac_lo); ++ *((u32*) h_dest) = swab32(entry->ipv4_hnapt.dmac_hi); ++ *((u16*) &h_dest[4]) = swab16(entry->ipv4_hnapt.dmac_lo); ++ seq_printf(m, ++ "(%p)0x%05x|state=%s|type=%s|%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n", ++ (void *)h->foe_table_dev + ((void *)(entry) - (void *)h->foe_table_cpu), ++ ei(entry, end), es(entry), pt(entry), ++ &saddr, entry->ipv4_hnapt.sport, ++ &daddr, entry->ipv4_hnapt.dport, ++ &nsaddr, entry->ipv4_hnapt.new_sport, ++ &ndaddr, entry->ipv4_hnapt.new_dport, h_source, ++ h_dest, ntohs(entry->ipv4_hnapt.etype), ++ entry->ipv4_hnapt.info_blk1, ++ entry->ipv4_hnapt.info_blk2, ++ entry->ipv4_hnapt.vlan1, ++ entry->ipv4_hnapt.vlan2); ++ } else ++ seq_printf(m, "0x%05x state=%s\n", ++ ei(entry, end), es(entry)); ++ entry++; ++ } ++ ++ return 0; ++} ++ ++static int hnat_debug_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, hnat_debug_show, file->private_data); ++} ++ ++static const struct file_operations hnat_debug_fops = { ++ .open = hnat_debug_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++#define QDMA_TX_SCH_TX 0x1a14 ++ ++static ssize_t hnat_sched_show(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ int id = (int) file->private_data; ++ struct hnat_priv *h = host; ++ u32 reg = readl(h->fe_base + QDMA_TX_SCH_TX); ++ int enable; ++ int max_rate; ++ char *buf; ++ unsigned int len = 0, buf_len = 1500; ++ ssize_t ret_cnt; ++ ++ buf = kzalloc(buf_len, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ ++ if (id) ++ reg >>= 16; ++ reg &= 0xffff; ++ enable = !! (reg & BIT(11)); ++ max_rate = ((reg >> 4) & 0x7f); ++ reg &= 0xf; ++ while (reg--) ++ max_rate *= 10; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "EN\tMAX\n%d\t%d\n", enable, max_rate); ++ ++ if (len > buf_len) ++ len = buf_len; ++ ++ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); ++ ++ kfree(buf); ++ return ret_cnt; ++} ++ ++static ssize_t hnat_sched_write(struct file *file, ++ const char __user *buf, size_t length, loff_t *offset) ++{ ++ int id = (int) file->private_data; ++ struct hnat_priv *h = host; ++ char line[64]; ++ int enable, rate, exp = 0, shift = 0; ++ size_t size; ++ u32 reg = readl(h->fe_base + QDMA_TX_SCH_TX); ++ u32 val = 0; ++ ++ if (length > sizeof(line)) ++ return -EINVAL; ++ ++ if (copy_from_user(line, buf, length)) ++ return -EFAULT; ++ ++ sscanf(line, "%d %d", &enable, &rate); ++ ++ while (rate > 127) { ++ rate /= 10; ++ exp++; ++ } ++ ++ if (enable) ++ val |= BIT(11); ++ val |= (rate & 0x7f) << 4; ++ val |= exp & 0xf; ++ if (id) ++ shift = 16; ++ reg &= ~(0xffff << shift); ++ reg |= val << shift; ++ writel(reg, h->fe_base + QDMA_TX_SCH_TX); ++ ++ size = strlen(line); ++ *offset += size; ++ ++ return length; ++} ++ ++static const struct file_operations hnat_sched_fops = { ++ .open = simple_open, ++ .read = hnat_sched_show, ++ .write = hnat_sched_write, ++ .llseek = default_llseek, ++}; ++ ++#define QTX_CFG(x) (0x1800 + (x * 0x10)) ++#define QTX_SCH(x) (0x1804 + (x * 0x10)) ++ ++static ssize_t hnat_queue_show(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct hnat_priv *h = host; ++ int id = (int) file->private_data; ++ u32 reg = readl(h->fe_base + QTX_SCH(id)); ++ u32 cfg = readl(h->fe_base + QTX_CFG(id)); ++ int scheduler = !!(reg & BIT(31)); ++ int min_rate_en = !!(reg & BIT(27)); ++ int min_rate = (reg >> 20) & 0x7f; ++ int min_rate_exp = (reg >> 16) & 0xf; ++ int max_rate_en = !!(reg & BIT(11)); ++ int max_weight = (reg >> 12) & 0xf; ++ int max_rate = (reg >> 4) & 0x7f; ++ int max_rate_exp = reg & 0xf; ++ char *buf; ++ unsigned int len = 0, buf_len = 1500; ++ ssize_t ret_cnt; ++ ++ buf = kzalloc(buf_len, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ while (min_rate_exp--) ++ min_rate *= 10; ++ ++ while (max_rate_exp--) ++ max_rate *= 10; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "scheduler: %d\nhw resv: %d\nsw resv: %d\n", ++ scheduler, (cfg >> 8) & 0xff, cfg & 0xff); ++ len += scnprintf(buf + len, buf_len - len, ++ "\tEN\tRATE\t\tWEIGHT\n"); ++ len += scnprintf(buf + len, buf_len - len, ++ "max\t%d\t%8d\t%d\n", max_rate_en, max_rate, max_weight); ++ len += scnprintf(buf + len, buf_len - len, ++ "min\t%d\t%8d\t-\n", min_rate_en, min_rate); ++ ++ if (len > buf_len) ++ len = buf_len; ++ ++ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); ++ ++ kfree(buf); ++ return ret_cnt; ++} ++ ++static ssize_t hnat_queue_write(struct file *file, ++ const char __user *buf, size_t length, loff_t *offset) ++{ ++ int id = (int) file->private_data; ++ struct hnat_priv *h = host; ++ char line[64]; ++ int max_enable, max_rate, max_exp = 0; ++ int min_enable, min_rate, min_exp = 0; ++ int weight; ++ int resv; ++ int scheduler; ++ size_t size; ++ u32 reg = readl(h->fe_base + QTX_SCH(id)); ++ ++ if (length > sizeof(line)) ++ return -EINVAL; ++ ++ if (copy_from_user(line, buf, length)) ++ return -EFAULT; ++ ++ sscanf(line, "%d %d %d %d %d %d %d", &scheduler, &min_enable, &min_rate, &max_enable, &max_rate, &weight, &resv); ++ ++ while (max_rate > 127) { ++ max_rate /= 10; ++ max_exp++; ++ } ++ ++ while (min_rate > 127) { ++ min_rate /= 10; ++ min_exp++; ++ } ++ ++ reg &= 0x70000000; ++ if (scheduler) ++ reg |= BIT(31); ++ if (min_enable) ++ reg |= BIT(27); ++ reg |= (min_rate & 0x7f) << 20; ++ reg |= (min_exp & 0xf) << 16; ++ if (max_enable) ++ reg |= BIT(11); ++ reg |= (weight & 0xf) << 12; ++ reg |= (max_rate & 0x7f) << 4; ++ reg |= max_exp & 0xf; ++ writel(reg, h->fe_base + QTX_SCH(id)); ++ ++ resv &= 0xff; ++ reg = readl(h->fe_base + QTX_CFG(id)); ++ reg &= 0xffff0000; ++ reg |= (resv << 8) | resv; ++ writel(reg, h->fe_base + QTX_CFG(id)); ++ ++ size = strlen(line); ++ *offset += size; ++ ++ return length; ++} ++ ++static const struct file_operations hnat_queue_fops = { ++ .open = simple_open, ++ .read = hnat_queue_show, ++ .write = hnat_queue_write, ++ .llseek = default_llseek, ++}; ++ ++static void hnat_ac_timer_handle(unsigned long priv) ++{ ++ struct hnat_priv *h = (struct hnat_priv*) priv; ++ int i; ++ ++ for (i = 0; i < HNAT_COUNTER_MAX; i++) { ++ u32 b_hi, b_lo; ++ u64 b; ++ ++ b_lo = readl(h->fe_base + HNAT_AC_BYTE_LO(i)); ++ b_hi = readl(h->fe_base + HNAT_AC_BYTE_HI(i)); ++ b = b_hi; ++ b <<= 32; ++ b += b_lo; ++ h->acct[i].bytes += b; ++ h->acct[i].packets += readl(h->fe_base + HNAT_AC_PACKET(i)); ++ } ++ ++ mod_timer(&h->ac_timer, jiffies + HNAT_AC_TIMER_INTERVAL); ++} ++ ++static ssize_t hnat_counter_show(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct hnat_priv *h = host; ++ int id = (int) file->private_data; ++ char *buf; ++ unsigned int len = 0, buf_len = 1500; ++ ssize_t ret_cnt; ++ int id2 = id + (HNAT_COUNTER_MAX / 2); ++ ++ buf = kzalloc(buf_len, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ len += scnprintf(buf + len, buf_len - len, ++ "tx pkts : %llu\ntx bytes: %llu\nrx pktks : %llu\nrx bytes : %llu\n", ++ h->acct[id].packets, h->acct[id].bytes, ++ h->acct[id2].packets, h->acct[id2].bytes); ++ ++ if (len > buf_len) ++ len = buf_len; ++ ++ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); ++ ++ kfree(buf); ++ return ret_cnt; ++} ++ ++static const struct file_operations hnat_counter_fops = { ++ .open = simple_open, ++ .read = hnat_counter_show, ++ .llseek = default_llseek, ++}; ++ ++#define dump_register(nm) \ ++{ \ ++ .name = __stringify(nm), \ ++ .offset = PPE_ ##nm , \ ++} ++ ++static const struct debugfs_reg32 hnat_regs[] = { ++ dump_register(GLO_CFG), ++ dump_register(FLOW_CFG), ++ dump_register(IP_PROT_CHK), ++ dump_register(IP_PROT_0), ++ dump_register(IP_PROT_1), ++ dump_register(IP_PROT_2), ++ dump_register(IP_PROT_3), ++ dump_register(TB_CFG), ++ dump_register(TB_BASE), ++ dump_register(TB_USED), ++ dump_register(BNDR), ++ dump_register(BIND_LMT_0), ++ dump_register(BIND_LMT_1), ++ dump_register(KA), ++ dump_register(UNB_AGE), ++ dump_register(BND_AGE_0), ++ dump_register(BND_AGE_1), ++ dump_register(HASH_SEED), ++ dump_register(DFT_CPORT), ++ dump_register(MCAST_PPSE), ++ dump_register(MCAST_L_0), ++ dump_register(MCAST_H_0), ++ dump_register(MCAST_L_1), ++ dump_register(MCAST_H_1), ++ dump_register(MCAST_L_2), ++ dump_register(MCAST_H_2), ++ dump_register(MCAST_L_3), ++ dump_register(MCAST_H_3), ++ dump_register(MCAST_L_4), ++ dump_register(MCAST_H_4), ++ dump_register(MCAST_L_5), ++ dump_register(MCAST_H_5), ++ dump_register(MCAST_L_6), ++ dump_register(MCAST_H_6), ++ dump_register(MCAST_L_7), ++ dump_register(MCAST_H_7), ++ dump_register(MCAST_L_8), ++ dump_register(MCAST_H_8), ++ dump_register(MCAST_L_9), ++ dump_register(MCAST_H_9), ++ dump_register(MCAST_L_A), ++ dump_register(MCAST_H_A), ++ dump_register(MCAST_L_B), ++ dump_register(MCAST_H_B), ++ dump_register(MCAST_L_C), ++ dump_register(MCAST_H_C), ++ dump_register(MCAST_L_D), ++ dump_register(MCAST_H_D), ++ dump_register(MCAST_L_E), ++ dump_register(MCAST_H_E), ++ dump_register(MCAST_L_F), ++ dump_register(MCAST_H_F), ++ dump_register(MTU_DRP), ++ dump_register(MTU_VLYR_0), ++ dump_register(MTU_VLYR_1), ++ dump_register(MTU_VLYR_2), ++ dump_register(VPM_TPID), ++ dump_register(VPM_TPID), ++ dump_register(CAH_CTRL), ++ dump_register(CAH_TAG_SRH), ++ dump_register(CAH_LINE_RW), ++ dump_register(CAH_WDATA), ++ dump_register(CAH_RDATA), ++}; ++ ++int hnat_init_debugfs(struct hnat_priv *h) ++{ ++ int ret = 0; ++ struct dentry *root; ++ struct dentry *file; ++ int i; ++ char name[16]; ++ ++ root = debugfs_create_dir("hnat", NULL); ++ if (!root) { ++ dev_err(h->dev, "%s:err at %d\n", __func__, __LINE__); ++ ret = -ENOMEM; ++ goto err0; ++ } ++ h->root = root; ++ h->regset = kzalloc(sizeof(*h->regset), GFP_KERNEL); ++ if (!h->regset) { ++ dev_err(h->dev, "%s:err at %d\n", __func__, __LINE__); ++ ret = -ENOMEM; ++ goto err1; ++ } ++ h->regset->regs = hnat_regs; ++ h->regset->nregs = ARRAY_SIZE(hnat_regs); ++ h->regset->base = h->ppe_base; ++ ++ file = debugfs_create_regset32("regdump", S_IRUGO, root, h->regset); ++ if (!file) { ++ dev_err(h->dev, "%s:err at %d\n", __func__, __LINE__); ++ ret = -ENOMEM; ++ goto err1; ++ } ++ debugfs_create_file("all_entry", S_IRUGO, root, h, &hnat_debug_fops); ++ for (i = 0; i < HNAT_COUNTER_MAX / 2; i++) { ++ snprintf(name, sizeof(name), "counter%d", i); ++ debugfs_create_file(name, S_IRUGO, root, (void *)i, &hnat_counter_fops); ++ } ++ ++ for (i = 0; i < 2; i++) { ++ snprintf(name, sizeof(name), "scheduler%d", i); ++ debugfs_create_file(name, S_IRUGO, root, (void *)i, &hnat_sched_fops); ++ } ++ ++ for (i = 0; i < 16; i++) { ++ snprintf(name, sizeof(name), "queue%d", i); ++ debugfs_create_file(name, S_IRUGO, root, (void *)i, &hnat_queue_fops); ++ } ++ ++ setup_timer(&h->ac_timer, hnat_ac_timer_handle, (unsigned long) h); ++ mod_timer(&h->ac_timer, jiffies + HNAT_AC_TIMER_INTERVAL); ++ ++ return 0; ++ ++ err1: ++ debugfs_remove_recursive(root); ++ err0: ++ return ret; ++} ++ ++void hnat_deinit_debugfs(struct hnat_priv *h) ++{ ++ del_timer(&h->ac_timer); ++ debugfs_remove_recursive(h->root); ++ h->root = NULL; ++} +diff --git a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c +new file mode 100644 +index 0000000000000..0ce436ac00e61 +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c +@@ -0,0 +1,312 @@ ++/* This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Copyright (C) 2014-2016 Sean Wang ++ * Copyright (C) 2016-2017 John Crispin ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "nf_hnat_mtk.h" ++#include "hnat.h" ++ ++#include "../mtk_eth_soc.h" ++ ++static unsigned int skb_to_hnat_info(struct sk_buff *skb, ++ const struct net_device *dev, ++ struct foe_entry *foe) ++{ ++ struct foe_entry entry = { 0 }; ++ int lan = IS_LAN(dev); ++ int wan = IS_WAN(dev); ++ struct ethhdr *eth; ++ struct iphdr *iph; ++ struct tcphdr *tcph; ++ struct udphdr *udph; ++ int tcp = 0; ++ int ipv4 = 0; ++ u32 gmac; ++ ++ eth = eth_hdr(skb); ++ switch (ntohs(eth->h_proto)) { ++ case ETH_P_IP: ++ ipv4 = 1; ++ break; ++ ++ default: ++ return -1; ++ } ++ ++ iph = ip_hdr(skb); ++ switch (iph->protocol) { ++ case IPPROTO_TCP: ++ tcph = tcp_hdr(skb); ++ tcp = 1; ++ break; ++ ++ case IPPROTO_UDP: ++ udph = udp_hdr(skb); ++ break; ++ ++ default: ++ return -1; ++ } ++ ++ entry.ipv4_hnapt.etype = htons(ETH_P_IP); ++ ++ if (lan) { ++ entry.ipv4_hnapt.etype = htons(ETH_P_8021Q); ++ entry.bfib1.vlan_layer = 1; ++ ++ /* lan0-port1, lan1-port2, lan2-port3, lan3-port4 */ ++ entry.ipv4_hnapt.vlan1 = BIT((dev->name[3] - '0')+1); ++ } else if (wan) { ++ entry.ipv4_hnapt.etype = htons(ETH_P_8021Q); ++ entry.bfib1.vlan_layer = 1; ++ ++ /* wan port 0 */ ++ entry.ipv4_hnapt.vlan1 = BIT(0); ++ } ++ ++ if (dev->priv_flags & IFF_802_1Q_VLAN) { ++ struct vlan_dev_priv *vlan = vlan_dev_priv(dev); ++ ++ entry.ipv4_hnapt.etype = htons(ETH_P_8021Q); ++ entry.bfib1.vlan_layer = 1; ++ entry.ipv4_hnapt.vlan2 = vlan->vlan_id; ++ } ++ ++ entry.ipv4_hnapt.dmac_hi = swab32(*((u32*) eth->h_dest)); ++ entry.ipv4_hnapt.dmac_lo = swab16(*((u16*) ð->h_dest[4])); ++ entry.ipv4_hnapt.smac_hi = swab32(*((u32*) eth->h_source)); ++ entry.ipv4_hnapt.smac_lo = swab16(*((u16*) ð->h_source[4])); ++ entry.ipv4_hnapt.pppoe_id = 0; ++ entry.bfib1.psn = 0; ++ entry.ipv4_hnapt.bfib1.vpm = 1; ++ ++ if (ipv4) ++ entry.ipv4_hnapt.bfib1.pkt_type = IPV4_HNAPT; ++ ++ entry.ipv4_hnapt.new_sip = ntohl(iph->saddr); ++ entry.ipv4_hnapt.new_dip = ntohl(iph->daddr); ++ entry.ipv4_hnapt.iblk2.dscp = iph->tos; ++#if defined(CONFIG_NET_MEDIATEK_HW_QOS) ++ entry.ipv4_hnapt.iblk2.qid = skb->mark & 0x7; ++ if (lan) ++ entry.ipv4_hnapt.iblk2.qid += 8; ++ entry.ipv4_hnapt.iblk2.fqos = 1; ++#endif ++ if (tcp) { ++ entry.ipv4_hnapt.new_sport = ntohs(tcph->source); ++ entry.ipv4_hnapt.new_dport = ntohs(tcph->dest); ++ entry.ipv4_hnapt.bfib1.udp = 0; ++ } else { ++ entry.ipv4_hnapt.new_sport = ntohs(udph->source); ++ entry.ipv4_hnapt.new_dport = ntohs(udph->dest); ++ entry.ipv4_hnapt.bfib1.udp = 1; ++ } ++ ++ if (IS_LAN(dev)) ++ gmac = NR_GMAC1_PORT; ++ else if (IS_WAN(dev)) ++ gmac = NR_GMAC2_PORT; ++ ++ if (is_multicast_ether_addr(ð->h_dest[0])) ++ entry.ipv4_hnapt.iblk2.mcast = 1; ++ else ++ entry.ipv4_hnapt.iblk2.mcast = 0; ++ ++ entry.ipv4_hnapt.iblk2.dp = gmac; ++ entry.ipv4_hnapt.iblk2.port_mg = 0x3f; ++ entry.ipv4_hnapt.iblk2.port_ag = (skb->mark >> 3) & 0x1f; ++ if (IS_LAN(dev)) ++ entry.ipv4_hnapt.iblk2.port_ag += 32; ++ entry.bfib1.time_stamp = readl((host->fe_base + 0x0010)) & (0xFFFF); ++ entry.ipv4_hnapt.bfib1.ttl = 1; ++ entry.ipv4_hnapt.bfib1.cah = 1; ++ entry.ipv4_hnapt.bfib1.ka = 1; ++ entry.bfib1.state = BIND; ++ ++ entry.ipv4_hnapt.sip = foe->ipv4_hnapt.sip; ++ entry.ipv4_hnapt.dip = foe->ipv4_hnapt.dip; ++ entry.ipv4_hnapt.sport = foe->ipv4_hnapt.sport; ++ entry.ipv4_hnapt.dport = foe->ipv4_hnapt.dport; ++ ++ memcpy(foe, &entry, sizeof(entry)); ++ ++ return 0; ++} ++ ++static unsigned int mtk_hnat_nf_post_routing(struct sk_buff *skb, ++ const struct net_device *out, ++ unsigned int (*fn)(struct sk_buff *, const struct net_device *), ++ const char *func) ++{ ++ struct foe_entry *entry; ++ struct nf_conn *ct; ++ enum ip_conntrack_info ctinfo; ++ const struct nf_conn_help *help; ++ ++#if 0 ++ if ((skb->mark & 0x7) < 4) ++ return 0; ++#endif ++ ++ ct = nf_ct_get(skb, &ctinfo); ++ if (!ct) ++ return 0; ++ ++ /* rcu_read_lock()ed by nf_hook_slow */ ++ help = nfct_help(ct); ++ if (help && rcu_dereference(help->helper)) ++ return 0; ++ ++ if ((FROM_GE_WAN(skb) || FROM_GE_LAN(skb)) && ++ skb_hnat_is_hashed(skb) && ++ (skb_hnat_reason(skb) == HIT_BIND_KEEPALIVE_DUP_OLD_HDR)) ++ return -1; ++ ++ if ((IS_LAN(out) && FROM_GE_WAN(skb)) || ++ (IS_WAN(out) && FROM_GE_LAN(skb))) { ++ if (!skb_hnat_is_hashed(skb)) ++ return 0; ++ ++ entry = &host->foe_table_cpu[skb_hnat_entry(skb)]; ++ if (entry_hnat_is_bound(entry)) ++ return 0; ++ ++ if (skb_hnat_reason(skb) == HIT_UNBIND_RATE_REACH && ++ skb_hnat_alg(skb) == 0) { ++ if (fn && fn(skb, out)) ++ return 0; ++ skb_to_hnat_info(skb, out, entry); ++ } ++ } ++ ++ return 0; ++} ++ ++static unsigned int mtk_hnat_nf_pre_routing(void *priv, ++ struct sk_buff *skb, ++ const struct nf_hook_state *state) ++{ ++ if (IS_WAN(state->in)) ++ HNAT_SKB_CB(skb)->iif = FOE_MAGIC_GE_WAN; ++ else if (IS_LAN(state->in)) ++ HNAT_SKB_CB(skb)->iif = FOE_MAGIC_GE_LAN; ++ else if (!IS_BR(state->in)) ++ HNAT_SKB_CB(skb)->iif = FOE_INVALID; ++ ++ return NF_ACCEPT; ++} ++ ++static unsigned int hnat_get_nexthop(struct sk_buff *skb, const struct net_device *out) { ++ ++ u32 nexthop; ++ struct neighbour *neigh; ++ struct dst_entry *dst = skb_dst(skb); ++ struct rtable *rt = (struct rtable *)dst; ++ struct net_device *dev = (__force struct net_device *)out; ++ ++ rcu_read_lock_bh(); ++ nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr); ++ neigh = __ipv4_neigh_lookup_noref(dev, nexthop); ++ if (unlikely(!neigh)) { ++ dev_err(host->dev, "%s:++ no neigh\n", __func__); ++ return -1; ++ } ++ ++ /* why do we get all zero ethernet address ? */ ++ if (!is_valid_ether_addr(neigh->ha)){ ++ rcu_read_unlock_bh(); ++ return -1; ++ } ++ ++ memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN); ++ memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN); ++ ++ rcu_read_unlock_bh(); ++ ++ return 0; ++} ++ ++static unsigned int mtk_hnat_ipv4_nf_post_routing(void *priv, ++ struct sk_buff *skb, ++ const struct nf_hook_state *state) ++{ ++ if (!mtk_hnat_nf_post_routing(skb, state->out, hnat_get_nexthop, __func__)) ++ return NF_ACCEPT; ++ ++ return NF_DROP; ++} ++ ++static unsigned int mtk_hnat_br_nf_post_routing(void *priv, ++ struct sk_buff *skb, ++ const struct nf_hook_state *state) ++{ ++ if (!mtk_hnat_nf_post_routing(skb, state->out , 0, __func__)) ++ return NF_ACCEPT; ++ ++ return NF_DROP; ++} ++ ++static struct nf_hook_ops mtk_hnat_nf_ops[] __read_mostly = { ++ { ++ .hook = mtk_hnat_nf_pre_routing, ++ .pf = NFPROTO_IPV4, ++ .hooknum = NF_INET_PRE_ROUTING, ++ .priority = NF_IP_PRI_FIRST, ++ }, { ++ .hook = mtk_hnat_ipv4_nf_post_routing, ++ .pf = NFPROTO_IPV4, ++ .hooknum = NF_INET_POST_ROUTING, ++ .priority = NF_IP_PRI_LAST, ++ }, { ++ .hook = mtk_hnat_nf_pre_routing, ++ .pf = NFPROTO_BRIDGE, ++ .hooknum = NF_BR_PRE_ROUTING, ++ .priority = NF_BR_PRI_FIRST, ++ }, { ++ .hook = mtk_hnat_br_nf_post_routing, ++ .pf = NFPROTO_BRIDGE, ++ .hooknum = NF_BR_POST_ROUTING, ++ .priority = NF_BR_PRI_LAST - 1, ++ }, ++}; ++ ++/* ++int hnat_register_nf_hooks(void) ++{ ++ return nf_register_hooks(mtk_hnat_nf_ops, ++ ARRAY_SIZE(mtk_hnat_nf_ops)); ++} ++ ++void hnat_unregister_nf_hooks(void) ++{ ++ nf_unregister_hooks(mtk_hnat_nf_ops, ++ ARRAY_SIZE(mtk_hnat_nf_ops)); ++} ++*/ ++int hnat_register_nf_hooks(struct net *net) ++{ ++ return nf_register_net_hooks(net,mtk_hnat_nf_ops, ++ ARRAY_SIZE(mtk_hnat_nf_ops)); ++} ++ ++void hnat_unregister_nf_hooks(struct net *net) ++{ ++ nf_unregister_net_hooks(net,mtk_hnat_nf_ops, ++ ARRAY_SIZE(mtk_hnat_nf_ops)); ++} +diff --git a/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h b/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h +new file mode 100644 +index 0000000000000..e19812840a840 +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h +@@ -0,0 +1,44 @@ ++/* This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Copyright (C) 2014-2016 Sean Wang ++ * Copyright (C) 2016-2017 John Crispin ++ */ ++ ++#ifndef NF_HNAT_MTK_H ++#define NF_HNAT_MTK_H ++ ++#include ++#include ++ ++#define HNAT_SKB_CB2(__skb) ((struct hnat_skb_cb2 *)&((__skb)->cb[44])) ++struct hnat_skb_cb2 { ++ __u32 magic; ++}; ++ ++struct hnat_desc { ++ u32 entry:14; ++ u32 crsn:5; ++ u32 sport:4; ++ u32 alg:9; ++} __attribute__ ((packed)); ++ ++#define skb_hnat_magic(skb) (((struct hnat_desc *)(skb->head))->magic) ++#define skb_hnat_reason(skb) (((struct hnat_desc *)(skb->head))->crsn) ++#define skb_hnat_entry(skb) (((struct hnat_desc *)(skb->head))->entry) ++#define skb_hnat_sport(skb) (((struct hnat_desc *)(skb->head))->sport) ++#define skb_hnat_alg(skb) (((struct hnat_desc *)(skb->head))->alg) ++ ++u32 hnat_tx(struct sk_buff *skb); ++u32 hnat_set_skb_info(struct sk_buff *skb, u32 *rxd); ++u32 hnat_reg(struct net_device *, void __iomem *); ++u32 hnat_unreg(void); ++ ++#endif ++ +diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c +index 8febdd48b9f16..9bcc787f8abf3 100644 +--- a/net/netfilter/nf_conntrack_proto_tcp.c ++++ b/net/netfilter/nf_conntrack_proto_tcp.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -20,6 +21,7 @@ + #include + #include + ++#include + #include + + #include +@@ -51,6 +53,11 @@ static int nf_ct_tcp_max_retrans __read_mostly = 3; + /* FIXME: Examine ipfilter's timeouts and conntrack transitions more + closely. They're more complex. --RR */ + ++#ifndef IPV4_DEVCONF_DFLT ++ #define IPV4_DEVCONF_DFLT(net, attr) \ ++ IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) ++#endif ++ + static const char *const tcp_conntrack_names[] = { + "NONE", + "SYN_SENT", +@@ -506,6 +513,18 @@ static bool tcp_in_window(const struct nf_conn *ct, + s32 receiver_offset; + bool res, in_recv_win; + ++ if (net) { ++ if ((net->ipv4.devconf_all && net->ipv4.devconf_dflt && net->ipv6.devconf_all) && ++ net->ipv6.devconf_dflt) { ++ if ((IPV4_DEVCONF_DFLT(net, FORWARDING) || ++ IPV4_DEVCONF_ALL(net, FORWARDING)) || ++ (net->ipv6.devconf_all->forwarding || ++ net->ipv6.devconf_dflt->forwarding)) { ++ return true; ++ } ++ } ++ } ++ + /* + * Get the required data from the packet. + */