mirror of
https://github.com/Ysurac/openmptcprouter.git
synced 2025-02-13 20:01:55 +00:00
1824 lines
54 KiB
Diff
1824 lines
54 KiB
Diff
From b3ba09aea285ecdac5228805411f1e7a6c68382a Mon Sep 17 00:00:00 2001
|
|
From: Frank Wunderlich <frank-w@public-files.de>
|
|
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 <linux/tcp.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
+#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 <sean.wang@mediatek.com>
|
|
+ * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org>
|
|
+ */
|
|
+
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/if.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/reset.h>
|
|
+
|
|
+#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 <sean.wang@mediatek.com>");
|
|
+MODULE_AUTHOR("John Crispin <john@phrozen.org>");
|
|
+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 <sean.wang@mediatek.com>
|
|
+ * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org>
|
|
+ */
|
|
+
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/if.h>
|
|
+#include <linux/if_ether.h>
|
|
+#include <net/net_namespace.h>
|
|
+/*--------------------------------------------------------------------------*/
|
|
+/* 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 <sean.wang@mediatek.com>
|
|
+ * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org>
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+
|
|
+#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 <sean.wang@mediatek.com>
|
|
+ * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org>
|
|
+ */
|
|
+
|
|
+#include <linux/netfilter_bridge.h>
|
|
+
|
|
+#include <linux/of.h>
|
|
+#include <net/arp.h>
|
|
+#include <net/neighbour.h>
|
|
+#include <net/netfilter/nf_conntrack_helper.h>
|
|
+#include <net/net_namespace.h>
|
|
+
|
|
+#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 <sean.wang@mediatek.com>
|
|
+ * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org>
|
|
+ */
|
|
+
|
|
+#ifndef NF_HNAT_MTK_H
|
|
+#define NF_HNAT_MTK_H
|
|
+
|
|
+#include <asm/dma-mapping.h>
|
|
+#include <linux/netdevice.h>
|
|
+
|
|
+#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 <linux/types.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/module.h>
|
|
+#include <linux/inetdevice.h>
|
|
#include <linux/in.h>
|
|
#include <net/ip.h>
|
|
#include <linux/tcp.h>
|
|
@@ -20,6 +21,7 @@
|
|
#include <net/ip6_checksum.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
+#include <net/ip.h>
|
|
#include <net/tcp.h>
|
|
|
|
#include <linux/netfilter.h>
|
|
@@ -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.
|
|
*/
|