kernel: update flow offload patches to upstream version
authorFelix Fietkau <nbd@nbd.name>
Sat, 10 Apr 2021 11:20:04 +0000 (13:20 +0200)
committerFelix Fietkau <nbd@nbd.name>
Sat, 10 Apr 2021 14:14:34 +0000 (16:14 +0200)
Move patches to backport-5.10, since the series was accepted upstream

Signed-off-by: Felix Fietkau <nbd@nbd.name>
74 files changed:
target/linux/generic/backport-5.10/610-v5.13-00-netfilter-flowtable-add-hash-offset-field-to-tuple.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-01-netfilter-flowtable-separate-replace-destroy-and-sta.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-02-netfilter-Fix-fall-through-warnings-for-Clang.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-03-netfilter-conntrack-Remove-unused-variable-declarati.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-04-netfilter-flowtable-consolidate-skb_try_make_writabl.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-05-netfilter-flowtable-move-skb_try_make_writable-befor.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-06-netfilter-flowtable-move-FLOW_OFFLOAD_DIR_MAX-away-f.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-07-netfilter-flowtable-fast-NAT-functions-never-fail.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-08-netfilter-flowtable-call-dst_check-to-fall-back-to-c.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-09-netfilter-flowtable-refresh-timeout-after-dst-and-wr.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-10-netfilter-nftables-update-table-flags-from-the-commi.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-11-net-resolve-forwarding-path-from-virtual-netdevice-a.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-12-net-8021q-resolve-forwarding-path-for-vlan-devices.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-13-net-bridge-resolve-forwarding-path-for-bridge-device.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-14-net-bridge-resolve-forwarding-path-for-VLAN-tag-acti.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-15-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-16-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-17-netfilter-flowtable-add-xmit-path-types.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-18-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-19-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-20-netfilter-flowtable-add-vlan-support.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-21-netfilter-flowtable-add-bridge-vlan-filtering-suppor.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-22-netfilter-flowtable-add-pppoe-support.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-23-netfilter-flowtable-add-dsa-support.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-24-selftests-netfilter-flowtable-bridge-and-vlan-suppor.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-25-netfilter-flowtable-add-offload-support-for-xmit-pat.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-26-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-27-netfilter-flowtable-bridge-vlan-hardware-offload-and.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-28-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-29-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-30-dsa-slave-add-support-for-TC_SETUP_FT.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-31-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-32-net-ethernet-mtk_eth_soc-add-support-for-initializin.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-33-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/610-v5.13-34-docs-nf_flowtable-update-documentation-with-enhancem.patch [new file with mode: 0644]
target/linux/generic/backport-5.10/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch
target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch
target/linux/generic/backport-5.10/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch
target/linux/generic/backport-5.10/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch
target/linux/generic/backport-5.10/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch
target/linux/generic/hack-5.10/650-netfilter-add-xt_FLOWOFFLOAD-target.patch
target/linux/generic/pending-5.10/640-00-netfilter-flowtable-add-hash-offset-field-to-tuple.patch [deleted file]
target/linux/generic/pending-5.10/640-01-netfilter-flowtable-add-xmit-path-types.patch [deleted file]
target/linux/generic/pending-5.10/640-02-net-resolve-forwarding-path-from-virtual-netdevice-a.patch [deleted file]
target/linux/generic/pending-5.10/640-03-net-8021q-resolve-forwarding-path-for-vlan-devices.patch [deleted file]
target/linux/generic/pending-5.10/640-04-bridge-resolve-forwarding-path-for-bridge-devices.patch [deleted file]
target/linux/generic/pending-5.10/640-05-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch [deleted file]
target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch [deleted file]
target/linux/generic/pending-5.10/640-07-netfilter-flowtable-add-vlan-support.patch [deleted file]
target/linux/generic/pending-5.10/640-08-selftests-netfilter-flowtable-bridge-and-VLAN-suppor.patch [deleted file]
target/linux/generic/pending-5.10/640-09-net-bridge-resolve-VLAN-tag-actions-in-forwarding-pa.patch [deleted file]
target/linux/generic/pending-5.10/640-10-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch [deleted file]
target/linux/generic/pending-5.10/640-11-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch [deleted file]
target/linux/generic/pending-5.10/640-12-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch [deleted file]
target/linux/generic/pending-5.10/640-13-netfilter-flowtable-add-pppoe-support.patch [deleted file]
target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-dsa-support.patch [deleted file]
target/linux/generic/pending-5.10/640-15-netfilter-flowtable-add-offload-support-for-xmit-pat.patch [deleted file]
target/linux/generic/pending-5.10/640-16-dsa-slave-add-support-for-TC_SETUP_FT.patch [deleted file]
target/linux/generic/pending-5.10/640-17-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch [deleted file]
target/linux/generic/pending-5.10/640-18-netfilter-nf_flow_table-fix-untagging-with-hardware-.patch [deleted file]
target/linux/generic/pending-5.10/640-19-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch [deleted file]
target/linux/generic/pending-5.10/640-20-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch [deleted file]
target/linux/generic/pending-5.10/770-00-net-ethernet-mtk_eth_soc-use-napi_consume_skb.patch
target/linux/generic/pending-5.10/770-01-net-ethernet-mtk_eth_soc-significantly-reduce-mdio-b.patch
target/linux/generic/pending-5.10/770-02-net-ethernet-mtk_eth_soc-fix-rx-vlan-offload.patch
target/linux/generic/pending-5.10/770-03-net-ethernet-mtk_eth_soc-fix-unnecessary-tx-queue-st.patch
target/linux/generic/pending-5.10/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch
target/linux/generic/pending-5.10/770-05-net-ethernet-mtk_eth_soc-increase-DMA-ring-sizes.patch
target/linux/generic/pending-5.10/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch
target/linux/generic/pending-5.10/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch
target/linux/generic/pending-5.10/770-09-net-ethernet-mtk_eth_soc-only-read-the-full-rx-descr.patch
target/linux/generic/pending-5.10/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch [deleted file]
target/linux/generic/pending-5.10/770-15-net-ethernet-mtk_eth_soc-add-support-for-initializin.patch [deleted file]
target/linux/generic/pending-5.10/770-16-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch [deleted file]

diff --git a/target/linux/generic/backport-5.10/610-v5.13-00-netfilter-flowtable-add-hash-offset-field-to-tuple.patch b/target/linux/generic/backport-5.10/610-v5.13-00-netfilter-flowtable-add-hash-offset-field-to-tuple.patch
new file mode 100644 (file)
index 0000000..c881ccf
--- /dev/null
@@ -0,0 +1,52 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Fri, 20 Nov 2020 13:49:13 +0100
+Subject: [PATCH] netfilter: flowtable: add hash offset field to tuple
+
+Add a placeholder field to calculate hash tuple offset. Similar to
+2c407aca6497 ("netfilter: conntrack: avoid gcc-10 zero-length-bounds
+warning").
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -107,6 +107,10 @@ struct flow_offload_tuple {
+       u8                              l3proto;
+       u8                              l4proto;
++
++      /* All members above are keys for lookups, see flow_offload_hash(). */
++      struct { }                      __hash;
++
+       u8                              dir;
+       u16                             mtu;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -191,14 +191,14 @@ static u32 flow_offload_hash(const void
+ {
+       const struct flow_offload_tuple *tuple = data;
+-      return jhash(tuple, offsetof(struct flow_offload_tuple, dir), seed);
++      return jhash(tuple, offsetof(struct flow_offload_tuple, __hash), seed);
+ }
+ static u32 flow_offload_hash_obj(const void *data, u32 len, u32 seed)
+ {
+       const struct flow_offload_tuple_rhash *tuplehash = data;
+-      return jhash(&tuplehash->tuple, offsetof(struct flow_offload_tuple, dir), seed);
++      return jhash(&tuplehash->tuple, offsetof(struct flow_offload_tuple, __hash), seed);
+ }
+ static int flow_offload_hash_cmp(struct rhashtable_compare_arg *arg,
+@@ -207,7 +207,7 @@ static int flow_offload_hash_cmp(struct
+       const struct flow_offload_tuple *tuple = arg->key;
+       const struct flow_offload_tuple_rhash *x = ptr;
+-      if (memcmp(&x->tuple, tuple, offsetof(struct flow_offload_tuple, dir)))
++      if (memcmp(&x->tuple, tuple, offsetof(struct flow_offload_tuple, __hash)))
+               return 1;
+       return 0;
diff --git a/target/linux/generic/backport-5.10/610-v5.13-01-netfilter-flowtable-separate-replace-destroy-and-sta.patch b/target/linux/generic/backport-5.10/610-v5.13-01-netfilter-flowtable-separate-replace-destroy-and-sta.patch
new file mode 100644 (file)
index 0000000..86184dd
--- /dev/null
@@ -0,0 +1,98 @@
+From: Oz Shlomo <ozsh@nvidia.com>
+Date: Tue, 23 Mar 2021 00:56:19 +0100
+Subject: [PATCH] netfilter: flowtable: separate replace, destroy and
+ stats to different workqueues
+
+Currently the flow table offload replace, destroy and stats work items are
+executed on a single workqueue. As such, DESTROY and STATS commands may
+be backloged after a burst of REPLACE work items. This scenario can bloat
+up memory and may cause active connections to age.
+
+Instatiate add, del and stats workqueues to avoid backlogs of non-dependent
+actions. Provide sysfs control over the workqueue attributes, allowing
+userspace applications to control the workqueue cpumask.
+
+Signed-off-by: Oz Shlomo <ozsh@nvidia.com>
+Reviewed-by: Paul Blakey <paulb@nvidia.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -13,7 +13,9 @@
+ #include <net/netfilter/nf_conntrack_core.h>
+ #include <net/netfilter/nf_conntrack_tuple.h>
+-static struct workqueue_struct *nf_flow_offload_wq;
++static struct workqueue_struct *nf_flow_offload_add_wq;
++static struct workqueue_struct *nf_flow_offload_del_wq;
++static struct workqueue_struct *nf_flow_offload_stats_wq;
+ struct flow_offload_work {
+       struct list_head        list;
+@@ -826,7 +828,12 @@ static void flow_offload_work_handler(st
+ static void flow_offload_queue_work(struct flow_offload_work *offload)
+ {
+-      queue_work(nf_flow_offload_wq, &offload->work);
++      if (offload->cmd == FLOW_CLS_REPLACE)
++              queue_work(nf_flow_offload_add_wq, &offload->work);
++      else if (offload->cmd == FLOW_CLS_DESTROY)
++              queue_work(nf_flow_offload_del_wq, &offload->work);
++      else
++              queue_work(nf_flow_offload_stats_wq, &offload->work);
+ }
+ static struct flow_offload_work *
+@@ -898,8 +905,11 @@ void nf_flow_offload_stats(struct nf_flo
+ void nf_flow_table_offload_flush(struct nf_flowtable *flowtable)
+ {
+-      if (nf_flowtable_hw_offload(flowtable))
+-              flush_workqueue(nf_flow_offload_wq);
++      if (nf_flowtable_hw_offload(flowtable)) {
++              flush_workqueue(nf_flow_offload_add_wq);
++              flush_workqueue(nf_flow_offload_del_wq);
++              flush_workqueue(nf_flow_offload_stats_wq);
++      }
+ }
+ static int nf_flow_table_block_setup(struct nf_flowtable *flowtable,
+@@ -1011,15 +1021,33 @@ EXPORT_SYMBOL_GPL(nf_flow_table_offload_
+ int nf_flow_table_offload_init(void)
+ {
+-      nf_flow_offload_wq  = alloc_workqueue("nf_flow_table_offload",
+-                                            WQ_UNBOUND, 0);
+-      if (!nf_flow_offload_wq)
++      nf_flow_offload_add_wq  = alloc_workqueue("nf_ft_offload_add",
++                                                WQ_UNBOUND | WQ_SYSFS, 0);
++      if (!nf_flow_offload_add_wq)
+               return -ENOMEM;
++      nf_flow_offload_del_wq  = alloc_workqueue("nf_ft_offload_del",
++                                                WQ_UNBOUND | WQ_SYSFS, 0);
++      if (!nf_flow_offload_del_wq)
++              goto err_del_wq;
++
++      nf_flow_offload_stats_wq  = alloc_workqueue("nf_ft_offload_stats",
++                                                  WQ_UNBOUND | WQ_SYSFS, 0);
++      if (!nf_flow_offload_stats_wq)
++              goto err_stats_wq;
++
+       return 0;
++
++err_stats_wq:
++      destroy_workqueue(nf_flow_offload_del_wq);
++err_del_wq:
++      destroy_workqueue(nf_flow_offload_add_wq);
++      return -ENOMEM;
+ }
+ void nf_flow_table_offload_exit(void)
+ {
+-      destroy_workqueue(nf_flow_offload_wq);
++      destroy_workqueue(nf_flow_offload_add_wq);
++      destroy_workqueue(nf_flow_offload_del_wq);
++      destroy_workqueue(nf_flow_offload_stats_wq);
+ }
diff --git a/target/linux/generic/backport-5.10/610-v5.13-02-netfilter-Fix-fall-through-warnings-for-Clang.patch b/target/linux/generic/backport-5.10/610-v5.13-02-netfilter-Fix-fall-through-warnings-for-Clang.patch
new file mode 100644 (file)
index 0000000..b5776ab
--- /dev/null
@@ -0,0 +1,44 @@
+From: "Gustavo A. R. Silva" <gustavoars@kernel.org>
+Date: Tue, 23 Mar 2021 00:56:20 +0100
+Subject: [PATCH] netfilter: Fix fall-through warnings for Clang
+
+In preparation to enable -Wimplicit-fallthrough for Clang, fix multiple
+warnings by explicitly adding multiple break statements instead of just
+letting the code fall through to the next case.
+
+Link: https://github.com/KSPP/linux/issues/115
+Acked-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_conntrack_proto_dccp.c
++++ b/net/netfilter/nf_conntrack_proto_dccp.c
+@@ -397,6 +397,7 @@ dccp_new(struct nf_conn *ct, const struc
+                       msg = "not picking up existing connection ";
+                       goto out_invalid;
+               }
++              break;
+       case CT_DCCP_REQUEST:
+               break;
+       case CT_DCCP_INVALID:
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -8364,6 +8364,7 @@ static int nf_tables_check_loops(const s
+                                                       data->verdict.chain);
+                               if (err < 0)
+                                       return err;
++                              break;
+                       default:
+                               break;
+                       }
+--- a/net/netfilter/nft_ct.c
++++ b/net/netfilter/nft_ct.c
+@@ -528,6 +528,7 @@ static void __nft_ct_set_destroy(const s
+       case NFT_CT_ZONE:
+               if (--nft_ct_pcpu_template_refcnt == 0)
+                       nft_ct_tmpl_put_pcpu();
++              break;
+ #endif
+       default:
+               break;
diff --git a/target/linux/generic/backport-5.10/610-v5.13-03-netfilter-conntrack-Remove-unused-variable-declarati.patch b/target/linux/generic/backport-5.10/610-v5.13-03-netfilter-conntrack-Remove-unused-variable-declarati.patch
new file mode 100644 (file)
index 0000000..37e80d9
--- /dev/null
@@ -0,0 +1,22 @@
+From: YueHaibing <yuehaibing@huawei.com>
+Date: Tue, 23 Mar 2021 00:56:21 +0100
+Subject: [PATCH] netfilter: conntrack: Remove unused variable
+ declaration
+
+commit e97c3e278e95 ("tproxy: split off ipv6 defragmentation to a separate
+module") left behind this.
+
+Signed-off-by: YueHaibing <yuehaibing@huawei.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h
++++ b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h
+@@ -4,7 +4,4 @@
+ extern const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6;
+-#include <linux/sysctl.h>
+-extern struct ctl_table nf_ct_ipv6_sysctl_table[];
+-
+ #endif /* _NF_CONNTRACK_IPV6_H*/
diff --git a/target/linux/generic/backport-5.10/610-v5.13-04-netfilter-flowtable-consolidate-skb_try_make_writabl.patch b/target/linux/generic/backport-5.10/610-v5.13-04-netfilter-flowtable-consolidate-skb_try_make_writabl.patch
new file mode 100644 (file)
index 0000000..2a4ecba
--- /dev/null
@@ -0,0 +1,291 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:22 +0100
+Subject: [PATCH] netfilter: flowtable: consolidate
+ skb_try_make_writable() call
+
+Fetch the layer 4 header size to be mangled by NAT when building the
+tuple, then use it to make writable the network and the transport
+headers. After this update, the NAT routines now assumes that the skbuff
+area is writable. Do the pointer refetch only after the single
+skb_try_make_writable() call.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -395,9 +395,6 @@ static int nf_flow_nat_port_tcp(struct s
+ {
+       struct tcphdr *tcph;
+-      if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
+-              return -1;
+-
+       tcph = (void *)(skb_network_header(skb) + thoff);
+       inet_proto_csum_replace2(&tcph->check, skb, port, new_port, false);
+@@ -409,9 +406,6 @@ static int nf_flow_nat_port_udp(struct s
+ {
+       struct udphdr *udph;
+-      if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
+-              return -1;
+-
+       udph = (void *)(skb_network_header(skb) + thoff);
+       if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+               inet_proto_csum_replace2(&udph->check, skb, port,
+@@ -447,9 +441,6 @@ int nf_flow_snat_port(const struct flow_
+       struct flow_ports *hdr;
+       __be16 port, new_port;
+-      if (skb_try_make_writable(skb, thoff + sizeof(*hdr)))
+-              return -1;
+-
+       hdr = (void *)(skb_network_header(skb) + thoff);
+       switch (dir) {
+@@ -478,9 +469,6 @@ int nf_flow_dnat_port(const struct flow_
+       struct flow_ports *hdr;
+       __be16 port, new_port;
+-      if (skb_try_make_writable(skb, thoff + sizeof(*hdr)))
+-              return -1;
+-
+       hdr = (void *)(skb_network_header(skb) + thoff);
+       switch (dir) {
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -39,9 +39,6 @@ static int nf_flow_nat_ip_tcp(struct sk_
+ {
+       struct tcphdr *tcph;
+-      if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
+-              return -1;
+-
+       tcph = (void *)(skb_network_header(skb) + thoff);
+       inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, true);
+@@ -53,9 +50,6 @@ static int nf_flow_nat_ip_udp(struct sk_
+ {
+       struct udphdr *udph;
+-      if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
+-              return -1;
+-
+       udph = (void *)(skb_network_header(skb) + thoff);
+       if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+               inet_proto_csum_replace4(&udph->check, skb, addr,
+@@ -136,19 +130,17 @@ static int nf_flow_dnat_ip(const struct
+ }
+ static int nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
+-                        unsigned int thoff, enum flow_offload_tuple_dir dir)
++                        unsigned int thoff, enum flow_offload_tuple_dir dir,
++                        struct iphdr *iph)
+ {
+-      struct iphdr *iph = ip_hdr(skb);
+-
+       if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
+           (nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
+-           nf_flow_snat_ip(flow, skb, ip_hdr(skb), thoff, dir) < 0))
++           nf_flow_snat_ip(flow, skb, iph, thoff, dir) < 0))
+               return -1;
+-      iph = ip_hdr(skb);
+       if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
+           (nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
+-           nf_flow_dnat_ip(flow, skb, ip_hdr(skb), thoff, dir) < 0))
++           nf_flow_dnat_ip(flow, skb, iph, thoff, dir) < 0))
+               return -1;
+       return 0;
+@@ -160,10 +152,10 @@ static bool ip_has_options(unsigned int
+ }
+ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
+-                          struct flow_offload_tuple *tuple)
++                          struct flow_offload_tuple *tuple, u32 *hdrsize)
+ {
+-      unsigned int thoff, hdrsize;
+       struct flow_ports *ports;
++      unsigned int thoff;
+       struct iphdr *iph;
+       if (!pskb_may_pull(skb, sizeof(*iph)))
+@@ -178,10 +170,10 @@ static int nf_flow_tuple_ip(struct sk_bu
+       switch (iph->protocol) {
+       case IPPROTO_TCP:
+-              hdrsize = sizeof(struct tcphdr);
++              *hdrsize = sizeof(struct tcphdr);
+               break;
+       case IPPROTO_UDP:
+-              hdrsize = sizeof(struct udphdr);
++              *hdrsize = sizeof(struct udphdr);
+               break;
+       default:
+               return -1;
+@@ -191,7 +183,7 @@ static int nf_flow_tuple_ip(struct sk_bu
+               return -1;
+       thoff = iph->ihl * 4;
+-      if (!pskb_may_pull(skb, thoff + hdrsize))
++      if (!pskb_may_pull(skb, thoff + *hdrsize))
+               return -1;
+       iph = ip_hdr(skb);
+@@ -252,11 +244,12 @@ nf_flow_offload_ip_hook(void *priv, stru
+       unsigned int thoff;
+       struct iphdr *iph;
+       __be32 nexthop;
++      u32 hdrsize;
+       if (skb->protocol != htons(ETH_P_IP))
+               return NF_ACCEPT;
+-      if (nf_flow_tuple_ip(skb, state->in, &tuple) < 0)
++      if (nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize) < 0)
+               return NF_ACCEPT;
+       tuplehash = flow_offload_lookup(flow_table, &tuple);
+@@ -271,11 +264,13 @@ nf_flow_offload_ip_hook(void *priv, stru
+       if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
+               return NF_ACCEPT;
+-      if (skb_try_make_writable(skb, sizeof(*iph)))
++      iph = ip_hdr(skb);
++      thoff = iph->ihl * 4;
++      if (skb_try_make_writable(skb, thoff + hdrsize))
+               return NF_DROP;
+-      thoff = ip_hdr(skb)->ihl * 4;
+-      if (nf_flow_state_check(flow, ip_hdr(skb)->protocol, skb, thoff))
++      iph = ip_hdr(skb);
++      if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+               return NF_ACCEPT;
+       flow_offload_refresh(flow_table, flow);
+@@ -285,10 +280,9 @@ nf_flow_offload_ip_hook(void *priv, stru
+               return NF_ACCEPT;
+       }
+-      if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
++      if (nf_flow_nat_ip(flow, skb, thoff, dir, iph) < 0)
+               return NF_DROP;
+-      iph = ip_hdr(skb);
+       ip_decrease_ttl(iph);
+       skb->tstamp = 0;
+@@ -317,9 +311,6 @@ static int nf_flow_nat_ipv6_tcp(struct s
+ {
+       struct tcphdr *tcph;
+-      if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
+-              return -1;
+-
+       tcph = (void *)(skb_network_header(skb) + thoff);
+       inet_proto_csum_replace16(&tcph->check, skb, addr->s6_addr32,
+                                 new_addr->s6_addr32, true);
+@@ -333,9 +324,6 @@ static int nf_flow_nat_ipv6_udp(struct s
+ {
+       struct udphdr *udph;
+-      if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
+-              return -1;
+-
+       udph = (void *)(skb_network_header(skb) + thoff);
+       if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+               inet_proto_csum_replace16(&udph->check, skb, addr->s6_addr32,
+@@ -417,31 +405,30 @@ static int nf_flow_dnat_ipv6(const struc
+ static int nf_flow_nat_ipv6(const struct flow_offload *flow,
+                           struct sk_buff *skb,
+-                          enum flow_offload_tuple_dir dir)
++                          enum flow_offload_tuple_dir dir,
++                          struct ipv6hdr *ip6h)
+ {
+-      struct ipv6hdr *ip6h = ipv6_hdr(skb);
+       unsigned int thoff = sizeof(*ip6h);
+       if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
+           (nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
+-           nf_flow_snat_ipv6(flow, skb, ipv6_hdr(skb), thoff, dir) < 0))
++           nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
+               return -1;
+-      ip6h = ipv6_hdr(skb);
+       if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
+           (nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
+-           nf_flow_dnat_ipv6(flow, skb, ipv6_hdr(skb), thoff, dir) < 0))
++           nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
+               return -1;
+       return 0;
+ }
+ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
+-                            struct flow_offload_tuple *tuple)
++                            struct flow_offload_tuple *tuple, u32 *hdrsize)
+ {
+-      unsigned int thoff, hdrsize;
+       struct flow_ports *ports;
+       struct ipv6hdr *ip6h;
++      unsigned int thoff;
+       if (!pskb_may_pull(skb, sizeof(*ip6h)))
+               return -1;
+@@ -450,10 +437,10 @@ static int nf_flow_tuple_ipv6(struct sk_
+       switch (ip6h->nexthdr) {
+       case IPPROTO_TCP:
+-              hdrsize = sizeof(struct tcphdr);
++              *hdrsize = sizeof(struct tcphdr);
+               break;
+       case IPPROTO_UDP:
+-              hdrsize = sizeof(struct udphdr);
++              *hdrsize = sizeof(struct udphdr);
+               break;
+       default:
+               return -1;
+@@ -463,7 +450,7 @@ static int nf_flow_tuple_ipv6(struct sk_
+               return -1;
+       thoff = sizeof(*ip6h);
+-      if (!pskb_may_pull(skb, thoff + hdrsize))
++      if (!pskb_may_pull(skb, thoff + *hdrsize))
+               return -1;
+       ip6h = ipv6_hdr(skb);
+@@ -493,11 +480,12 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       struct net_device *outdev;
+       struct ipv6hdr *ip6h;
+       struct rt6_info *rt;
++      u32 hdrsize;
+       if (skb->protocol != htons(ETH_P_IPV6))
+               return NF_ACCEPT;
+-      if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0)
++      if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &hdrsize) < 0)
+               return NF_ACCEPT;
+       tuplehash = flow_offload_lookup(flow_table, &tuple);
+@@ -523,13 +511,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
+               return NF_ACCEPT;
+       }
+-      if (skb_try_make_writable(skb, sizeof(*ip6h)))
++      if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
+               return NF_DROP;
+-      if (nf_flow_nat_ipv6(flow, skb, dir) < 0)
++      ip6h = ipv6_hdr(skb);
++      if (nf_flow_nat_ipv6(flow, skb, dir, ip6h) < 0)
+               return NF_DROP;
+-      ip6h = ipv6_hdr(skb);
+       ip6h->hop_limit--;
+       skb->tstamp = 0;
diff --git a/target/linux/generic/backport-5.10/610-v5.13-05-netfilter-flowtable-move-skb_try_make_writable-befor.patch b/target/linux/generic/backport-5.10/610-v5.13-05-netfilter-flowtable-move-skb_try_make_writable-befor.patch
new file mode 100644 (file)
index 0000000..84e294d
--- /dev/null
@@ -0,0 +1,35 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:23 +0100
+Subject: [PATCH] netfilter: flowtable: move skb_try_make_writable()
+ before NAT in IPv4
+
+For consistency with the IPv6 flowtable datapath and to make sure the
+skbuff is writable right before the NAT header updates.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -266,10 +266,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+       iph = ip_hdr(skb);
+       thoff = iph->ihl * 4;
+-      if (skb_try_make_writable(skb, thoff + hdrsize))
+-              return NF_DROP;
+-
+-      iph = ip_hdr(skb);
+       if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+               return NF_ACCEPT;
+@@ -280,6 +276,10 @@ nf_flow_offload_ip_hook(void *priv, stru
+               return NF_ACCEPT;
+       }
++      if (skb_try_make_writable(skb, thoff + hdrsize))
++              return NF_DROP;
++
++      iph = ip_hdr(skb);
+       if (nf_flow_nat_ip(flow, skb, thoff, dir, iph) < 0)
+               return NF_DROP;
diff --git a/target/linux/generic/backport-5.10/610-v5.13-06-netfilter-flowtable-move-FLOW_OFFLOAD_DIR_MAX-away-f.patch b/target/linux/generic/backport-5.10/610-v5.13-06-netfilter-flowtable-move-FLOW_OFFLOAD_DIR_MAX-away-f.patch
new file mode 100644 (file)
index 0000000..473647d
--- /dev/null
@@ -0,0 +1,82 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:24 +0100
+Subject: [PATCH] netfilter: flowtable: move FLOW_OFFLOAD_DIR_MAX away
+ from enumeration
+
+This allows to remove the default case which should not ever happen and
+that was added to avoid gcc warnings on unhandled FLOW_OFFLOAD_DIR_MAX
+enumeration case.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -86,8 +86,8 @@ static inline bool nf_flowtable_hw_offlo
+ enum flow_offload_tuple_dir {
+       FLOW_OFFLOAD_DIR_ORIGINAL = IP_CT_DIR_ORIGINAL,
+       FLOW_OFFLOAD_DIR_REPLY = IP_CT_DIR_REPLY,
+-      FLOW_OFFLOAD_DIR_MAX = IP_CT_DIR_MAX
+ };
++#define FLOW_OFFLOAD_DIR_MAX  IP_CT_DIR_MAX
+ struct flow_offload_tuple {
+       union {
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -454,8 +454,6 @@ int nf_flow_snat_port(const struct flow_
+               new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port;
+               hdr->dest = new_port;
+               break;
+-      default:
+-              return -1;
+       }
+       return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
+@@ -482,8 +480,6 @@ int nf_flow_dnat_port(const struct flow_
+               new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_port;
+               hdr->source = new_port;
+               break;
+-      default:
+-              return -1;
+       }
+       return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -96,8 +96,6 @@ static int nf_flow_snat_ip(const struct
+               new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v4.s_addr;
+               iph->daddr = new_addr;
+               break;
+-      default:
+-              return -1;
+       }
+       csum_replace4(&iph->check, addr, new_addr);
+@@ -121,8 +119,6 @@ static int nf_flow_dnat_ip(const struct
+               new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v4.s_addr;
+               iph->saddr = new_addr;
+               break;
+-      default:
+-              return -1;
+       }
+       csum_replace4(&iph->check, addr, new_addr);
+@@ -371,8 +367,6 @@ static int nf_flow_snat_ipv6(const struc
+               new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6;
+               ip6h->daddr = new_addr;
+               break;
+-      default:
+-              return -1;
+       }
+       return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
+@@ -396,8 +390,6 @@ static int nf_flow_dnat_ipv6(const struc
+               new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6;
+               ip6h->saddr = new_addr;
+               break;
+-      default:
+-              return -1;
+       }
+       return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
diff --git a/target/linux/generic/backport-5.10/610-v5.13-07-netfilter-flowtable-fast-NAT-functions-never-fail.patch b/target/linux/generic/backport-5.10/610-v5.13-07-netfilter-flowtable-fast-NAT-functions-never-fail.patch
new file mode 100644 (file)
index 0000000..71bb394
--- /dev/null
@@ -0,0 +1,394 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:25 +0100
+Subject: [PATCH] netfilter: flowtable: fast NAT functions never fail
+
+Simplify existing fast NAT routines by returning void. After the
+skb_try_make_writable() call consolidation, these routines cannot ever
+fail.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -229,12 +229,12 @@ void nf_flow_table_free(struct nf_flowta
+ void flow_offload_teardown(struct flow_offload *flow);
+-int nf_flow_snat_port(const struct flow_offload *flow,
+-                    struct sk_buff *skb, unsigned int thoff,
+-                    u8 protocol, enum flow_offload_tuple_dir dir);
+-int nf_flow_dnat_port(const struct flow_offload *flow,
+-                    struct sk_buff *skb, unsigned int thoff,
+-                    u8 protocol, enum flow_offload_tuple_dir dir);
++void nf_flow_snat_port(const struct flow_offload *flow,
++                     struct sk_buff *skb, unsigned int thoff,
++                     u8 protocol, enum flow_offload_tuple_dir dir);
++void nf_flow_dnat_port(const struct flow_offload *flow,
++                     struct sk_buff *skb, unsigned int thoff,
++                     u8 protocol, enum flow_offload_tuple_dir dir);
+ struct flow_ports {
+       __be16 source, dest;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -389,20 +389,17 @@ static void nf_flow_offload_work_gc(stru
+       queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ);
+ }
+-
+-static int nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff,
+-                              __be16 port, __be16 new_port)
++static void nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff,
++                               __be16 port, __be16 new_port)
+ {
+       struct tcphdr *tcph;
+       tcph = (void *)(skb_network_header(skb) + thoff);
+       inet_proto_csum_replace2(&tcph->check, skb, port, new_port, false);
+-
+-      return 0;
+ }
+-static int nf_flow_nat_port_udp(struct sk_buff *skb, unsigned int thoff,
+-                              __be16 port, __be16 new_port)
++static void nf_flow_nat_port_udp(struct sk_buff *skb, unsigned int thoff,
++                               __be16 port, __be16 new_port)
+ {
+       struct udphdr *udph;
+@@ -413,30 +410,24 @@ static int nf_flow_nat_port_udp(struct s
+               if (!udph->check)
+                       udph->check = CSUM_MANGLED_0;
+       }
+-
+-      return 0;
+ }
+-static int nf_flow_nat_port(struct sk_buff *skb, unsigned int thoff,
+-                          u8 protocol, __be16 port, __be16 new_port)
++static void nf_flow_nat_port(struct sk_buff *skb, unsigned int thoff,
++                           u8 protocol, __be16 port, __be16 new_port)
+ {
+       switch (protocol) {
+       case IPPROTO_TCP:
+-              if (nf_flow_nat_port_tcp(skb, thoff, port, new_port) < 0)
+-                      return NF_DROP;
++              nf_flow_nat_port_tcp(skb, thoff, port, new_port);
+               break;
+       case IPPROTO_UDP:
+-              if (nf_flow_nat_port_udp(skb, thoff, port, new_port) < 0)
+-                      return NF_DROP;
++              nf_flow_nat_port_udp(skb, thoff, port, new_port);
+               break;
+       }
+-
+-      return 0;
+ }
+-int nf_flow_snat_port(const struct flow_offload *flow,
+-                    struct sk_buff *skb, unsigned int thoff,
+-                    u8 protocol, enum flow_offload_tuple_dir dir)
++void nf_flow_snat_port(const struct flow_offload *flow,
++                     struct sk_buff *skb, unsigned int thoff,
++                     u8 protocol, enum flow_offload_tuple_dir dir)
+ {
+       struct flow_ports *hdr;
+       __be16 port, new_port;
+@@ -456,13 +447,13 @@ int nf_flow_snat_port(const struct flow_
+               break;
+       }
+-      return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
++      nf_flow_nat_port(skb, thoff, protocol, port, new_port);
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_snat_port);
+-int nf_flow_dnat_port(const struct flow_offload *flow,
+-                    struct sk_buff *skb, unsigned int thoff,
+-                    u8 protocol, enum flow_offload_tuple_dir dir)
++void nf_flow_dnat_port(const struct flow_offload *flow, struct sk_buff *skb,
++                     unsigned int thoff, u8 protocol,
++                     enum flow_offload_tuple_dir dir)
+ {
+       struct flow_ports *hdr;
+       __be16 port, new_port;
+@@ -482,7 +473,7 @@ int nf_flow_dnat_port(const struct flow_
+               break;
+       }
+-      return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
++      nf_flow_nat_port(skb, thoff, protocol, port, new_port);
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_dnat_port);
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -34,19 +34,17 @@ static int nf_flow_state_check(struct fl
+       return 0;
+ }
+-static int nf_flow_nat_ip_tcp(struct sk_buff *skb, unsigned int thoff,
+-                            __be32 addr, __be32 new_addr)
++static void nf_flow_nat_ip_tcp(struct sk_buff *skb, unsigned int thoff,
++                             __be32 addr, __be32 new_addr)
+ {
+       struct tcphdr *tcph;
+       tcph = (void *)(skb_network_header(skb) + thoff);
+       inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, true);
+-
+-      return 0;
+ }
+-static int nf_flow_nat_ip_udp(struct sk_buff *skb, unsigned int thoff,
+-                            __be32 addr, __be32 new_addr)
++static void nf_flow_nat_ip_udp(struct sk_buff *skb, unsigned int thoff,
++                             __be32 addr, __be32 new_addr)
+ {
+       struct udphdr *udph;
+@@ -57,31 +55,25 @@ static int nf_flow_nat_ip_udp(struct sk_
+               if (!udph->check)
+                       udph->check = CSUM_MANGLED_0;
+       }
+-
+-      return 0;
+ }
+-static int nf_flow_nat_ip_l4proto(struct sk_buff *skb, struct iphdr *iph,
+-                                unsigned int thoff, __be32 addr,
+-                                __be32 new_addr)
++static void nf_flow_nat_ip_l4proto(struct sk_buff *skb, struct iphdr *iph,
++                                 unsigned int thoff, __be32 addr,
++                                 __be32 new_addr)
+ {
+       switch (iph->protocol) {
+       case IPPROTO_TCP:
+-              if (nf_flow_nat_ip_tcp(skb, thoff, addr, new_addr) < 0)
+-                      return NF_DROP;
++              nf_flow_nat_ip_tcp(skb, thoff, addr, new_addr);
+               break;
+       case IPPROTO_UDP:
+-              if (nf_flow_nat_ip_udp(skb, thoff, addr, new_addr) < 0)
+-                      return NF_DROP;
++              nf_flow_nat_ip_udp(skb, thoff, addr, new_addr);
+               break;
+       }
+-
+-      return 0;
+ }
+-static int nf_flow_snat_ip(const struct flow_offload *flow, struct sk_buff *skb,
+-                         struct iphdr *iph, unsigned int thoff,
+-                         enum flow_offload_tuple_dir dir)
++static void nf_flow_snat_ip(const struct flow_offload *flow,
++                          struct sk_buff *skb, struct iphdr *iph,
++                          unsigned int thoff, enum flow_offload_tuple_dir dir)
+ {
+       __be32 addr, new_addr;
+@@ -99,12 +91,12 @@ static int nf_flow_snat_ip(const struct
+       }
+       csum_replace4(&iph->check, addr, new_addr);
+-      return nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
++      nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
+ }
+-static int nf_flow_dnat_ip(const struct flow_offload *flow, struct sk_buff *skb,
+-                         struct iphdr *iph, unsigned int thoff,
+-                         enum flow_offload_tuple_dir dir)
++static void nf_flow_dnat_ip(const struct flow_offload *flow,
++                          struct sk_buff *skb, struct iphdr *iph,
++                          unsigned int thoff, enum flow_offload_tuple_dir dir)
+ {
+       __be32 addr, new_addr;
+@@ -122,24 +114,21 @@ static int nf_flow_dnat_ip(const struct
+       }
+       csum_replace4(&iph->check, addr, new_addr);
+-      return nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
++      nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
+ }
+-static int nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
++static void nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
+                         unsigned int thoff, enum flow_offload_tuple_dir dir,
+                         struct iphdr *iph)
+ {
+-      if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
+-          (nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
+-           nf_flow_snat_ip(flow, skb, iph, thoff, dir) < 0))
+-              return -1;
+-
+-      if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
+-          (nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
+-           nf_flow_dnat_ip(flow, skb, iph, thoff, dir) < 0))
+-              return -1;
+-
+-      return 0;
++      if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
++              nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir);
++              nf_flow_snat_ip(flow, skb, iph, thoff, dir);
++      }
++      if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
++              nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir);
++              nf_flow_dnat_ip(flow, skb, iph, thoff, dir);
++      }
+ }
+ static bool ip_has_options(unsigned int thoff)
+@@ -276,8 +265,7 @@ nf_flow_offload_ip_hook(void *priv, stru
+               return NF_DROP;
+       iph = ip_hdr(skb);
+-      if (nf_flow_nat_ip(flow, skb, thoff, dir, iph) < 0)
+-              return NF_DROP;
++      nf_flow_nat_ip(flow, skb, thoff, dir, iph);
+       ip_decrease_ttl(iph);
+       skb->tstamp = 0;
+@@ -301,22 +289,21 @@ nf_flow_offload_ip_hook(void *priv, stru
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
+-static int nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
+-                              struct in6_addr *addr,
+-                              struct in6_addr *new_addr)
++static void nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
++                               struct in6_addr *addr,
++                               struct in6_addr *new_addr,
++                               struct ipv6hdr *ip6h)
+ {
+       struct tcphdr *tcph;
+       tcph = (void *)(skb_network_header(skb) + thoff);
+       inet_proto_csum_replace16(&tcph->check, skb, addr->s6_addr32,
+                                 new_addr->s6_addr32, true);
+-
+-      return 0;
+ }
+-static int nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
+-                              struct in6_addr *addr,
+-                              struct in6_addr *new_addr)
++static void nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
++                               struct in6_addr *addr,
++                               struct in6_addr *new_addr)
+ {
+       struct udphdr *udph;
+@@ -327,32 +314,26 @@ static int nf_flow_nat_ipv6_udp(struct s
+               if (!udph->check)
+                       udph->check = CSUM_MANGLED_0;
+       }
+-
+-      return 0;
+ }
+-static int nf_flow_nat_ipv6_l4proto(struct sk_buff *skb, struct ipv6hdr *ip6h,
+-                                  unsigned int thoff, struct in6_addr *addr,
+-                                  struct in6_addr *new_addr)
++static void nf_flow_nat_ipv6_l4proto(struct sk_buff *skb, struct ipv6hdr *ip6h,
++                                   unsigned int thoff, struct in6_addr *addr,
++                                   struct in6_addr *new_addr)
+ {
+       switch (ip6h->nexthdr) {
+       case IPPROTO_TCP:
+-              if (nf_flow_nat_ipv6_tcp(skb, thoff, addr, new_addr) < 0)
+-                      return NF_DROP;
++              nf_flow_nat_ipv6_tcp(skb, thoff, addr, new_addr, ip6h);
+               break;
+       case IPPROTO_UDP:
+-              if (nf_flow_nat_ipv6_udp(skb, thoff, addr, new_addr) < 0)
+-                      return NF_DROP;
++              nf_flow_nat_ipv6_udp(skb, thoff, addr, new_addr);
+               break;
+       }
+-
+-      return 0;
+ }
+-static int nf_flow_snat_ipv6(const struct flow_offload *flow,
+-                           struct sk_buff *skb, struct ipv6hdr *ip6h,
+-                           unsigned int thoff,
+-                           enum flow_offload_tuple_dir dir)
++static void nf_flow_snat_ipv6(const struct flow_offload *flow,
++                            struct sk_buff *skb, struct ipv6hdr *ip6h,
++                            unsigned int thoff,
++                            enum flow_offload_tuple_dir dir)
+ {
+       struct in6_addr addr, new_addr;
+@@ -369,13 +350,13 @@ static int nf_flow_snat_ipv6(const struc
+               break;
+       }
+-      return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
++      nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
+ }
+-static int nf_flow_dnat_ipv6(const struct flow_offload *flow,
+-                           struct sk_buff *skb, struct ipv6hdr *ip6h,
+-                           unsigned int thoff,
+-                           enum flow_offload_tuple_dir dir)
++static void nf_flow_dnat_ipv6(const struct flow_offload *flow,
++                            struct sk_buff *skb, struct ipv6hdr *ip6h,
++                            unsigned int thoff,
++                            enum flow_offload_tuple_dir dir)
+ {
+       struct in6_addr addr, new_addr;
+@@ -392,27 +373,24 @@ static int nf_flow_dnat_ipv6(const struc
+               break;
+       }
+-      return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
++      nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
+ }
+-static int nf_flow_nat_ipv6(const struct flow_offload *flow,
+-                          struct sk_buff *skb,
+-                          enum flow_offload_tuple_dir dir,
+-                          struct ipv6hdr *ip6h)
++static void nf_flow_nat_ipv6(const struct flow_offload *flow,
++                           struct sk_buff *skb,
++                           enum flow_offload_tuple_dir dir,
++                           struct ipv6hdr *ip6h)
+ {
+       unsigned int thoff = sizeof(*ip6h);
+-      if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
+-          (nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
+-           nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
+-              return -1;
+-
+-      if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
+-          (nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
+-           nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
+-              return -1;
+-
+-      return 0;
++      if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
++              nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir);
++              nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir);
++      }
++      if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
++              nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir);
++              nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir);
++      }
+ }
+ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
+@@ -507,8 +485,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
+               return NF_DROP;
+       ip6h = ipv6_hdr(skb);
+-      if (nf_flow_nat_ipv6(flow, skb, dir, ip6h) < 0)
+-              return NF_DROP;
++      nf_flow_nat_ipv6(flow, skb, dir, ip6h);
+       ip6h->hop_limit--;
+       skb->tstamp = 0;
diff --git a/target/linux/generic/backport-5.10/610-v5.13-08-netfilter-flowtable-call-dst_check-to-fall-back-to-c.patch b/target/linux/generic/backport-5.10/610-v5.13-08-netfilter-flowtable-call-dst_check-to-fall-back-to-c.patch
new file mode 100644 (file)
index 0000000..2767850
--- /dev/null
@@ -0,0 +1,46 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:26 +0100
+Subject: [PATCH] netfilter: flowtable: call dst_check() to fall back to
+ classic forwarding
+
+In case the route is stale, pass up the packet to the classic forwarding
+path for re-evaluation and schedule this flow entry for removal.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -197,14 +197,6 @@ static bool nf_flow_exceeds_mtu(const st
+       return true;
+ }
+-static int nf_flow_offload_dst_check(struct dst_entry *dst)
+-{
+-      if (unlikely(dst_xfrm(dst)))
+-              return dst_check(dst, 0) ? 0 : -1;
+-
+-      return 0;
+-}
+-
+ static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
+                                     const struct nf_hook_state *state,
+                                     struct dst_entry *dst)
+@@ -256,7 +248,7 @@ nf_flow_offload_ip_hook(void *priv, stru
+       flow_offload_refresh(flow_table, flow);
+-      if (nf_flow_offload_dst_check(&rt->dst)) {
++      if (!dst_check(&rt->dst, 0)) {
+               flow_offload_teardown(flow);
+               return NF_ACCEPT;
+       }
+@@ -476,7 +468,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       flow_offload_refresh(flow_table, flow);
+-      if (nf_flow_offload_dst_check(&rt->dst)) {
++      if (!dst_check(&rt->dst, 0)) {
+               flow_offload_teardown(flow);
+               return NF_ACCEPT;
+       }
diff --git a/target/linux/generic/backport-5.10/610-v5.13-09-netfilter-flowtable-refresh-timeout-after-dst-and-wr.patch b/target/linux/generic/backport-5.10/610-v5.13-09-netfilter-flowtable-refresh-timeout-after-dst-and-wr.patch
new file mode 100644 (file)
index 0000000..14ac2ee
--- /dev/null
@@ -0,0 +1,49 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:27 +0100
+Subject: [PATCH] netfilter: flowtable: refresh timeout after dst and
+ writable checks
+
+Refresh the timeout (and retry hardware offload) once the skbuff dst
+is confirmed to be current and after the skbuff is made writable.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -246,8 +246,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+       if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+               return NF_ACCEPT;
+-      flow_offload_refresh(flow_table, flow);
+-
+       if (!dst_check(&rt->dst, 0)) {
+               flow_offload_teardown(flow);
+               return NF_ACCEPT;
+@@ -256,6 +254,8 @@ nf_flow_offload_ip_hook(void *priv, stru
+       if (skb_try_make_writable(skb, thoff + hdrsize))
+               return NF_DROP;
++      flow_offload_refresh(flow_table, flow);
++
+       iph = ip_hdr(skb);
+       nf_flow_nat_ip(flow, skb, thoff, dir, iph);
+@@ -466,8 +466,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
+                               sizeof(*ip6h)))
+               return NF_ACCEPT;
+-      flow_offload_refresh(flow_table, flow);
+-
+       if (!dst_check(&rt->dst, 0)) {
+               flow_offload_teardown(flow);
+               return NF_ACCEPT;
+@@ -476,6 +474,8 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
+               return NF_DROP;
++      flow_offload_refresh(flow_table, flow);
++
+       ip6h = ipv6_hdr(skb);
+       nf_flow_nat_ipv6(flow, skb, dir, ip6h);
diff --git a/target/linux/generic/backport-5.10/610-v5.13-10-netfilter-nftables-update-table-flags-from-the-commi.patch b/target/linux/generic/backport-5.10/610-v5.13-10-netfilter-nftables-update-table-flags-from-the-commi.patch
new file mode 100644 (file)
index 0000000..107ad1c
--- /dev/null
@@ -0,0 +1,103 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 23 Mar 2021 00:56:28 +0100
+Subject: [PATCH] netfilter: nftables: update table flags from the commit
+ phase
+
+Do not update table flags from the preparation phase. Store the flags
+update into the transaction, then update the flags from the commit
+phase.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1470,13 +1470,16 @@ struct nft_trans_chain {
+ struct nft_trans_table {
+       bool                            update;
+-      bool                            enable;
++      u8                              state;
++      u32                             flags;
+ };
+ #define nft_trans_table_update(trans) \
+       (((struct nft_trans_table *)trans->data)->update)
+-#define nft_trans_table_enable(trans) \
+-      (((struct nft_trans_table *)trans->data)->enable)
++#define nft_trans_table_state(trans)  \
++      (((struct nft_trans_table *)trans->data)->state)
++#define nft_trans_table_flags(trans)  \
++      (((struct nft_trans_table *)trans->data)->flags)
+ struct nft_trans_elem {
+       struct nft_set                  *set;
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -891,6 +891,12 @@ static void nf_tables_table_disable(stru
+       nft_table_disable(net, table, 0);
+ }
++enum {
++      NFT_TABLE_STATE_UNCHANGED       = 0,
++      NFT_TABLE_STATE_DORMANT,
++      NFT_TABLE_STATE_WAKEUP
++};
++
+ static int nf_tables_updtable(struct nft_ctx *ctx)
+ {
+       struct nft_trans *trans;
+@@ -914,19 +920,17 @@ static int nf_tables_updtable(struct nft
+       if ((flags & NFT_TABLE_F_DORMANT) &&
+           !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
+-              nft_trans_table_enable(trans) = false;
++              nft_trans_table_state(trans) = NFT_TABLE_STATE_DORMANT;
+       } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+                  ctx->table->flags & NFT_TABLE_F_DORMANT) {
+-              ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
+               ret = nf_tables_table_enable(ctx->net, ctx->table);
+               if (ret >= 0)
+-                      nft_trans_table_enable(trans) = true;
+-              else
+-                      ctx->table->flags |= NFT_TABLE_F_DORMANT;
++                      nft_trans_table_state(trans) = NFT_TABLE_STATE_WAKEUP;
+       }
+       if (ret < 0)
+               goto err;
++      nft_trans_table_flags(trans) = flags;
+       nft_trans_table_update(trans) = true;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       return 0;
+@@ -7873,11 +7877,10 @@ static int nf_tables_commit(struct net *
+               switch (trans->msg_type) {
+               case NFT_MSG_NEWTABLE:
+                       if (nft_trans_table_update(trans)) {
+-                              if (!nft_trans_table_enable(trans)) {
+-                                      nf_tables_table_disable(net,
+-                                                              trans->ctx.table);
+-                                      trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+-                              }
++                              if (nft_trans_table_state(trans) == NFT_TABLE_STATE_DORMANT)
++                                      nf_tables_table_disable(net, trans->ctx.table);
++
++                              trans->ctx.table->flags = nft_trans_table_flags(trans);
+                       } else {
+                               nft_clear(net, trans->ctx.table);
+                       }
+@@ -8090,11 +8093,9 @@ static int __nf_tables_abort(struct net
+               switch (trans->msg_type) {
+               case NFT_MSG_NEWTABLE:
+                       if (nft_trans_table_update(trans)) {
+-                              if (nft_trans_table_enable(trans)) {
+-                                      nf_tables_table_disable(net,
+-                                                              trans->ctx.table);
+-                                      trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+-                              }
++                              if (nft_trans_table_state(trans) == NFT_TABLE_STATE_WAKEUP)
++                                      nf_tables_table_disable(net, trans->ctx.table);
++
+                               nft_trans_destroy(trans);
+                       } else {
+                               list_del_rcu(&trans->ctx.table->list);
diff --git a/target/linux/generic/backport-5.10/610-v5.13-11-net-resolve-forwarding-path-from-virtual-netdevice-a.patch b/target/linux/generic/backport-5.10/610-v5.13-11-net-resolve-forwarding-path-from-virtual-netdevice-a.patch
new file mode 100644 (file)
index 0000000..8253ac1
--- /dev/null
@@ -0,0 +1,170 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:32 +0100
+Subject: [PATCH] net: resolve forwarding path from virtual netdevice and
+ HW destination address
+
+This patch adds dev_fill_forward_path() which resolves the path to reach
+the real netdevice from the IP forwarding side. This function takes as
+input the netdevice and the destination hardware address and it walks
+down the devices calling .ndo_fill_forward_path() for each device until
+the real device is found.
+
+For instance, assuming the following topology:
+
+               IP forwarding
+              /             \
+           br0              eth0
+           / \
+       eth1  eth2
+        .
+        .
+        .
+       ethX
+ ab:cd:ef:ab:cd:ef
+
+where eth1 and eth2 are bridge ports and eth0 provides WAN connectivity.
+ethX is the interface in another box which is connected to the eth1
+bridge port.
+
+For packets going through IP forwarding to br0 whose destination MAC
+address is ab:cd:ef:ab:cd:ef, dev_fill_forward_path() provides the
+following path:
+
+       br0 -> eth1
+
+.ndo_fill_forward_path for br0 looks up at the FDB for the bridge port
+from the destination MAC address to get the bridge port eth1.
+
+This information allows to create a fast path that bypasses the classic
+bridge and IP forwarding paths, so packets go directly from the bridge
+port eth1 to eth0 (wan interface) and vice versa.
+
+             fast path
+      .------------------------.
+     /                          \
+    |           IP forwarding   |
+    |          /             \  \/
+    |       br0               eth0
+    .       / \
+     -> eth1  eth2
+        .
+        .
+        .
+       ethX
+ ab:cd:ef:ab:cd:ef
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -827,6 +827,27 @@ typedef u16 (*select_queue_fallback_t)(s
+                                      struct sk_buff *skb,
+                                      struct net_device *sb_dev);
++enum net_device_path_type {
++      DEV_PATH_ETHERNET = 0,
++};
++
++struct net_device_path {
++      enum net_device_path_type       type;
++      const struct net_device         *dev;
++};
++
++#define NET_DEVICE_PATH_STACK_MAX     5
++
++struct net_device_path_stack {
++      int                     num_paths;
++      struct net_device_path  path[NET_DEVICE_PATH_STACK_MAX];
++};
++
++struct net_device_path_ctx {
++      const struct net_device *dev;
++      const u8                *daddr;
++};
++
+ enum tc_setup_type {
+       TC_SETUP_QDISC_MQPRIO,
+       TC_SETUP_CLSU32,
+@@ -1273,6 +1294,8 @@ struct netdev_net_notifier {
+  * struct net_device *(*ndo_get_peer_dev)(struct net_device *dev);
+  *    If a device is paired with a peer device, return the peer instance.
+  *    The caller must be under RCU read context.
++ * int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, struct net_device_path *path);
++ *     Get the forwarding path to reach the real device from the HW destination address
+  */
+ struct net_device_ops {
+       int                     (*ndo_init)(struct net_device *dev);
+@@ -1481,6 +1504,8 @@ struct net_device_ops {
+       int                     (*ndo_tunnel_ctl)(struct net_device *dev,
+                                                 struct ip_tunnel_parm *p, int cmd);
+       struct net_device *     (*ndo_get_peer_dev)(struct net_device *dev);
++      int                     (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx,
++                                                         struct net_device_path *path);
+ };
+ /**
+@@ -2795,6 +2820,8 @@ void dev_remove_offload(struct packet_of
+ int dev_get_iflink(const struct net_device *dev);
+ int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
++int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
++                        struct net_device_path_stack *stack);
+ struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
+                                     unsigned short mask);
+ struct net_device *dev_get_by_name(struct net *net, const char *name);
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -847,6 +847,52 @@ int dev_fill_metadata_dst(struct net_dev
+ }
+ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
++static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
++{
++      int k = stack->num_paths++;
++
++      if (WARN_ON_ONCE(k >= NET_DEVICE_PATH_STACK_MAX))
++              return NULL;
++
++      return &stack->path[k];
++}
++
++int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
++                        struct net_device_path_stack *stack)
++{
++      const struct net_device *last_dev;
++      struct net_device_path_ctx ctx = {
++              .dev    = dev,
++              .daddr  = daddr,
++      };
++      struct net_device_path *path;
++      int ret = 0;
++
++      stack->num_paths = 0;
++      while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
++              last_dev = ctx.dev;
++              path = dev_fwd_path(stack);
++              if (!path)
++                      return -1;
++
++              memset(path, 0, sizeof(struct net_device_path));
++              ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
++              if (ret < 0)
++                      return -1;
++
++              if (WARN_ON_ONCE(last_dev == ctx.dev))
++                      return -1;
++      }
++      path = dev_fwd_path(stack);
++      if (!path)
++              return -1;
++      path->type = DEV_PATH_ETHERNET;
++      path->dev = ctx.dev;
++
++      return ret;
++}
++EXPORT_SYMBOL_GPL(dev_fill_forward_path);
++
+ /**
+  *    __dev_get_by_name       - find a device by its name
+  *    @net: the applicable net namespace
diff --git a/target/linux/generic/backport-5.10/610-v5.13-12-net-8021q-resolve-forwarding-path-for-vlan-devices.patch b/target/linux/generic/backport-5.10/610-v5.13-12-net-8021q-resolve-forwarding-path-for-vlan-devices.patch
new file mode 100644 (file)
index 0000000..16eb940
--- /dev/null
@@ -0,0 +1,80 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:33 +0100
+Subject: [PATCH] net: 8021q: resolve forwarding path for vlan devices
+
+Add .ndo_fill_forward_path for vlan devices.
+
+For instance, assuming the following topology:
+
+                   IP forwarding
+                  /             \
+            eth0.100             eth0
+            |
+            eth0
+            .
+            .
+            .
+           ethX
+     ab:cd:ef:ab:cd:ef
+
+For packets going through IP forwarding to eth0.100 whose destination
+MAC address is ab:cd:ef:ab:cd:ef, dev_fill_forward_path() provides the
+following path:
+
+        eth0.100 -> eth0
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -829,11 +829,18 @@ typedef u16 (*select_queue_fallback_t)(s
+ enum net_device_path_type {
+       DEV_PATH_ETHERNET = 0,
++      DEV_PATH_VLAN,
+ };
+ struct net_device_path {
+       enum net_device_path_type       type;
+       const struct net_device         *dev;
++      union {
++              struct {
++                      u16             id;
++                      __be16          proto;
++              } encap;
++      };
+ };
+ #define NET_DEVICE_PATH_STACK_MAX     5
+--- a/net/8021q/vlan_dev.c
++++ b/net/8021q/vlan_dev.c
+@@ -767,6 +767,20 @@ static int vlan_dev_get_iflink(const str
+       return real_dev->ifindex;
+ }
++static int vlan_dev_fill_forward_path(struct net_device_path_ctx *ctx,
++                                    struct net_device_path *path)
++{
++      struct vlan_dev_priv *vlan = vlan_dev_priv(ctx->dev);
++
++      path->type = DEV_PATH_VLAN;
++      path->encap.id = vlan->vlan_id;
++      path->encap.proto = vlan->vlan_proto;
++      path->dev = ctx->dev;
++      ctx->dev = vlan->real_dev;
++
++      return 0;
++}
++
+ static const struct ethtool_ops vlan_ethtool_ops = {
+       .get_link_ksettings     = vlan_ethtool_get_link_ksettings,
+       .get_drvinfo            = vlan_ethtool_get_drvinfo,
+@@ -805,6 +819,7 @@ static const struct net_device_ops vlan_
+ #endif
+       .ndo_fix_features       = vlan_dev_fix_features,
+       .ndo_get_iflink         = vlan_dev_get_iflink,
++      .ndo_fill_forward_path  = vlan_dev_fill_forward_path,
+ };
+ static void vlan_dev_free(struct net_device *dev)
diff --git a/target/linux/generic/backport-5.10/610-v5.13-13-net-bridge-resolve-forwarding-path-for-bridge-device.patch b/target/linux/generic/backport-5.10/610-v5.13-13-net-bridge-resolve-forwarding-path-for-bridge-device.patch
new file mode 100644 (file)
index 0000000..bdb12b1
--- /dev/null
@@ -0,0 +1,62 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:34 +0100
+Subject: [PATCH] net: bridge: resolve forwarding path for bridge devices
+
+Add .ndo_fill_forward_path for bridge devices.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -830,6 +830,7 @@ typedef u16 (*select_queue_fallback_t)(s
+ enum net_device_path_type {
+       DEV_PATH_ETHERNET = 0,
+       DEV_PATH_VLAN,
++      DEV_PATH_BRIDGE,
+ };
+ struct net_device_path {
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -398,6 +398,32 @@ static int br_del_slave(struct net_devic
+       return br_del_if(br, slave_dev);
+ }
++static int br_fill_forward_path(struct net_device_path_ctx *ctx,
++                              struct net_device_path *path)
++{
++      struct net_bridge_fdb_entry *f;
++      struct net_bridge_port *dst;
++      struct net_bridge *br;
++
++      if (netif_is_bridge_port(ctx->dev))
++              return -1;
++
++      br = netdev_priv(ctx->dev);
++      f = br_fdb_find_rcu(br, ctx->daddr, 0);
++      if (!f || !f->dst)
++              return -1;
++
++      dst = READ_ONCE(f->dst);
++      if (!dst)
++              return -1;
++
++      path->type = DEV_PATH_BRIDGE;
++      path->dev = dst->br->dev;
++      ctx->dev = dst->dev;
++
++      return 0;
++}
++
+ static const struct ethtool_ops br_ethtool_ops = {
+       .get_drvinfo             = br_getinfo,
+       .get_link                = ethtool_op_get_link,
+@@ -432,6 +458,7 @@ static const struct net_device_ops br_ne
+       .ndo_bridge_setlink      = br_setlink,
+       .ndo_bridge_dellink      = br_dellink,
+       .ndo_features_check      = passthru_features_check,
++      .ndo_fill_forward_path   = br_fill_forward_path,
+ };
+ static struct device_type br_type = {
diff --git a/target/linux/generic/backport-5.10/610-v5.13-14-net-bridge-resolve-forwarding-path-for-VLAN-tag-acti.patch b/target/linux/generic/backport-5.10/610-v5.13-14-net-bridge-resolve-forwarding-path-for-VLAN-tag-acti.patch
new file mode 100644 (file)
index 0000000..2c1d935
--- /dev/null
@@ -0,0 +1,207 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:35 +0100
+Subject: [PATCH] net: bridge: resolve forwarding path for VLAN tag
+ actions in bridge devices
+
+Depending on the VLAN settings of the bridge and the port, the bridge can
+either add or remove a tag. When vlan filtering is enabled, the fdb lookup
+also needs to know the VLAN tag/proto for the destination address
+To provide this, keep track of the stack of VLAN tags for the path in the
+lookup context
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -841,10 +841,20 @@ struct net_device_path {
+                       u16             id;
+                       __be16          proto;
+               } encap;
++              struct {
++                      enum {
++                              DEV_PATH_BR_VLAN_KEEP,
++                              DEV_PATH_BR_VLAN_TAG,
++                              DEV_PATH_BR_VLAN_UNTAG,
++                      }               vlan_mode;
++                      u16             vlan_id;
++                      __be16          vlan_proto;
++              } bridge;
+       };
+ };
+ #define NET_DEVICE_PATH_STACK_MAX     5
++#define NET_DEVICE_PATH_VLAN_MAX      2
+ struct net_device_path_stack {
+       int                     num_paths;
+@@ -854,6 +864,12 @@ struct net_device_path_stack {
+ struct net_device_path_ctx {
+       const struct net_device *dev;
+       const u8                *daddr;
++
++      int                     num_vlans;
++      struct {
++              u16             id;
++              __be16          proto;
++      } vlan[NET_DEVICE_PATH_VLAN_MAX];
+ };
+ enum tc_setup_type {
+--- a/net/8021q/vlan_dev.c
++++ b/net/8021q/vlan_dev.c
+@@ -777,6 +777,12 @@ static int vlan_dev_fill_forward_path(st
+       path->encap.proto = vlan->vlan_proto;
+       path->dev = ctx->dev;
+       ctx->dev = vlan->real_dev;
++      if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
++              return -ENOSPC;
++
++      ctx->vlan[ctx->num_vlans].id = vlan->vlan_id;
++      ctx->vlan[ctx->num_vlans].proto = vlan->vlan_proto;
++      ctx->num_vlans++;
+       return 0;
+ }
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -409,7 +409,10 @@ static int br_fill_forward_path(struct n
+               return -1;
+       br = netdev_priv(ctx->dev);
+-      f = br_fdb_find_rcu(br, ctx->daddr, 0);
++
++      br_vlan_fill_forward_path_pvid(br, ctx, path);
++
++      f = br_fdb_find_rcu(br, ctx->daddr, path->bridge.vlan_id);
+       if (!f || !f->dst)
+               return -1;
+@@ -417,10 +420,28 @@ static int br_fill_forward_path(struct n
+       if (!dst)
+               return -1;
++      if (br_vlan_fill_forward_path_mode(br, dst, path))
++              return -1;
++
+       path->type = DEV_PATH_BRIDGE;
+       path->dev = dst->br->dev;
+       ctx->dev = dst->dev;
++      switch (path->bridge.vlan_mode) {
++      case DEV_PATH_BR_VLAN_TAG:
++              if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
++                      return -ENOSPC;
++              ctx->vlan[ctx->num_vlans].id = path->bridge.vlan_id;
++              ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto;
++              ctx->num_vlans++;
++              break;
++      case DEV_PATH_BR_VLAN_UNTAG:
++              ctx->num_vlans--;
++              break;
++      case DEV_PATH_BR_VLAN_KEEP:
++              break;
++      }
++
+       return 0;
+ }
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -1095,6 +1095,13 @@ void br_vlan_notify(const struct net_bri
+ bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
+                            const struct net_bridge_vlan *range_end);
++void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
++                                  struct net_device_path_ctx *ctx,
++                                  struct net_device_path *path);
++int br_vlan_fill_forward_path_mode(struct net_bridge *br,
++                                 struct net_bridge_port *dst,
++                                 struct net_device_path *path);
++
+ static inline struct net_bridge_vlan_group *br_vlan_group(
+                                       const struct net_bridge *br)
+ {
+@@ -1252,6 +1259,19 @@ static inline int nbp_get_num_vlan_infos
+ {
+       return 0;
+ }
++
++static inline void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
++                                                struct net_device_path_ctx *ctx,
++                                                struct net_device_path *path)
++{
++}
++
++static inline int br_vlan_fill_forward_path_mode(struct net_bridge *br,
++                                               struct net_bridge_port *dst,
++                                               struct net_device_path *path)
++{
++      return 0;
++}
+ static inline struct net_bridge_vlan_group *br_vlan_group(
+                                       const struct net_bridge *br)
+--- a/net/bridge/br_vlan.c
++++ b/net/bridge/br_vlan.c
+@@ -1327,6 +1327,59 @@ int br_vlan_get_pvid_rcu(const struct ne
+ }
+ EXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
++void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
++                                  struct net_device_path_ctx *ctx,
++                                  struct net_device_path *path)
++{
++      struct net_bridge_vlan_group *vg;
++      int idx = ctx->num_vlans - 1;
++      u16 vid;
++
++      path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
++
++      if (!br_opt_get(br, BROPT_VLAN_ENABLED))
++              return;
++
++      vg = br_vlan_group(br);
++
++      if (idx >= 0 &&
++          ctx->vlan[idx].proto == br->vlan_proto) {
++              vid = ctx->vlan[idx].id;
++      } else {
++              path->bridge.vlan_mode = DEV_PATH_BR_VLAN_TAG;
++              vid = br_get_pvid(vg);
++      }
++
++      path->bridge.vlan_id = vid;
++      path->bridge.vlan_proto = br->vlan_proto;
++}
++
++int br_vlan_fill_forward_path_mode(struct net_bridge *br,
++                                 struct net_bridge_port *dst,
++                                 struct net_device_path *path)
++{
++      struct net_bridge_vlan_group *vg;
++      struct net_bridge_vlan *v;
++
++      if (!br_opt_get(br, BROPT_VLAN_ENABLED))
++              return 0;
++
++      vg = nbp_vlan_group_rcu(dst);
++      v = br_vlan_find(vg, path->bridge.vlan_id);
++      if (!v || !br_vlan_should_use(v))
++              return -EINVAL;
++
++      if (!(v->flags & BRIDGE_VLAN_INFO_UNTAGGED))
++              return 0;
++
++      if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
++              path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
++      else
++              path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;
++
++      return 0;
++}
++
+ int br_vlan_get_info(const struct net_device *dev, u16 vid,
+                    struct bridge_vlan_info *p_vinfo)
+ {
diff --git a/target/linux/generic/backport-5.10/610-v5.13-15-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch b/target/linux/generic/backport-5.10/610-v5.13-15-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch
new file mode 100644 (file)
index 0000000..aaa6fd7
--- /dev/null
@@ -0,0 +1,113 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:36 +0100
+Subject: [PATCH] net: ppp: resolve forwarding path for bridge pppoe
+ devices
+
+Pass on the PPPoE session ID, destination hardware address and the real
+device.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/drivers/net/ppp/ppp_generic.c
++++ b/drivers/net/ppp/ppp_generic.c
+@@ -1450,12 +1450,34 @@ static void ppp_dev_priv_destructor(stru
+               ppp_destroy_interface(ppp);
+ }
++static int ppp_fill_forward_path(struct net_device_path_ctx *ctx,
++                               struct net_device_path *path)
++{
++      struct ppp *ppp = netdev_priv(ctx->dev);
++      struct ppp_channel *chan;
++      struct channel *pch;
++
++      if (ppp->flags & SC_MULTILINK)
++              return -EOPNOTSUPP;
++
++      if (list_empty(&ppp->channels))
++              return -ENODEV;
++
++      pch = list_first_entry(&ppp->channels, struct channel, clist);
++      chan = pch->chan;
++      if (!chan->ops->fill_forward_path)
++              return -EOPNOTSUPP;
++
++      return chan->ops->fill_forward_path(ctx, path, chan);
++}
++
+ static const struct net_device_ops ppp_netdev_ops = {
+       .ndo_init        = ppp_dev_init,
+       .ndo_uninit      = ppp_dev_uninit,
+       .ndo_start_xmit  = ppp_start_xmit,
+       .ndo_do_ioctl    = ppp_net_ioctl,
+       .ndo_get_stats64 = ppp_get_stats64,
++      .ndo_fill_forward_path = ppp_fill_forward_path,
+ };
+ static struct device_type ppp_type = {
+--- a/drivers/net/ppp/pppoe.c
++++ b/drivers/net/ppp/pppoe.c
+@@ -972,8 +972,31 @@ static int pppoe_xmit(struct ppp_channel
+       return __pppoe_xmit(sk, skb);
+ }
++static int pppoe_fill_forward_path(struct net_device_path_ctx *ctx,
++                                 struct net_device_path *path,
++                                 const struct ppp_channel *chan)
++{
++      struct sock *sk = (struct sock *)chan->private;
++      struct pppox_sock *po = pppox_sk(sk);
++      struct net_device *dev = po->pppoe_dev;
++
++      if (sock_flag(sk, SOCK_DEAD) ||
++          !(sk->sk_state & PPPOX_CONNECTED) || !dev)
++              return -1;
++
++      path->type = DEV_PATH_PPPOE;
++      path->encap.proto = htons(ETH_P_PPP_SES);
++      path->encap.id = be16_to_cpu(po->num);
++      memcpy(path->encap.h_dest, po->pppoe_pa.remote, ETH_ALEN);
++      path->dev = ctx->dev;
++      ctx->dev = dev;
++
++      return 0;
++}
++
+ static const struct ppp_channel_ops pppoe_chan_ops = {
+       .start_xmit = pppoe_xmit,
++      .fill_forward_path = pppoe_fill_forward_path,
+ };
+ static int pppoe_recvmsg(struct socket *sock, struct msghdr *m,
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -831,6 +831,7 @@ enum net_device_path_type {
+       DEV_PATH_ETHERNET = 0,
+       DEV_PATH_VLAN,
+       DEV_PATH_BRIDGE,
++      DEV_PATH_PPPOE,
+ };
+ struct net_device_path {
+@@ -840,6 +841,7 @@ struct net_device_path {
+               struct {
+                       u16             id;
+                       __be16          proto;
++                      u8              h_dest[ETH_ALEN];
+               } encap;
+               struct {
+                       enum {
+--- a/include/linux/ppp_channel.h
++++ b/include/linux/ppp_channel.h
+@@ -28,6 +28,9 @@ struct ppp_channel_ops {
+       int     (*start_xmit)(struct ppp_channel *, struct sk_buff *);
+       /* Handle an ioctl call that has come in via /dev/ppp. */
+       int     (*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
++      int     (*fill_forward_path)(struct net_device_path_ctx *,
++                                   struct net_device_path *,
++                                   const struct ppp_channel *);
+ };
+ struct ppp_channel {
diff --git a/target/linux/generic/backport-5.10/610-v5.13-16-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch b/target/linux/generic/backport-5.10/610-v5.13-16-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch
new file mode 100644 (file)
index 0000000..62f18a0
--- /dev/null
@@ -0,0 +1,63 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:37 +0100
+Subject: [PATCH] net: dsa: resolve forwarding path for dsa slave ports
+
+Add .ndo_fill_forward_path for dsa slave port devices
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -832,6 +832,7 @@ enum net_device_path_type {
+       DEV_PATH_VLAN,
+       DEV_PATH_BRIDGE,
+       DEV_PATH_PPPOE,
++      DEV_PATH_DSA,
+ };
+ struct net_device_path {
+@@ -852,6 +853,10 @@ struct net_device_path {
+                       u16             vlan_id;
+                       __be16          vlan_proto;
+               } bridge;
++              struct {
++                      int port;
++                      u16 proto;
++              } dsa;
+       };
+ };
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1617,6 +1617,21 @@ static struct devlink_port *dsa_slave_ge
+       return dp->ds->devlink ? &dp->devlink_port : NULL;
+ }
++static int dsa_slave_fill_forward_path(struct net_device_path_ctx *ctx,
++                                     struct net_device_path *path)
++{
++      struct dsa_port *dp = dsa_slave_to_port(ctx->dev);
++      struct dsa_port *cpu_dp = dp->cpu_dp;
++
++      path->dev = ctx->dev;
++      path->type = DEV_PATH_DSA;
++      path->dsa.proto = cpu_dp->tag_ops->proto;
++      path->dsa.port = dp->index;
++      ctx->dev = cpu_dp->master;
++
++      return 0;
++}
++
+ static const struct net_device_ops dsa_slave_netdev_ops = {
+       .ndo_open               = dsa_slave_open,
+       .ndo_stop               = dsa_slave_close,
+@@ -1642,6 +1657,7 @@ static const struct net_device_ops dsa_s
+       .ndo_vlan_rx_kill_vid   = dsa_slave_vlan_rx_kill_vid,
+       .ndo_get_devlink_port   = dsa_slave_get_devlink_port,
+       .ndo_change_mtu         = dsa_slave_change_mtu,
++      .ndo_fill_forward_path  = dsa_slave_fill_forward_path,
+ };
+ static struct device_type dsa_type = {
diff --git a/target/linux/generic/backport-5.10/610-v5.13-17-netfilter-flowtable-add-xmit-path-types.patch b/target/linux/generic/backport-5.10/610-v5.13-17-netfilter-flowtable-add-xmit-path-types.patch
new file mode 100644 (file)
index 0000000..e0fdf49
--- /dev/null
@@ -0,0 +1,147 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:38 +0100
+Subject: [PATCH] netfilter: flowtable: add xmit path types
+
+Add the xmit_type field that defines the two supported xmit paths in the
+flowtable data plane, which are the neighbour and the xfrm xmit paths.
+This patch prepares for new flowtable xmit path types to come.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -89,6 +89,11 @@ enum flow_offload_tuple_dir {
+ };
+ #define FLOW_OFFLOAD_DIR_MAX  IP_CT_DIR_MAX
++enum flow_offload_xmit_type {
++      FLOW_OFFLOAD_XMIT_NEIGH         = 0,
++      FLOW_OFFLOAD_XMIT_XFRM,
++};
++
+ struct flow_offload_tuple {
+       union {
+               struct in_addr          src_v4;
+@@ -111,7 +116,8 @@ struct flow_offload_tuple {
+       /* All members above are keys for lookups, see flow_offload_hash(). */
+       struct { }                      __hash;
+-      u8                              dir;
++      u8                              dir:6,
++                                      xmit_type:2;
+       u16                             mtu;
+@@ -158,7 +164,8 @@ static inline __s32 nf_flow_timeout_delt
+ struct nf_flow_route {
+       struct {
+-              struct dst_entry        *dst;
++              struct dst_entry                *dst;
++              enum flow_offload_xmit_type     xmit_type;
+       } tuple[FLOW_OFFLOAD_DIR_MAX];
+ };
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -95,6 +95,7 @@ static int flow_offload_fill_route(struc
+       }
+       flow_tuple->iifidx = other_dst->dev->ifindex;
++      flow_tuple->xmit_type = route->tuple[dir].xmit_type;
+       flow_tuple->dst_cache = dst;
+       return 0;
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -235,8 +235,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+       dir = tuplehash->tuple.dir;
+       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+-      rt = (struct rtable *)flow->tuplehash[dir].tuple.dst_cache;
+-      outdev = rt->dst.dev;
+       if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
+               return NF_ACCEPT;
+@@ -265,13 +263,16 @@ nf_flow_offload_ip_hook(void *priv, stru
+       if (flow_table->flags & NF_FLOWTABLE_COUNTER)
+               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+-      if (unlikely(dst_xfrm(&rt->dst))) {
++      rt = (struct rtable *)tuplehash->tuple.dst_cache;
++
++      if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+               memset(skb->cb, 0, sizeof(struct inet_skb_parm));
+               IPCB(skb)->iif = skb->dev->ifindex;
+               IPCB(skb)->flags = IPSKB_FORWARDED;
+               return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+       }
++      outdev = rt->dst.dev;
+       skb->dev = outdev;
+       nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
+       skb_dst_set_noref(skb, &rt->dst);
+@@ -456,8 +457,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       dir = tuplehash->tuple.dir;
+       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+-      rt = (struct rt6_info *)flow->tuplehash[dir].tuple.dst_cache;
+-      outdev = rt->dst.dev;
+       if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
+               return NF_ACCEPT;
+@@ -485,13 +484,16 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       if (flow_table->flags & NF_FLOWTABLE_COUNTER)
+               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+-      if (unlikely(dst_xfrm(&rt->dst))) {
++      rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
++
++      if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+               memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+               IP6CB(skb)->iif = skb->dev->ifindex;
+               IP6CB(skb)->flags = IP6SKB_FORWARDED;
+               return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+       }
++      outdev = rt->dst.dev;
+       skb->dev = outdev;
+       nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
+       skb_dst_set_noref(skb, &rt->dst);
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -19,6 +19,22 @@ struct nft_flow_offload {
+       struct nft_flowtable    *flowtable;
+ };
++static enum flow_offload_xmit_type nft_xmit_type(struct dst_entry *dst)
++{
++      if (dst_xfrm(dst))
++              return FLOW_OFFLOAD_XMIT_XFRM;
++
++      return FLOW_OFFLOAD_XMIT_NEIGH;
++}
++
++static void nft_default_forward_path(struct nf_flow_route *route,
++                                   struct dst_entry *dst_cache,
++                                   enum ip_conntrack_dir dir)
++{
++      route->tuple[dir].dst           = dst_cache;
++      route->tuple[dir].xmit_type     = nft_xmit_type(dst_cache);
++}
++
+ static int nft_flow_route(const struct nft_pktinfo *pkt,
+                         const struct nf_conn *ct,
+                         struct nf_flow_route *route,
+@@ -44,8 +60,8 @@ static int nft_flow_route(const struct n
+       if (!other_dst)
+               return -ENOENT;
+-      route->tuple[dir].dst           = this_dst;
+-      route->tuple[!dir].dst          = other_dst;
++      nft_default_forward_path(route, this_dst, dir);
++      nft_default_forward_path(route, other_dst, !dir);
+       return 0;
+ }
diff --git a/target/linux/generic/backport-5.10/610-v5.13-18-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch b/target/linux/generic/backport-5.10/610-v5.13-18-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch
new file mode 100644 (file)
index 0000000..1fe8739
--- /dev/null
@@ -0,0 +1,191 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:39 +0100
+Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
+ obtain ingress device
+
+Obtain the ingress device in the tuple from the route in the reply
+direction. Use dev_fill_forward_path() instead to get the real ingress
+device for this flow.
+
+Fall back to use the ingress device that the IP forwarding route
+provides if:
+
+- dev_fill_forward_path() finds no real ingress device.
+- the ingress device that is obtained is not part of the flowtable
+  devices.
+- this route has a xfrm policy.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -165,6 +165,9 @@ static inline __s32 nf_flow_timeout_delt
+ struct nf_flow_route {
+       struct {
+               struct dst_entry                *dst;
++              struct {
++                      u32                     ifindex;
++              } in;
+               enum flow_offload_xmit_type     xmit_type;
+       } tuple[FLOW_OFFLOAD_DIR_MAX];
+ };
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -79,7 +79,6 @@ static int flow_offload_fill_route(struc
+                                  enum flow_offload_tuple_dir dir)
+ {
+       struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
+-      struct dst_entry *other_dst = route->tuple[!dir].dst;
+       struct dst_entry *dst = route->tuple[dir].dst;
+       if (!dst_hold_safe(route->tuple[dir].dst))
+@@ -94,7 +93,7 @@ static int flow_offload_fill_route(struc
+               break;
+       }
+-      flow_tuple->iifidx = other_dst->dev->ifindex;
++      flow_tuple->iifidx = route->tuple[dir].in.ifindex;
+       flow_tuple->xmit_type = route->tuple[dir].xmit_type;
+       flow_tuple->dst_cache = dst;
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -31,14 +31,104 @@ static void nft_default_forward_path(str
+                                    struct dst_entry *dst_cache,
+                                    enum ip_conntrack_dir dir)
+ {
++      route->tuple[!dir].in.ifindex   = dst_cache->dev->ifindex;
+       route->tuple[dir].dst           = dst_cache;
+       route->tuple[dir].xmit_type     = nft_xmit_type(dst_cache);
+ }
++static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
++                                   const struct dst_entry *dst_cache,
++                                   const struct nf_conn *ct,
++                                   enum ip_conntrack_dir dir,
++                                   struct net_device_path_stack *stack)
++{
++      const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
++      struct net_device *dev = dst_cache->dev;
++      unsigned char ha[ETH_ALEN];
++      struct neighbour *n;
++      u8 nud_state;
++
++      n = dst_neigh_lookup(dst_cache, daddr);
++      if (!n)
++              return -1;
++
++      read_lock_bh(&n->lock);
++      nud_state = n->nud_state;
++      ether_addr_copy(ha, n->ha);
++      read_unlock_bh(&n->lock);
++      neigh_release(n);
++
++      if (!(nud_state & NUD_VALID))
++              return -1;
++
++      return dev_fill_forward_path(dev, ha, stack);
++}
++
++struct nft_forward_info {
++      const struct net_device *indev;
++};
++
++static void nft_dev_path_info(const struct net_device_path_stack *stack,
++                            struct nft_forward_info *info)
++{
++      const struct net_device_path *path;
++      int i;
++
++      for (i = 0; i < stack->num_paths; i++) {
++              path = &stack->path[i];
++              switch (path->type) {
++              case DEV_PATH_ETHERNET:
++                      info->indev = path->dev;
++                      break;
++              case DEV_PATH_VLAN:
++              case DEV_PATH_BRIDGE:
++              default:
++                      info->indev = NULL;
++                      break;
++              }
++      }
++}
++
++static bool nft_flowtable_find_dev(const struct net_device *dev,
++                                 struct nft_flowtable *ft)
++{
++      struct nft_hook *hook;
++      bool found = false;
++
++      list_for_each_entry_rcu(hook, &ft->hook_list, list) {
++              if (hook->ops.dev != dev)
++                      continue;
++
++              found = true;
++              break;
++      }
++
++      return found;
++}
++
++static void nft_dev_forward_path(struct nf_flow_route *route,
++                               const struct nf_conn *ct,
++                               enum ip_conntrack_dir dir,
++                               struct nft_flowtable *ft)
++{
++      const struct dst_entry *dst = route->tuple[dir].dst;
++      struct net_device_path_stack stack;
++      struct nft_forward_info info = {};
++
++      if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
++              nft_dev_path_info(&stack, &info);
++
++      if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
++              return;
++
++      route->tuple[!dir].in.ifindex = info.indev->ifindex;
++}
++
+ static int nft_flow_route(const struct nft_pktinfo *pkt,
+                         const struct nf_conn *ct,
+                         struct nf_flow_route *route,
+-                        enum ip_conntrack_dir dir)
++                        enum ip_conntrack_dir dir,
++                        struct nft_flowtable *ft)
+ {
+       struct dst_entry *this_dst = skb_dst(pkt->skb);
+       struct dst_entry *other_dst = NULL;
+@@ -63,6 +153,12 @@ static int nft_flow_route(const struct n
+       nft_default_forward_path(route, this_dst, dir);
+       nft_default_forward_path(route, other_dst, !dir);
++      if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH &&
++          route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
++              nft_dev_forward_path(route, ct, dir, ft);
++              nft_dev_forward_path(route, ct, !dir, ft);
++      }
++
+       return 0;
+ }
+@@ -90,8 +186,8 @@ static void nft_flow_offload_eval(const
+       struct nft_flow_offload *priv = nft_expr_priv(expr);
+       struct nf_flowtable *flowtable = &priv->flowtable->data;
+       struct tcphdr _tcph, *tcph = NULL;
++      struct nf_flow_route route = {};
+       enum ip_conntrack_info ctinfo;
+-      struct nf_flow_route route;
+       struct flow_offload *flow;
+       enum ip_conntrack_dir dir;
+       struct nf_conn *ct;
+@@ -128,7 +224,7 @@ static void nft_flow_offload_eval(const
+               goto out;
+       dir = CTINFO2DIR(ctinfo);
+-      if (nft_flow_route(pkt, ct, &route, dir) < 0)
++      if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
+               goto err_flow_route;
+       flow = flow_offload_alloc(ct);
diff --git a/target/linux/generic/backport-5.10/610-v5.13-19-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch b/target/linux/generic/backport-5.10/610-v5.13-19-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch
new file mode 100644 (file)
index 0000000..e42d255
--- /dev/null
@@ -0,0 +1,374 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:40 +0100
+Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
+ obtain egress device
+
+The egress device in the tuple is obtained from route. Use
+dev_fill_forward_path() instead to provide the real egress device for
+this flow whenever this is available.
+
+The new FLOW_OFFLOAD_XMIT_DIRECT type uses dev_queue_xmit() to transmit
+ethernet frames. Cache the source and destination hardware address to
+use dev_queue_xmit() to transfer packets.
+
+The FLOW_OFFLOAD_XMIT_DIRECT replaces FLOW_OFFLOAD_XMIT_NEIGH if
+dev_fill_forward_path() finds a direct transmit path.
+
+In case of topology updates, if peer is moved to different bridge port,
+the connection will time out, reconnect will result in a new entry with
+the correct path. Snooping fdb updates would allow for cleaning up stale
+flowtable entries.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -92,6 +92,7 @@ enum flow_offload_tuple_dir {
+ enum flow_offload_xmit_type {
+       FLOW_OFFLOAD_XMIT_NEIGH         = 0,
+       FLOW_OFFLOAD_XMIT_XFRM,
++      FLOW_OFFLOAD_XMIT_DIRECT,
+ };
+ struct flow_offload_tuple {
+@@ -120,8 +121,14 @@ struct flow_offload_tuple {
+                                       xmit_type:2;
+       u16                             mtu;
+-
+-      struct dst_entry                *dst_cache;
++      union {
++              struct dst_entry        *dst_cache;
++              struct {
++                      u32             ifidx;
++                      u8              h_source[ETH_ALEN];
++                      u8              h_dest[ETH_ALEN];
++              } out;
++      };
+ };
+ struct flow_offload_tuple_rhash {
+@@ -168,6 +175,11 @@ struct nf_flow_route {
+               struct {
+                       u32                     ifindex;
+               } in;
++              struct {
++                      u32                     ifindex;
++                      u8                      h_source[ETH_ALEN];
++                      u8                      h_dest[ETH_ALEN];
++              } out;
+               enum flow_offload_xmit_type     xmit_type;
+       } tuple[FLOW_OFFLOAD_DIR_MAX];
+ };
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -81,9 +81,6 @@ static int flow_offload_fill_route(struc
+       struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
+       struct dst_entry *dst = route->tuple[dir].dst;
+-      if (!dst_hold_safe(route->tuple[dir].dst))
+-              return -1;
+-
+       switch (flow_tuple->l3proto) {
+       case NFPROTO_IPV4:
+               flow_tuple->mtu = ip_dst_mtu_maybe_forward(dst, true);
+@@ -94,12 +91,36 @@ static int flow_offload_fill_route(struc
+       }
+       flow_tuple->iifidx = route->tuple[dir].in.ifindex;
++
++      switch (route->tuple[dir].xmit_type) {
++      case FLOW_OFFLOAD_XMIT_DIRECT:
++              memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest,
++                     ETH_ALEN);
++              memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
++                     ETH_ALEN);
++              flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
++              break;
++      case FLOW_OFFLOAD_XMIT_XFRM:
++      case FLOW_OFFLOAD_XMIT_NEIGH:
++              if (!dst_hold_safe(route->tuple[dir].dst))
++                      return -1;
++
++              flow_tuple->dst_cache = dst;
++              break;
++      }
+       flow_tuple->xmit_type = route->tuple[dir].xmit_type;
+-      flow_tuple->dst_cache = dst;
+       return 0;
+ }
++static void nft_flow_dst_release(struct flow_offload *flow,
++                               enum flow_offload_tuple_dir dir)
++{
++      if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
++          flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
++              dst_release(flow->tuplehash[dir].tuple.dst_cache);
++}
++
+ int flow_offload_route_init(struct flow_offload *flow,
+                           const struct nf_flow_route *route)
+ {
+@@ -118,7 +139,7 @@ int flow_offload_route_init(struct flow_
+       return 0;
+ err_route_reply:
+-      dst_release(route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].dst);
++      nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_ORIGINAL);
+       return err;
+ }
+@@ -169,8 +190,8 @@ static void flow_offload_fixup_ct(struct
+ static void flow_offload_route_release(struct flow_offload *flow)
+ {
+-      dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_cache);
+-      dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_cache);
++      nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_ORIGINAL);
++      nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_REPLY);
+ }
+ void flow_offload_free(struct flow_offload *flow)
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -207,6 +207,24 @@ static unsigned int nf_flow_xmit_xfrm(st
+       return NF_STOLEN;
+ }
++static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
++                                     const struct flow_offload_tuple_rhash *tuplehash,
++                                     unsigned short type)
++{
++      struct net_device *outdev;
++
++      outdev = dev_get_by_index_rcu(net, tuplehash->tuple.out.ifidx);
++      if (!outdev)
++              return NF_DROP;
++
++      skb->dev = outdev;
++      dev_hard_header(skb, skb->dev, type, tuplehash->tuple.out.h_dest,
++                      tuplehash->tuple.out.h_source, skb->len);
++      dev_queue_xmit(skb);
++
++      return NF_STOLEN;
++}
++
+ unsigned int
+ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
+                       const struct nf_hook_state *state)
+@@ -222,6 +240,7 @@ nf_flow_offload_ip_hook(void *priv, stru
+       struct iphdr *iph;
+       __be32 nexthop;
+       u32 hdrsize;
++      int ret;
+       if (skb->protocol != htons(ETH_P_IP))
+               return NF_ACCEPT;
+@@ -244,9 +263,13 @@ nf_flow_offload_ip_hook(void *priv, stru
+       if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+               return NF_ACCEPT;
+-      if (!dst_check(&rt->dst, 0)) {
+-              flow_offload_teardown(flow);
+-              return NF_ACCEPT;
++      if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
++          tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
++              rt = (struct rtable *)tuplehash->tuple.dst_cache;
++              if (!dst_check(&rt->dst, 0)) {
++                      flow_offload_teardown(flow);
++                      return NF_ACCEPT;
++              }
+       }
+       if (skb_try_make_writable(skb, thoff + hdrsize))
+@@ -263,8 +286,6 @@ nf_flow_offload_ip_hook(void *priv, stru
+       if (flow_table->flags & NF_FLOWTABLE_COUNTER)
+               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+-      rt = (struct rtable *)tuplehash->tuple.dst_cache;
+-
+       if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+               memset(skb->cb, 0, sizeof(struct inet_skb_parm));
+               IPCB(skb)->iif = skb->dev->ifindex;
+@@ -272,13 +293,23 @@ nf_flow_offload_ip_hook(void *priv, stru
+               return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+       }
+-      outdev = rt->dst.dev;
+-      skb->dev = outdev;
+-      nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
+-      skb_dst_set_noref(skb, &rt->dst);
+-      neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb);
++      switch (tuplehash->tuple.xmit_type) {
++      case FLOW_OFFLOAD_XMIT_NEIGH:
++              outdev = rt->dst.dev;
++              skb->dev = outdev;
++              nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
++              skb_dst_set_noref(skb, &rt->dst);
++              neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb);
++              ret = NF_STOLEN;
++              break;
++      case FLOW_OFFLOAD_XMIT_DIRECT:
++              ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IP);
++              if (ret == NF_DROP)
++                      flow_offload_teardown(flow);
++              break;
++      }
+-      return NF_STOLEN;
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
+@@ -444,6 +475,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       struct ipv6hdr *ip6h;
+       struct rt6_info *rt;
+       u32 hdrsize;
++      int ret;
+       if (skb->protocol != htons(ETH_P_IPV6))
+               return NF_ACCEPT;
+@@ -465,9 +497,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
+                               sizeof(*ip6h)))
+               return NF_ACCEPT;
+-      if (!dst_check(&rt->dst, 0)) {
+-              flow_offload_teardown(flow);
+-              return NF_ACCEPT;
++      if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
++          tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
++              rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
++              if (!dst_check(&rt->dst, 0)) {
++                      flow_offload_teardown(flow);
++                      return NF_ACCEPT;
++              }
+       }
+       if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
+@@ -484,8 +520,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       if (flow_table->flags & NF_FLOWTABLE_COUNTER)
+               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
+-      rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
+-
+       if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+               memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+               IP6CB(skb)->iif = skb->dev->ifindex;
+@@ -493,12 +527,22 @@ nf_flow_offload_ipv6_hook(void *priv, st
+               return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+       }
+-      outdev = rt->dst.dev;
+-      skb->dev = outdev;
+-      nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
+-      skb_dst_set_noref(skb, &rt->dst);
+-      neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
++      switch (tuplehash->tuple.xmit_type) {
++      case FLOW_OFFLOAD_XMIT_NEIGH:
++              outdev = rt->dst.dev;
++              skb->dev = outdev;
++              nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
++              skb_dst_set_noref(skb, &rt->dst);
++              neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
++              ret = NF_STOLEN;
++              break;
++      case FLOW_OFFLOAD_XMIT_DIRECT:
++              ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IPV6);
++              if (ret == NF_DROP)
++                      flow_offload_teardown(flow);
++              break;
++      }
+-      return NF_STOLEN;
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(nf_flow_offload_ipv6_hook);
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -39,12 +39,11 @@ static void nft_default_forward_path(str
+ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
+                                    const struct dst_entry *dst_cache,
+                                    const struct nf_conn *ct,
+-                                   enum ip_conntrack_dir dir,
++                                   enum ip_conntrack_dir dir, u8 *ha,
+                                    struct net_device_path_stack *stack)
+ {
+       const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
+       struct net_device *dev = dst_cache->dev;
+-      unsigned char ha[ETH_ALEN];
+       struct neighbour *n;
+       u8 nud_state;
+@@ -66,27 +65,43 @@ static int nft_dev_fill_forward_path(con
+ struct nft_forward_info {
+       const struct net_device *indev;
++      const struct net_device *outdev;
++      u8 h_source[ETH_ALEN];
++      u8 h_dest[ETH_ALEN];
++      enum flow_offload_xmit_type xmit_type;
+ };
+ static void nft_dev_path_info(const struct net_device_path_stack *stack,
+-                            struct nft_forward_info *info)
++                            struct nft_forward_info *info,
++                            unsigned char *ha)
+ {
+       const struct net_device_path *path;
+       int i;
++      memcpy(info->h_dest, ha, ETH_ALEN);
++
+       for (i = 0; i < stack->num_paths; i++) {
+               path = &stack->path[i];
+               switch (path->type) {
+               case DEV_PATH_ETHERNET:
+                       info->indev = path->dev;
++                      if (is_zero_ether_addr(info->h_source))
++                              memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
+                       break;
+-              case DEV_PATH_VLAN:
+               case DEV_PATH_BRIDGE:
++                      if (is_zero_ether_addr(info->h_source))
++                              memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
++
++                      info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
++                      break;
++              case DEV_PATH_VLAN:
+               default:
+                       info->indev = NULL;
+                       break;
+               }
+       }
++      if (!info->outdev)
++              info->outdev = info->indev;
+ }
+ static bool nft_flowtable_find_dev(const struct net_device *dev,
+@@ -114,14 +129,22 @@ static void nft_dev_forward_path(struct
+       const struct dst_entry *dst = route->tuple[dir].dst;
+       struct net_device_path_stack stack;
+       struct nft_forward_info info = {};
++      unsigned char ha[ETH_ALEN];
+-      if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
+-              nft_dev_path_info(&stack, &info);
++      if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
++              nft_dev_path_info(&stack, &info, ha);
+       if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
+               return;
+       route->tuple[!dir].in.ifindex = info.indev->ifindex;
++
++      if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
++              memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
++              memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
++              route->tuple[dir].out.ifindex = info.outdev->ifindex;
++              route->tuple[dir].xmit_type = info.xmit_type;
++      }
+ }
+ static int nft_flow_route(const struct nft_pktinfo *pkt,
diff --git a/target/linux/generic/backport-5.10/610-v5.13-20-netfilter-flowtable-add-vlan-support.patch b/target/linux/generic/backport-5.10/610-v5.13-20-netfilter-flowtable-add-vlan-support.patch
new file mode 100644 (file)
index 0000000..976054c
--- /dev/null
@@ -0,0 +1,410 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:41 +0100
+Subject: [PATCH] netfilter: flowtable: add vlan support
+
+Add the vlan id and protocol to the flow tuple to uniquely identify
+flows from the receive path. For the transmit path, dev_hard_header() on
+the vlan device push the headers. This patch includes support for two
+vlan headers (QinQ) from the ingress path.
+
+Add a generic encap field to the flowtable entry which stores the
+protocol and the tag id. This allows to reuse these fields in the PPPoE
+support coming in a later patch.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -95,6 +95,8 @@ enum flow_offload_xmit_type {
+       FLOW_OFFLOAD_XMIT_DIRECT,
+ };
++#define NF_FLOW_TABLE_ENCAP_MAX               2
++
+ struct flow_offload_tuple {
+       union {
+               struct in_addr          src_v4;
+@@ -113,13 +115,17 @@ struct flow_offload_tuple {
+       u8                              l3proto;
+       u8                              l4proto;
++      struct {
++              u16                     id;
++              __be16                  proto;
++      } encap[NF_FLOW_TABLE_ENCAP_MAX];
+       /* All members above are keys for lookups, see flow_offload_hash(). */
+       struct { }                      __hash;
+-      u8                              dir:6,
+-                                      xmit_type:2;
+-
++      u8                              dir:4,
++                                      xmit_type:2,
++                                      encap_num:2;
+       u16                             mtu;
+       union {
+               struct dst_entry        *dst_cache;
+@@ -174,6 +180,11 @@ struct nf_flow_route {
+               struct dst_entry                *dst;
+               struct {
+                       u32                     ifindex;
++                      struct {
++                              u16             id;
++                              __be16          proto;
++                      } encap[NF_FLOW_TABLE_ENCAP_MAX];
++                      u8                      num_encaps;
+               } in;
+               struct {
+                       u32                     ifindex;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -80,6 +80,7 @@ static int flow_offload_fill_route(struc
+ {
+       struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
+       struct dst_entry *dst = route->tuple[dir].dst;
++      int i, j = 0;
+       switch (flow_tuple->l3proto) {
+       case NFPROTO_IPV4:
+@@ -91,6 +92,12 @@ static int flow_offload_fill_route(struc
+       }
+       flow_tuple->iifidx = route->tuple[dir].in.ifindex;
++      for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
++              flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
++              flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
++              j++;
++      }
++      flow_tuple->encap_num = route->tuple[dir].in.num_encaps;
+       switch (route->tuple[dir].xmit_type) {
+       case FLOW_OFFLOAD_XMIT_DIRECT:
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -136,23 +136,44 @@ static bool ip_has_options(unsigned int
+       return thoff != sizeof(struct iphdr);
+ }
++static void nf_flow_tuple_encap(struct sk_buff *skb,
++                              struct flow_offload_tuple *tuple)
++{
++      int i = 0;
++
++      if (skb_vlan_tag_present(skb)) {
++              tuple->encap[i].id = skb_vlan_tag_get(skb);
++              tuple->encap[i].proto = skb->vlan_proto;
++              i++;
++      }
++      if (skb->protocol == htons(ETH_P_8021Q)) {
++              struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
++
++              tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
++              tuple->encap[i].proto = skb->protocol;
++      }
++}
++
+ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
+-                          struct flow_offload_tuple *tuple, u32 *hdrsize)
++                          struct flow_offload_tuple *tuple, u32 *hdrsize,
++                          u32 offset)
+ {
+       struct flow_ports *ports;
+       unsigned int thoff;
+       struct iphdr *iph;
+-      if (!pskb_may_pull(skb, sizeof(*iph)))
++      if (!pskb_may_pull(skb, sizeof(*iph) + offset))
+               return -1;
+-      iph = ip_hdr(skb);
+-      thoff = iph->ihl * 4;
++      iph = (struct iphdr *)(skb_network_header(skb) + offset);
++      thoff = (iph->ihl * 4);
+       if (ip_is_fragment(iph) ||
+           unlikely(ip_has_options(thoff)))
+               return -1;
++      thoff += offset;
++
+       switch (iph->protocol) {
+       case IPPROTO_TCP:
+               *hdrsize = sizeof(struct tcphdr);
+@@ -167,11 +188,10 @@ static int nf_flow_tuple_ip(struct sk_bu
+       if (iph->ttl <= 1)
+               return -1;
+-      thoff = iph->ihl * 4;
+       if (!pskb_may_pull(skb, thoff + *hdrsize))
+               return -1;
+-      iph = ip_hdr(skb);
++      iph = (struct iphdr *)(skb_network_header(skb) + offset);
+       ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
+       tuple->src_v4.s_addr    = iph->saddr;
+@@ -181,6 +201,7 @@ static int nf_flow_tuple_ip(struct sk_bu
+       tuple->l3proto          = AF_INET;
+       tuple->l4proto          = iph->protocol;
+       tuple->iifidx           = dev->ifindex;
++      nf_flow_tuple_encap(skb, tuple);
+       return 0;
+ }
+@@ -207,6 +228,43 @@ static unsigned int nf_flow_xmit_xfrm(st
+       return NF_STOLEN;
+ }
++static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto,
++                                     u32 *offset)
++{
++      if (skb->protocol == htons(ETH_P_8021Q)) {
++              struct vlan_ethhdr *veth;
++
++              veth = (struct vlan_ethhdr *)skb_mac_header(skb);
++              if (veth->h_vlan_encapsulated_proto == proto) {
++                      *offset += VLAN_HLEN;
++                      return true;
++              }
++      }
++
++      return false;
++}
++
++static void nf_flow_encap_pop(struct sk_buff *skb,
++                            struct flow_offload_tuple_rhash *tuplehash)
++{
++      struct vlan_hdr *vlan_hdr;
++      int i;
++
++      for (i = 0; i < tuplehash->tuple.encap_num; i++) {
++              if (skb_vlan_tag_present(skb)) {
++                      __vlan_hwaccel_clear_tag(skb);
++                      continue;
++              }
++              if (skb->protocol == htons(ETH_P_8021Q)) {
++                      vlan_hdr = (struct vlan_hdr *)skb->data;
++                      __skb_pull(skb, VLAN_HLEN);
++                      vlan_set_encap_proto(skb, vlan_hdr);
++                      skb_reset_network_header(skb);
++                      break;
++              }
++      }
++}
++
+ static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
+                                      const struct flow_offload_tuple_rhash *tuplehash,
+                                      unsigned short type)
+@@ -235,17 +293,18 @@ nf_flow_offload_ip_hook(void *priv, stru
+       enum flow_offload_tuple_dir dir;
+       struct flow_offload *flow;
+       struct net_device *outdev;
++      u32 hdrsize, offset = 0;
++      unsigned int thoff, mtu;
+       struct rtable *rt;
+-      unsigned int thoff;
+       struct iphdr *iph;
+       __be32 nexthop;
+-      u32 hdrsize;
+       int ret;
+-      if (skb->protocol != htons(ETH_P_IP))
++      if (skb->protocol != htons(ETH_P_IP) &&
++          !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IP), &offset))
+               return NF_ACCEPT;
+-      if (nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize) < 0)
++      if (nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize, offset) < 0)
+               return NF_ACCEPT;
+       tuplehash = flow_offload_lookup(flow_table, &tuple);
+@@ -255,11 +314,12 @@ nf_flow_offload_ip_hook(void *priv, stru
+       dir = tuplehash->tuple.dir;
+       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+-      if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
++      mtu = flow->tuplehash[dir].tuple.mtu + offset;
++      if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
+               return NF_ACCEPT;
+-      iph = ip_hdr(skb);
+-      thoff = iph->ihl * 4;
++      iph = (struct iphdr *)(skb_network_header(skb) + offset);
++      thoff = (iph->ihl * 4) + offset;
+       if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+               return NF_ACCEPT;
+@@ -277,6 +337,9 @@ nf_flow_offload_ip_hook(void *priv, stru
+       flow_offload_refresh(flow_table, flow);
++      nf_flow_encap_pop(skb, tuplehash);
++      thoff -= offset;
++
+       iph = ip_hdr(skb);
+       nf_flow_nat_ip(flow, skb, thoff, dir, iph);
+@@ -418,16 +481,18 @@ static void nf_flow_nat_ipv6(const struc
+ }
+ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
+-                            struct flow_offload_tuple *tuple, u32 *hdrsize)
++                            struct flow_offload_tuple *tuple, u32 *hdrsize,
++                            u32 offset)
+ {
+       struct flow_ports *ports;
+       struct ipv6hdr *ip6h;
+       unsigned int thoff;
+-      if (!pskb_may_pull(skb, sizeof(*ip6h)))
++      thoff = sizeof(*ip6h) + offset;
++      if (!pskb_may_pull(skb, thoff))
+               return -1;
+-      ip6h = ipv6_hdr(skb);
++      ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
+       switch (ip6h->nexthdr) {
+       case IPPROTO_TCP:
+@@ -443,11 +508,10 @@ static int nf_flow_tuple_ipv6(struct sk_
+       if (ip6h->hop_limit <= 1)
+               return -1;
+-      thoff = sizeof(*ip6h);
+       if (!pskb_may_pull(skb, thoff + *hdrsize))
+               return -1;
+-      ip6h = ipv6_hdr(skb);
++      ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
+       ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
+       tuple->src_v6           = ip6h->saddr;
+@@ -457,6 +521,7 @@ static int nf_flow_tuple_ipv6(struct sk_
+       tuple->l3proto          = AF_INET6;
+       tuple->l4proto          = ip6h->nexthdr;
+       tuple->iifidx           = dev->ifindex;
++      nf_flow_tuple_encap(skb, tuple);
+       return 0;
+ }
+@@ -472,15 +537,17 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       const struct in6_addr *nexthop;
+       struct flow_offload *flow;
+       struct net_device *outdev;
++      unsigned int thoff, mtu;
++      u32 hdrsize, offset = 0;
+       struct ipv6hdr *ip6h;
+       struct rt6_info *rt;
+-      u32 hdrsize;
+       int ret;
+-      if (skb->protocol != htons(ETH_P_IPV6))
++      if (skb->protocol != htons(ETH_P_IPV6) &&
++          !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IPV6), &offset))
+               return NF_ACCEPT;
+-      if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &hdrsize) < 0)
++      if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &hdrsize, offset) < 0)
+               return NF_ACCEPT;
+       tuplehash = flow_offload_lookup(flow_table, &tuple);
+@@ -490,11 +557,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
+       dir = tuplehash->tuple.dir;
+       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+-      if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
++      mtu = flow->tuplehash[dir].tuple.mtu + offset;
++      if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
+               return NF_ACCEPT;
+-      if (nf_flow_state_check(flow, ipv6_hdr(skb)->nexthdr, skb,
+-                              sizeof(*ip6h)))
++      ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
++      thoff = sizeof(*ip6h) + offset;
++      if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff))
+               return NF_ACCEPT;
+       if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
+@@ -506,11 +575,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
+               }
+       }
+-      if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
++      if (skb_try_make_writable(skb, thoff + hdrsize))
+               return NF_DROP;
+       flow_offload_refresh(flow_table, flow);
++      nf_flow_encap_pop(skb, tuplehash);
++
+       ip6h = ipv6_hdr(skb);
+       nf_flow_nat_ipv6(flow, skb, dir, ip6h);
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -66,6 +66,11 @@ static int nft_dev_fill_forward_path(con
+ struct nft_forward_info {
+       const struct net_device *indev;
+       const struct net_device *outdev;
++      struct id {
++              __u16   id;
++              __be16  proto;
++      } encap[NF_FLOW_TABLE_ENCAP_MAX];
++      u8 num_encaps;
+       u8 h_source[ETH_ALEN];
+       u8 h_dest[ETH_ALEN];
+       enum flow_offload_xmit_type xmit_type;
+@@ -84,9 +89,23 @@ static void nft_dev_path_info(const stru
+               path = &stack->path[i];
+               switch (path->type) {
+               case DEV_PATH_ETHERNET:
++              case DEV_PATH_VLAN:
+                       info->indev = path->dev;
+                       if (is_zero_ether_addr(info->h_source))
+                               memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
++
++                      if (path->type == DEV_PATH_ETHERNET)
++                              break;
++
++                      /* DEV_PATH_VLAN */
++                      if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
++                              info->indev = NULL;
++                              break;
++                      }
++                      info->outdev = path->dev;
++                      info->encap[info->num_encaps].id = path->encap.id;
++                      info->encap[info->num_encaps].proto = path->encap.proto;
++                      info->num_encaps++;
+                       break;
+               case DEV_PATH_BRIDGE:
+                       if (is_zero_ether_addr(info->h_source))
+@@ -94,7 +113,6 @@ static void nft_dev_path_info(const stru
+                       info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
+                       break;
+-              case DEV_PATH_VLAN:
+               default:
+                       info->indev = NULL;
+                       break;
+@@ -130,6 +148,7 @@ static void nft_dev_forward_path(struct
+       struct net_device_path_stack stack;
+       struct nft_forward_info info = {};
+       unsigned char ha[ETH_ALEN];
++      int i;
+       if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
+               nft_dev_path_info(&stack, &info, ha);
+@@ -138,6 +157,11 @@ static void nft_dev_forward_path(struct
+               return;
+       route->tuple[!dir].in.ifindex = info.indev->ifindex;
++      for (i = 0; i < info.num_encaps; i++) {
++              route->tuple[!dir].in.encap[i].id = info.encap[i].id;
++              route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
++      }
++      route->tuple[!dir].in.num_encaps = info.num_encaps;
+       if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
+               memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
diff --git a/target/linux/generic/backport-5.10/610-v5.13-21-netfilter-flowtable-add-bridge-vlan-filtering-suppor.patch b/target/linux/generic/backport-5.10/610-v5.13-21-netfilter-flowtable-add-bridge-vlan-filtering-suppor.patch
new file mode 100644 (file)
index 0000000..cb3ef99
--- /dev/null
@@ -0,0 +1,30 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:42 +0100
+Subject: [PATCH] netfilter: flowtable: add bridge vlan filtering support
+
+Add the vlan tag based when PVID is set on.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -111,6 +111,18 @@ static void nft_dev_path_info(const stru
+                       if (is_zero_ether_addr(info->h_source))
+                               memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
++                      switch (path->bridge.vlan_mode) {
++                      case DEV_PATH_BR_VLAN_TAG:
++                              info->encap[info->num_encaps].id = path->bridge.vlan_id;
++                              info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
++                              info->num_encaps++;
++                              break;
++                      case DEV_PATH_BR_VLAN_UNTAG:
++                              info->num_encaps--;
++                              break;
++                      case DEV_PATH_BR_VLAN_KEEP:
++                              break;
++                      }
+                       info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
+                       break;
+               default:
diff --git a/target/linux/generic/backport-5.10/610-v5.13-22-netfilter-flowtable-add-pppoe-support.patch b/target/linux/generic/backport-5.10/610-v5.13-22-netfilter-flowtable-add-pppoe-support.patch
new file mode 100644 (file)
index 0000000..d5789cb
--- /dev/null
@@ -0,0 +1,145 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:43 +0100
+Subject: [PATCH] netfilter: flowtable: add pppoe support
+
+Add the PPPoE protocol and session id to the flow tuple using the encap
+fields to uniquely identify flows from the receive path. For the
+transmit path, dev_hard_header() on the vlan device push the headers.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_ip.c
++++ b/net/netfilter/nf_flow_table_ip.c
+@@ -7,6 +7,9 @@
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>
+ #include <linux/netdevice.h>
++#include <linux/if_ether.h>
++#include <linux/if_pppox.h>
++#include <linux/ppp_defs.h>
+ #include <net/ip.h>
+ #include <net/ipv6.h>
+ #include <net/ip6_route.h>
+@@ -139,6 +142,8 @@ static bool ip_has_options(unsigned int
+ static void nf_flow_tuple_encap(struct sk_buff *skb,
+                               struct flow_offload_tuple *tuple)
+ {
++      struct vlan_ethhdr *veth;
++      struct pppoe_hdr *phdr;
+       int i = 0;
+       if (skb_vlan_tag_present(skb)) {
+@@ -146,11 +151,17 @@ static void nf_flow_tuple_encap(struct s
+               tuple->encap[i].proto = skb->vlan_proto;
+               i++;
+       }
+-      if (skb->protocol == htons(ETH_P_8021Q)) {
+-              struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
+-
++      switch (skb->protocol) {
++      case htons(ETH_P_8021Q):
++              veth = (struct vlan_ethhdr *)skb_mac_header(skb);
+               tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
+               tuple->encap[i].proto = skb->protocol;
++              break;
++      case htons(ETH_P_PPP_SES):
++              phdr = (struct pppoe_hdr *)skb_mac_header(skb);
++              tuple->encap[i].id = ntohs(phdr->sid);
++              tuple->encap[i].proto = skb->protocol;
++              break;
+       }
+ }
+@@ -228,17 +239,41 @@ static unsigned int nf_flow_xmit_xfrm(st
+       return NF_STOLEN;
+ }
++static inline __be16 nf_flow_pppoe_proto(const struct sk_buff *skb)
++{
++      __be16 proto;
++
++      proto = *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
++                           sizeof(struct pppoe_hdr)));
++      switch (proto) {
++      case htons(PPP_IP):
++              return htons(ETH_P_IP);
++      case htons(PPP_IPV6):
++              return htons(ETH_P_IPV6);
++      }
++
++      return 0;
++}
++
+ static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto,
+                                      u32 *offset)
+ {
+-      if (skb->protocol == htons(ETH_P_8021Q)) {
+-              struct vlan_ethhdr *veth;
++      struct vlan_ethhdr *veth;
++      switch (skb->protocol) {
++      case htons(ETH_P_8021Q):
+               veth = (struct vlan_ethhdr *)skb_mac_header(skb);
+               if (veth->h_vlan_encapsulated_proto == proto) {
+                       *offset += VLAN_HLEN;
+                       return true;
+               }
++              break;
++      case htons(ETH_P_PPP_SES):
++              if (nf_flow_pppoe_proto(skb) == proto) {
++                      *offset += PPPOE_SES_HLEN;
++                      return true;
++              }
++              break;
+       }
+       return false;
+@@ -255,12 +290,18 @@ static void nf_flow_encap_pop(struct sk_
+                       __vlan_hwaccel_clear_tag(skb);
+                       continue;
+               }
+-              if (skb->protocol == htons(ETH_P_8021Q)) {
++              switch (skb->protocol) {
++              case htons(ETH_P_8021Q):
+                       vlan_hdr = (struct vlan_hdr *)skb->data;
+                       __skb_pull(skb, VLAN_HLEN);
+                       vlan_set_encap_proto(skb, vlan_hdr);
+                       skb_reset_network_header(skb);
+                       break;
++              case htons(ETH_P_PPP_SES):
++                      skb->protocol = nf_flow_pppoe_proto(skb);
++                      skb_pull(skb, PPPOE_SES_HLEN);
++                      skb_reset_network_header(skb);
++                      break;
+               }
+       }
+ }
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -90,6 +90,7 @@ static void nft_dev_path_info(const stru
+               switch (path->type) {
+               case DEV_PATH_ETHERNET:
+               case DEV_PATH_VLAN:
++              case DEV_PATH_PPPOE:
+                       info->indev = path->dev;
+                       if (is_zero_ether_addr(info->h_source))
+                               memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
+@@ -97,7 +98,7 @@ static void nft_dev_path_info(const stru
+                       if (path->type == DEV_PATH_ETHERNET)
+                               break;
+-                      /* DEV_PATH_VLAN */
++                      /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
+                       if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
+                               info->indev = NULL;
+                               break;
+@@ -106,6 +107,8 @@ static void nft_dev_path_info(const stru
+                       info->encap[info->num_encaps].id = path->encap.id;
+                       info->encap[info->num_encaps].proto = path->encap.proto;
+                       info->num_encaps++;
++                      if (path->type == DEV_PATH_PPPOE)
++                              memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN);
+                       break;
+               case DEV_PATH_BRIDGE:
+                       if (is_zero_ether_addr(info->h_source))
diff --git a/target/linux/generic/backport-5.10/610-v5.13-23-netfilter-flowtable-add-dsa-support.patch b/target/linux/generic/backport-5.10/610-v5.13-23-netfilter-flowtable-add-dsa-support.patch
new file mode 100644 (file)
index 0000000..b493183
--- /dev/null
@@ -0,0 +1,32 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:44 +0100
+Subject: [PATCH] netfilter: flowtable: add dsa support
+
+Replace the master ethernet device by the dsa slave port. Packets coming
+in from the software ingress path use the dsa slave port as input
+device.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -89,6 +89,7 @@ static void nft_dev_path_info(const stru
+               path = &stack->path[i];
+               switch (path->type) {
+               case DEV_PATH_ETHERNET:
++              case DEV_PATH_DSA:
+               case DEV_PATH_VLAN:
+               case DEV_PATH_PPPOE:
+                       info->indev = path->dev;
+@@ -97,6 +98,10 @@ static void nft_dev_path_info(const stru
+                       if (path->type == DEV_PATH_ETHERNET)
+                               break;
++                      if (path->type == DEV_PATH_DSA) {
++                              i = stack->num_paths;
++                              break;
++                      }
+                       /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
+                       if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
diff --git a/target/linux/generic/backport-5.10/610-v5.13-24-selftests-netfilter-flowtable-bridge-and-vlan-suppor.patch b/target/linux/generic/backport-5.10/610-v5.13-24-selftests-netfilter-flowtable-bridge-and-vlan-suppor.patch
new file mode 100644 (file)
index 0000000..4924fda
--- /dev/null
@@ -0,0 +1,107 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:45 +0100
+Subject: [PATCH] selftests: netfilter: flowtable bridge and vlan support
+
+This patch adds two new tests to cover bridge and vlan support:
+
+- Add a bridge device to the Router1 (nsr1) container and attach the
+  veth0 device to the bridge. Set the IP address to the bridge device
+  to exercise the bridge forwarding path.
+
+- Add vlan encapsulation between to the bridge device in the Router1 and
+  one of the sender containers (ns1).
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/tools/testing/selftests/netfilter/nft_flowtable.sh
++++ b/tools/testing/selftests/netfilter/nft_flowtable.sh
+@@ -371,6 +371,88 @@ else
+       ip netns exec nsr1 nft list ruleset
+ fi
++# Another test:
++# Add bridge interface br0 to Router1, with NAT enabled.
++ip -net nsr1 link add name br0 type bridge
++ip -net nsr1 addr flush dev veth0
++ip -net nsr1 link set up dev veth0
++ip -net nsr1 link set veth0 master br0
++ip -net nsr1 addr add 10.0.1.1/24 dev br0
++ip -net nsr1 addr add dead:1::1/64 dev br0
++ip -net nsr1 link set up dev br0
++
++ip netns exec nsr1 sysctl net.ipv4.conf.br0.forwarding=1 > /dev/null
++
++# br0 with NAT enabled.
++ip netns exec nsr1 nft -f - <<EOF
++flush table ip nat
++table ip nat {
++   chain prerouting {
++      type nat hook prerouting priority 0; policy accept;
++      meta iif "br0" ip daddr 10.6.6.6 tcp dport 1666 counter dnat ip to 10.0.2.99:12345
++   }
++
++   chain postrouting {
++      type nat hook postrouting priority 0; policy accept;
++      meta oifname "veth1" counter masquerade
++   }
++}
++EOF
++
++if test_tcp_forwarding_nat ns1 ns2; then
++      echo "PASS: flow offloaded for ns1/ns2 with bridge NAT"
++else
++      echo "FAIL: flow offload for ns1/ns2 with bridge NAT" 1>&2
++      ip netns exec nsr1 nft list ruleset
++      ret=1
++fi
++
++# Another test:
++# Add bridge interface br0 to Router1, with NAT and VLAN.
++ip -net nsr1 link set veth0 nomaster
++ip -net nsr1 link set down dev veth0
++ip -net nsr1 link add link veth0 name veth0.10 type vlan id 10
++ip -net nsr1 link set up dev veth0
++ip -net nsr1 link set up dev veth0.10
++ip -net nsr1 link set veth0.10 master br0
++
++ip -net ns1 addr flush dev eth0
++ip -net ns1 link add link eth0 name eth0.10 type vlan id 10
++ip -net ns1 link set eth0 up
++ip -net ns1 link set eth0.10 up
++ip -net ns1 addr add 10.0.1.99/24 dev eth0.10
++ip -net ns1 route add default via 10.0.1.1
++ip -net ns1 addr add dead:1::99/64 dev eth0.10
++
++if test_tcp_forwarding_nat ns1 ns2; then
++      echo "PASS: flow offloaded for ns1/ns2 with bridge NAT and VLAN"
++else
++      echo "FAIL: flow offload for ns1/ns2 with bridge NAT and VLAN" 1>&2
++      ip netns exec nsr1 nft list ruleset
++      ret=1
++fi
++
++# restore test topology (remove bridge and VLAN)
++ip -net nsr1 link set veth0 nomaster
++ip -net nsr1 link set veth0 down
++ip -net nsr1 link set veth0.10 down
++ip -net nsr1 link delete veth0.10 type vlan
++ip -net nsr1 link delete br0 type bridge
++ip -net ns1 addr flush dev eth0.10
++ip -net ns1 link set eth0.10 down
++ip -net ns1 link set eth0 down
++ip -net ns1 link delete eth0.10 type vlan
++
++# restore address in ns1 and nsr1
++ip -net ns1 link set eth0 up
++ip -net ns1 addr add 10.0.1.99/24 dev eth0
++ip -net ns1 route add default via 10.0.1.1
++ip -net ns1 addr add dead:1::99/64 dev eth0
++ip -net ns1 route add default via dead:1::1
++ip -net nsr1 addr add 10.0.1.1/24 dev veth0
++ip -net nsr1 addr add dead:1::1/64 dev veth0
++ip -net nsr1 link set up dev veth0
++
+ KEY_SHA="0x"$(ps -xaf | sha1sum | cut -d " " -f 1)
+ KEY_AES="0x"$(ps -xaf | md5sum | cut -d " " -f 1)
+ SPI1=$RANDOM
diff --git a/target/linux/generic/backport-5.10/610-v5.13-25-netfilter-flowtable-add-offload-support-for-xmit-pat.patch b/target/linux/generic/backport-5.10/610-v5.13-25-netfilter-flowtable-add-offload-support-for-xmit-pat.patch
new file mode 100644 (file)
index 0000000..7b6ec68
--- /dev/null
@@ -0,0 +1,310 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:46 +0100
+Subject: [PATCH] netfilter: flowtable: add offload support for xmit path
+ types
+
+When the flow tuple xmit_type is set to FLOW_OFFLOAD_XMIT_DIRECT, the
+dst_cache pointer is not valid, and the h_source/h_dest/ifidx out fields
+need to be used.
+
+This patch also adds the FLOW_ACTION_VLAN_PUSH action to pass the VLAN
+tag to the driver.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -177,28 +177,45 @@ static int flow_offload_eth_src(struct n
+                               enum flow_offload_tuple_dir dir,
+                               struct nf_flow_rule *flow_rule)
+ {
+-      const struct flow_offload_tuple *tuple = &flow->tuplehash[!dir].tuple;
+       struct flow_action_entry *entry0 = flow_action_entry_next(flow_rule);
+       struct flow_action_entry *entry1 = flow_action_entry_next(flow_rule);
+-      struct net_device *dev;
++      const struct flow_offload_tuple *other_tuple, *this_tuple;
++      struct net_device *dev = NULL;
++      const unsigned char *addr;
+       u32 mask, val;
+       u16 val16;
+-      dev = dev_get_by_index(net, tuple->iifidx);
+-      if (!dev)
+-              return -ENOENT;
++      this_tuple = &flow->tuplehash[dir].tuple;
++
++      switch (this_tuple->xmit_type) {
++      case FLOW_OFFLOAD_XMIT_DIRECT:
++              addr = this_tuple->out.h_source;
++              break;
++      case FLOW_OFFLOAD_XMIT_NEIGH:
++              other_tuple = &flow->tuplehash[!dir].tuple;
++              dev = dev_get_by_index(net, other_tuple->iifidx);
++              if (!dev)
++                      return -ENOENT;
++
++              addr = dev->dev_addr;
++              break;
++      default:
++              return -EOPNOTSUPP;
++      }
+       mask = ~0xffff0000;
+-      memcpy(&val16, dev->dev_addr, 2);
++      memcpy(&val16, addr, 2);
+       val = val16 << 16;
+       flow_offload_mangle(entry0, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4,
+                           &val, &mask);
+       mask = ~0xffffffff;
+-      memcpy(&val, dev->dev_addr + 2, 4);
++      memcpy(&val, addr + 2, 4);
+       flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 8,
+                           &val, &mask);
+-      dev_put(dev);
++
++      if (dev)
++              dev_put(dev);
+       return 0;
+ }
+@@ -210,27 +227,40 @@ static int flow_offload_eth_dst(struct n
+ {
+       struct flow_action_entry *entry0 = flow_action_entry_next(flow_rule);
+       struct flow_action_entry *entry1 = flow_action_entry_next(flow_rule);
+-      const void *daddr = &flow->tuplehash[!dir].tuple.src_v4;
++      const struct flow_offload_tuple *other_tuple, *this_tuple;
+       const struct dst_entry *dst_cache;
+       unsigned char ha[ETH_ALEN];
+       struct neighbour *n;
++      const void *daddr;
+       u32 mask, val;
+       u8 nud_state;
+       u16 val16;
+-      dst_cache = flow->tuplehash[dir].tuple.dst_cache;
+-      n = dst_neigh_lookup(dst_cache, daddr);
+-      if (!n)
+-              return -ENOENT;
+-
+-      read_lock_bh(&n->lock);
+-      nud_state = n->nud_state;
+-      ether_addr_copy(ha, n->ha);
+-      read_unlock_bh(&n->lock);
++      this_tuple = &flow->tuplehash[dir].tuple;
+-      if (!(nud_state & NUD_VALID)) {
++      switch (this_tuple->xmit_type) {
++      case FLOW_OFFLOAD_XMIT_DIRECT:
++              ether_addr_copy(ha, this_tuple->out.h_dest);
++              break;
++      case FLOW_OFFLOAD_XMIT_NEIGH:
++              other_tuple = &flow->tuplehash[!dir].tuple;
++              daddr = &other_tuple->src_v4;
++              dst_cache = this_tuple->dst_cache;
++              n = dst_neigh_lookup(dst_cache, daddr);
++              if (!n)
++                      return -ENOENT;
++
++              read_lock_bh(&n->lock);
++              nud_state = n->nud_state;
++              ether_addr_copy(ha, n->ha);
++              read_unlock_bh(&n->lock);
+               neigh_release(n);
+-              return -ENOENT;
++
++              if (!(nud_state & NUD_VALID))
++                      return -ENOENT;
++              break;
++      default:
++              return -EOPNOTSUPP;
+       }
+       mask = ~0xffffffff;
+@@ -243,7 +273,6 @@ static int flow_offload_eth_dst(struct n
+       val = val16;
+       flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4,
+                           &val, &mask);
+-      neigh_release(n);
+       return 0;
+ }
+@@ -465,27 +494,52 @@ static void flow_offload_ipv4_checksum(s
+       }
+ }
+-static void flow_offload_redirect(const struct flow_offload *flow,
++static void flow_offload_redirect(struct net *net,
++                                const struct flow_offload *flow,
+                                 enum flow_offload_tuple_dir dir,
+                                 struct nf_flow_rule *flow_rule)
+ {
+-      struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
+-      struct rtable *rt;
++      const struct flow_offload_tuple *this_tuple, *other_tuple;
++      struct flow_action_entry *entry;
++      struct net_device *dev;
++      int ifindex;
++
++      this_tuple = &flow->tuplehash[dir].tuple;
++      switch (this_tuple->xmit_type) {
++      case FLOW_OFFLOAD_XMIT_DIRECT:
++              this_tuple = &flow->tuplehash[dir].tuple;
++              ifindex = this_tuple->out.ifidx;
++              break;
++      case FLOW_OFFLOAD_XMIT_NEIGH:
++              other_tuple = &flow->tuplehash[!dir].tuple;
++              ifindex = other_tuple->iifidx;
++              break;
++      default:
++              return;
++      }
+-      rt = (struct rtable *)flow->tuplehash[dir].tuple.dst_cache;
++      dev = dev_get_by_index(net, ifindex);
++      if (!dev)
++              return;
++
++      entry = flow_action_entry_next(flow_rule);
+       entry->id = FLOW_ACTION_REDIRECT;
+-      entry->dev = rt->dst.dev;
+-      dev_hold(rt->dst.dev);
++      entry->dev = dev;
+ }
+ static void flow_offload_encap_tunnel(const struct flow_offload *flow,
+                                     enum flow_offload_tuple_dir dir,
+                                     struct nf_flow_rule *flow_rule)
+ {
++      const struct flow_offload_tuple *this_tuple;
+       struct flow_action_entry *entry;
+       struct dst_entry *dst;
+-      dst = flow->tuplehash[dir].tuple.dst_cache;
++      this_tuple = &flow->tuplehash[dir].tuple;
++      if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
++              return;
++
++      dst = this_tuple->dst_cache;
+       if (dst && dst->lwtstate) {
+               struct ip_tunnel_info *tun_info;
+@@ -502,10 +556,15 @@ static void flow_offload_decap_tunnel(co
+                                     enum flow_offload_tuple_dir dir,
+                                     struct nf_flow_rule *flow_rule)
+ {
++      const struct flow_offload_tuple *other_tuple;
+       struct flow_action_entry *entry;
+       struct dst_entry *dst;
+-      dst = flow->tuplehash[!dir].tuple.dst_cache;
++      other_tuple = &flow->tuplehash[!dir].tuple;
++      if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
++              return;
++
++      dst = other_tuple->dst_cache;
+       if (dst && dst->lwtstate) {
+               struct ip_tunnel_info *tun_info;
+@@ -517,10 +576,14 @@ static void flow_offload_decap_tunnel(co
+       }
+ }
+-int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
+-                          enum flow_offload_tuple_dir dir,
+-                          struct nf_flow_rule *flow_rule)
++static int
++nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
++                        enum flow_offload_tuple_dir dir,
++                        struct nf_flow_rule *flow_rule)
+ {
++      const struct flow_offload_tuple *other_tuple;
++      int i;
++
+       flow_offload_decap_tunnel(flow, dir, flow_rule);
+       flow_offload_encap_tunnel(flow, dir, flow_rule);
+@@ -528,6 +591,26 @@ int nf_flow_rule_route_ipv4(struct net *
+           flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
+               return -1;
++      other_tuple = &flow->tuplehash[!dir].tuple;
++
++      for (i = 0; i < other_tuple->encap_num; i++) {
++              struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
++
++              entry->id = FLOW_ACTION_VLAN_PUSH;
++              entry->vlan.vid = other_tuple->encap[i].id;
++              entry->vlan.proto = other_tuple->encap[i].proto;
++      }
++
++      return 0;
++}
++
++int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
++                          enum flow_offload_tuple_dir dir,
++                          struct nf_flow_rule *flow_rule)
++{
++      if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
++              return -1;
++
+       if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
+               flow_offload_ipv4_snat(net, flow, dir, flow_rule);
+               flow_offload_port_snat(net, flow, dir, flow_rule);
+@@ -540,7 +623,7 @@ int nf_flow_rule_route_ipv4(struct net *
+           test_bit(NF_FLOW_DNAT, &flow->flags))
+               flow_offload_ipv4_checksum(net, flow, flow_rule);
+-      flow_offload_redirect(flow, dir, flow_rule);
++      flow_offload_redirect(net, flow, dir, flow_rule);
+       return 0;
+ }
+@@ -550,11 +633,7 @@ int nf_flow_rule_route_ipv6(struct net *
+                           enum flow_offload_tuple_dir dir,
+                           struct nf_flow_rule *flow_rule)
+ {
+-      flow_offload_decap_tunnel(flow, dir, flow_rule);
+-      flow_offload_encap_tunnel(flow, dir, flow_rule);
+-
+-      if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
+-          flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
++      if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
+               return -1;
+       if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
+@@ -566,7 +645,7 @@ int nf_flow_rule_route_ipv6(struct net *
+               flow_offload_port_dnat(net, flow, dir, flow_rule);
+       }
+-      flow_offload_redirect(flow, dir, flow_rule);
++      flow_offload_redirect(net, flow, dir, flow_rule);
+       return 0;
+ }
+@@ -580,10 +659,10 @@ nf_flow_offload_rule_alloc(struct net *n
+                          enum flow_offload_tuple_dir dir)
+ {
+       const struct nf_flowtable *flowtable = offload->flowtable;
++      const struct flow_offload_tuple *tuple, *other_tuple;
+       const struct flow_offload *flow = offload->flow;
+-      const struct flow_offload_tuple *tuple;
++      struct dst_entry *other_dst = NULL;
+       struct nf_flow_rule *flow_rule;
+-      struct dst_entry *other_dst;
+       int err = -ENOMEM;
+       flow_rule = kzalloc(sizeof(*flow_rule), GFP_KERNEL);
+@@ -599,7 +678,10 @@ nf_flow_offload_rule_alloc(struct net *n
+       flow_rule->rule->match.key = &flow_rule->match.key;
+       tuple = &flow->tuplehash[dir].tuple;
+-      other_dst = flow->tuplehash[!dir].tuple.dst_cache;
++      other_tuple = &flow->tuplehash[!dir].tuple;
++      if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH)
++              other_dst = other_tuple->dst_cache;
++
+       err = nf_flow_rule_match(&flow_rule->match, tuple, other_dst);
+       if (err < 0)
+               goto err_flow_match;
diff --git a/target/linux/generic/backport-5.10/610-v5.13-26-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch b/target/linux/generic/backport-5.10/610-v5.13-26-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch
new file mode 100644 (file)
index 0000000..98ecf98
--- /dev/null
@@ -0,0 +1,114 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:47 +0100
+Subject: [PATCH] netfilter: nft_flow_offload: use direct xmit if
+ hardware offload is enabled
+
+If there is a forward path to reach an ethernet device and hardware
+offload is enabled, then use the direct xmit path.
+
+Moreover, store the real device in the direct xmit path info since
+software datapath uses dev_hard_header() to push the layer encapsulation
+headers while hardware offload refers to the real device.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -131,6 +131,7 @@ struct flow_offload_tuple {
+               struct dst_entry        *dst_cache;
+               struct {
+                       u32             ifidx;
++                      u32             hw_ifidx;
+                       u8              h_source[ETH_ALEN];
+                       u8              h_dest[ETH_ALEN];
+               } out;
+@@ -188,6 +189,7 @@ struct nf_flow_route {
+               } in;
+               struct {
+                       u32                     ifindex;
++                      u32                     hw_ifindex;
+                       u8                      h_source[ETH_ALEN];
+                       u8                      h_dest[ETH_ALEN];
+               } out;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -106,6 +106,7 @@ static int flow_offload_fill_route(struc
+               memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
+                      ETH_ALEN);
+               flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
++              flow_tuple->out.hw_ifidx = route->tuple[dir].out.hw_ifindex;
+               break;
+       case FLOW_OFFLOAD_XMIT_XFRM:
+       case FLOW_OFFLOAD_XMIT_NEIGH:
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -508,7 +508,7 @@ static void flow_offload_redirect(struct
+       switch (this_tuple->xmit_type) {
+       case FLOW_OFFLOAD_XMIT_DIRECT:
+               this_tuple = &flow->tuplehash[dir].tuple;
+-              ifindex = this_tuple->out.ifidx;
++              ifindex = this_tuple->out.hw_ifidx;
+               break;
+       case FLOW_OFFLOAD_XMIT_NEIGH:
+               other_tuple = &flow->tuplehash[!dir].tuple;
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -66,6 +66,7 @@ static int nft_dev_fill_forward_path(con
+ struct nft_forward_info {
+       const struct net_device *indev;
+       const struct net_device *outdev;
++      const struct net_device *hw_outdev;
+       struct id {
+               __u16   id;
+               __be16  proto;
+@@ -76,9 +77,18 @@ struct nft_forward_info {
+       enum flow_offload_xmit_type xmit_type;
+ };
++static bool nft_is_valid_ether_device(const struct net_device *dev)
++{
++      if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
++          dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr))
++              return false;
++
++      return true;
++}
++
+ static void nft_dev_path_info(const struct net_device_path_stack *stack,
+                             struct nft_forward_info *info,
+-                            unsigned char *ha)
++                            unsigned char *ha, struct nf_flowtable *flowtable)
+ {
+       const struct net_device_path *path;
+       int i;
+@@ -140,6 +150,12 @@ static void nft_dev_path_info(const stru
+       }
+       if (!info->outdev)
+               info->outdev = info->indev;
++
++      info->hw_outdev = info->indev;
++
++      if (nf_flowtable_hw_offload(flowtable) &&
++          nft_is_valid_ether_device(info->indev))
++              info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
+ }
+ static bool nft_flowtable_find_dev(const struct net_device *dev,
+@@ -171,7 +187,7 @@ static void nft_dev_forward_path(struct
+       int i;
+       if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
+-              nft_dev_path_info(&stack, &info, ha);
++              nft_dev_path_info(&stack, &info, ha, &ft->data);
+       if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
+               return;
+@@ -187,6 +203,7 @@ static void nft_dev_forward_path(struct
+               memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
+               memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
+               route->tuple[dir].out.ifindex = info.outdev->ifindex;
++              route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex;
+               route->tuple[dir].xmit_type = info.xmit_type;
+       }
+ }
diff --git a/target/linux/generic/backport-5.10/610-v5.13-27-netfilter-flowtable-bridge-vlan-hardware-offload-and.patch b/target/linux/generic/backport-5.10/610-v5.13-27-netfilter-flowtable-bridge-vlan-hardware-offload-and.patch
new file mode 100644 (file)
index 0000000..06d75dd
--- /dev/null
@@ -0,0 +1,123 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:48 +0100
+Subject: [PATCH] netfilter: flowtable: bridge vlan hardware offload and
+ switchdev
+
+The switch might have already added the VLAN tag through PVID hardware
+offload. Keep this extra VLAN in the flowtable but skip it on egress.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -849,6 +849,7 @@ struct net_device_path {
+                               DEV_PATH_BR_VLAN_KEEP,
+                               DEV_PATH_BR_VLAN_TAG,
+                               DEV_PATH_BR_VLAN_UNTAG,
++                              DEV_PATH_BR_VLAN_UNTAG_HW,
+                       }               vlan_mode;
+                       u16             vlan_id;
+                       __be16          vlan_proto;
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -123,9 +123,10 @@ struct flow_offload_tuple {
+       /* All members above are keys for lookups, see flow_offload_hash(). */
+       struct { }                      __hash;
+-      u8                              dir:4,
++      u8                              dir:2,
+                                       xmit_type:2,
+-                                      encap_num:2;
++                                      encap_num:2,
++                                      in_vlan_ingress:2;
+       u16                             mtu;
+       union {
+               struct dst_entry        *dst_cache;
+@@ -185,7 +186,8 @@ struct nf_flow_route {
+                               u16             id;
+                               __be16          proto;
+                       } encap[NF_FLOW_TABLE_ENCAP_MAX];
+-                      u8                      num_encaps;
++                      u8                      num_encaps:2,
++                                              ingress_vlans:2;
+               } in;
+               struct {
+                       u32                     ifindex;
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -435,6 +435,7 @@ static int br_fill_forward_path(struct n
+               ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto;
+               ctx->num_vlans++;
+               break;
++      case DEV_PATH_BR_VLAN_UNTAG_HW:
+       case DEV_PATH_BR_VLAN_UNTAG:
+               ctx->num_vlans--;
+               break;
+--- a/net/bridge/br_vlan.c
++++ b/net/bridge/br_vlan.c
+@@ -1374,6 +1374,8 @@ int br_vlan_fill_forward_path_mode(struc
+       if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
+               path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
++      else if (v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
++              path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG_HW;
+       else
+               path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -95,6 +95,8 @@ static int flow_offload_fill_route(struc
+       for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
+               flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
+               flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
++              if (route->tuple[dir].in.ingress_vlans & BIT(i))
++                      flow_tuple->in_vlan_ingress |= BIT(j);
+               j++;
+       }
+       flow_tuple->encap_num = route->tuple[dir].in.num_encaps;
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -594,8 +594,12 @@ nf_flow_rule_route_common(struct net *ne
+       other_tuple = &flow->tuplehash[!dir].tuple;
+       for (i = 0; i < other_tuple->encap_num; i++) {
+-              struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
++              struct flow_action_entry *entry;
++              if (other_tuple->in_vlan_ingress & BIT(i))
++                      continue;
++
++              entry = flow_action_entry_next(flow_rule);
+               entry->id = FLOW_ACTION_VLAN_PUSH;
+               entry->vlan.vid = other_tuple->encap[i].id;
+               entry->vlan.proto = other_tuple->encap[i].proto;
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -72,6 +72,7 @@ struct nft_forward_info {
+               __be16  proto;
+       } encap[NF_FLOW_TABLE_ENCAP_MAX];
+       u8 num_encaps;
++      u8 ingress_vlans;
+       u8 h_source[ETH_ALEN];
+       u8 h_dest[ETH_ALEN];
+       enum flow_offload_xmit_type xmit_type;
+@@ -130,6 +131,9 @@ static void nft_dev_path_info(const stru
+                               memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
+                       switch (path->bridge.vlan_mode) {
++                      case DEV_PATH_BR_VLAN_UNTAG_HW:
++                              info->ingress_vlans |= BIT(info->num_encaps - 1);
++                              break;
+                       case DEV_PATH_BR_VLAN_TAG:
+                               info->encap[info->num_encaps].id = path->bridge.vlan_id;
+                               info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
+@@ -198,6 +202,7 @@ static void nft_dev_forward_path(struct
+               route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
+       }
+       route->tuple[!dir].in.num_encaps = info.num_encaps;
++      route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
+       if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
+               memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
diff --git a/target/linux/generic/backport-5.10/610-v5.13-28-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch b/target/linux/generic/backport-5.10/610-v5.13-28-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch
new file mode 100644 (file)
index 0000000..64eae68
--- /dev/null
@@ -0,0 +1,30 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:49 +0100
+Subject: [PATCH] net: flow_offload: add FLOW_ACTION_PPPOE_PUSH
+
+Add an action to represent the PPPoE hardware offload support that
+includes the session ID.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/include/net/flow_offload.h
++++ b/include/net/flow_offload.h
+@@ -147,6 +147,7 @@ enum flow_action_id {
+       FLOW_ACTION_MPLS_POP,
+       FLOW_ACTION_MPLS_MANGLE,
+       FLOW_ACTION_GATE,
++      FLOW_ACTION_PPPOE_PUSH,
+       NUM_FLOW_ACTIONS,
+ };
+@@ -271,6 +272,9 @@ struct flow_action_entry {
+                       u32             num_entries;
+                       struct action_gate_entry *entries;
+               } gate;
++              struct {                                /* FLOW_ACTION_PPPOE_PUSH */
++                      u16             sid;
++              } pppoe;
+       };
+       struct flow_action_cookie *cookie; /* user defined action cookie */
+ };
diff --git a/target/linux/generic/backport-5.10/610-v5.13-29-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch b/target/linux/generic/backport-5.10/610-v5.13-29-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch
new file mode 100644 (file)
index 0000000..ed7346a
--- /dev/null
@@ -0,0 +1,35 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:50 +0100
+Subject: [PATCH] netfilter: flowtable: support for
+ FLOW_ACTION_PPPOE_PUSH
+
+Add a PPPoE push action if layer 2 protocol is ETH_P_PPP_SES to add
+PPPoE flowtable hardware offload support.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/netfilter/nf_flow_table_offload.c
++++ b/net/netfilter/nf_flow_table_offload.c
+@@ -600,9 +600,18 @@ nf_flow_rule_route_common(struct net *ne
+                       continue;
+               entry = flow_action_entry_next(flow_rule);
+-              entry->id = FLOW_ACTION_VLAN_PUSH;
+-              entry->vlan.vid = other_tuple->encap[i].id;
+-              entry->vlan.proto = other_tuple->encap[i].proto;
++
++              switch (other_tuple->encap[i].proto) {
++              case htons(ETH_P_PPP_SES):
++                      entry->id = FLOW_ACTION_PPPOE_PUSH;
++                      entry->pppoe.sid = other_tuple->encap[i].id;
++                      break;
++              case htons(ETH_P_8021Q):
++                      entry->id = FLOW_ACTION_VLAN_PUSH;
++                      entry->vlan.vid = other_tuple->encap[i].id;
++                      entry->vlan.proto = other_tuple->encap[i].proto;
++                      break;
++              }
+       }
+       return 0;
diff --git a/target/linux/generic/backport-5.10/610-v5.13-30-dsa-slave-add-support-for-TC_SETUP_FT.patch b/target/linux/generic/backport-5.10/610-v5.13-30-dsa-slave-add-support-for-TC_SETUP_FT.patch
new file mode 100644 (file)
index 0000000..e7a33ce
--- /dev/null
@@ -0,0 +1,53 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:51 +0100
+Subject: [PATCH] dsa: slave: add support for TC_SETUP_FT
+
+The dsa infrastructure provides a well-defined hierarchy of devices,
+pass up the call to set up the flow block to the master device. From the
+software dataplane, the netfilter infrastructure uses the dsa slave
+devices to refer to the input and output device for the given skbuff.
+Similarly, the flowtable definition in the ruleset refers to the dsa
+slave port devices.
+
+This patch adds the glue code to call ndo_setup_tc with TC_SETUP_FT
+with the master device via the dsa slave devices.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1237,14 +1237,32 @@ static int dsa_slave_setup_tc_block(stru
+       }
+ }
++static int dsa_slave_setup_ft_block(struct dsa_switch *ds, int port,
++                                  void *type_data)
++{
++      struct dsa_port *cpu_dp = dsa_to_port(ds, port)->cpu_dp;
++      struct net_device *master = cpu_dp->master;
++
++      if (!master->netdev_ops->ndo_setup_tc)
++              return -EOPNOTSUPP;
++
++      return master->netdev_ops->ndo_setup_tc(master, TC_SETUP_FT, type_data);
++}
++
+ static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type,
+                             void *type_data)
+ {
+       struct dsa_port *dp = dsa_slave_to_port(dev);
+       struct dsa_switch *ds = dp->ds;
+-      if (type == TC_SETUP_BLOCK)
++      switch (type) {
++      case TC_SETUP_BLOCK:
+               return dsa_slave_setup_tc_block(dev, type_data);
++      case TC_SETUP_FT:
++              return dsa_slave_setup_ft_block(ds, dp->index, type_data);
++      default:
++              break;
++      }
+       if (!ds->ops->port_setup_tc)
+               return -EOPNOTSUPP;
diff --git a/target/linux/generic/backport-5.10/610-v5.13-31-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch b/target/linux/generic/backport-5.10/610-v5.13-31-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch
new file mode 100644 (file)
index 0000000..75a4128
--- /dev/null
@@ -0,0 +1,68 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:52 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix parsing packets in GDM
+
+When using DSA, set the special tag in GDM ingress control to allow the MAC
+to parse packets properly earlier. This affects rx DMA source port reporting.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -19,6 +19,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/pinctrl/devinfo.h>
+ #include <linux/phylink.h>
++#include <net/dsa.h>
+ #include "mtk_eth_soc.h"
+@@ -1264,13 +1265,12 @@ static int mtk_poll_rx(struct napi_struc
+                       break;
+               /* find out which mac the packet come from. values start at 1 */
+-              if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
++              if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) ||
++                  (trxd.rxd4 & RX_DMA_SPECIAL_TAG))
+                       mac = 0;
+-              } else {
+-                      mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
+-                              RX_DMA_FPORT_MASK;
+-                      mac--;
+-              }
++              else
++                      mac = ((trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
++                             RX_DMA_FPORT_MASK) - 1;
+               if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
+                            !eth->netdev[mac]))
+@@ -2233,6 +2233,9 @@ static void mtk_gdm_config(struct mtk_et
+               val |= config;
++              if (!i && eth->netdev[0] && netdev_uses_dsa(eth->netdev[0]))
++                      val |= MTK_GDMA_SPECIAL_TAG;
++
+               mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
+       }
+       /* Reset and enable PSE */
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -81,6 +81,7 @@
+ /* GDM Exgress Control Register */
+ #define MTK_GDMA_FWD_CFG(x)   (0x500 + (x * 0x1000))
++#define MTK_GDMA_SPECIAL_TAG  BIT(24)
+ #define MTK_GDMA_ICS_EN               BIT(22)
+ #define MTK_GDMA_TCS_EN               BIT(21)
+ #define MTK_GDMA_UCS_EN               BIT(20)
+@@ -304,6 +305,7 @@
+ #define RX_DMA_L4_VALID_PDMA  BIT(30)         /* when PDMA is used */
+ #define RX_DMA_FPORT_SHIFT    19
+ #define RX_DMA_FPORT_MASK     0x7
++#define RX_DMA_SPECIAL_TAG    BIT(22)
+ /* PHY Indirect Access Control registers */
+ #define MTK_PHY_IAC           0x10004
diff --git a/target/linux/generic/backport-5.10/610-v5.13-32-net-ethernet-mtk_eth_soc-add-support-for-initializin.patch b/target/linux/generic/backport-5.10/610-v5.13-32-net-ethernet-mtk_eth_soc-add-support-for-initializin.patch
new file mode 100644 (file)
index 0000000..e52e96a
--- /dev/null
@@ -0,0 +1,1308 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:53 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for
+ initializing the PPE
+
+The PPE (packet processing engine) is used to offload NAT/routed or even
+bridged flows. This patch brings up the PPE and uses it to get a packet
+hash. It also contains some functionality that will be used to bring up
+flow offloading.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.h
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -4,5 +4,5 @@
+ #
+ obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
+-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
++mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o
+ obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2258,12 +2258,17 @@ static int mtk_open(struct net_device *d
+       /* we run 2 netdevs on the same dma ring so we only bring it up once */
+       if (!refcount_read(&eth->dma_refcnt)) {
+-              int err = mtk_start_dma(eth);
++              u32 gdm_config = MTK_GDMA_TO_PDMA;
++              int err;
++              err = mtk_start_dma(eth);
+               if (err)
+                       return err;
+-              mtk_gdm_config(eth, MTK_GDMA_TO_PDMA);
++              if (eth->soc->offload_version && mtk_ppe_start(&eth->ppe) == 0)
++                      gdm_config = MTK_GDMA_TO_PPE;
++
++              mtk_gdm_config(eth, gdm_config);
+               napi_enable(&eth->tx_napi);
+               napi_enable(&eth->rx_napi);
+@@ -2330,6 +2335,9 @@ static int mtk_stop(struct net_device *d
+       mtk_dma_free(eth);
++      if (eth->soc->offload_version)
++              mtk_ppe_stop(&eth->ppe);
++
+       return 0;
+ }
+@@ -3058,6 +3066,13 @@ static int mtk_probe(struct platform_dev
+                       goto err_free_dev;
+       }
++      if (eth->soc->offload_version) {
++              err = mtk_ppe_init(&eth->ppe, eth->dev,
++                                 eth->base + MTK_ETH_PPE_BASE, 2);
++              if (err)
++                      goto err_free_dev;
++      }
++
+       for (i = 0; i < MTK_MAX_DEVS; i++) {
+               if (!eth->netdev[i])
+                       continue;
+@@ -3132,6 +3147,7 @@ static const struct mtk_soc_data mt7621_
+       .hw_features = MTK_HW_FEATURES,
+       .required_clks = MT7621_CLKS_BITMAP,
+       .required_pctl = false,
++      .offload_version = 2,
+ };
+ static const struct mtk_soc_data mt7622_data = {
+@@ -3140,6 +3156,7 @@ static const struct mtk_soc_data mt7622_
+       .hw_features = MTK_HW_FEATURES,
+       .required_clks = MT7622_CLKS_BITMAP,
+       .required_pctl = false,
++      .offload_version = 2,
+ };
+ static const struct mtk_soc_data mt7623_data = {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -15,6 +15,7 @@
+ #include <linux/u64_stats_sync.h>
+ #include <linux/refcount.h>
+ #include <linux/phylink.h>
++#include "mtk_ppe.h"
+ #define MTK_QDMA_PAGE_SIZE    2048
+ #define       MTK_MAX_RX_LENGTH       1536
+@@ -86,6 +87,7 @@
+ #define MTK_GDMA_TCS_EN               BIT(21)
+ #define MTK_GDMA_UCS_EN               BIT(20)
+ #define MTK_GDMA_TO_PDMA      0x0
++#define MTK_GDMA_TO_PPE               0x4444
+ #define MTK_GDMA_DROP_ALL       0x7777
+ /* Unicast Filter MAC Address Register - Low */
+@@ -301,6 +303,12 @@
+ #define RX_DMA_VID(_x)                ((_x) & 0xfff)
+ /* QDMA descriptor rxd4 */
++#define MTK_RXD4_FOE_ENTRY    GENMASK(13, 0)
++#define MTK_RXD4_PPE_CPU_REASON       GENMASK(18, 14)
++#define MTK_RXD4_SRC_PORT     GENMASK(21, 19)
++#define MTK_RXD4_ALG          GENMASK(31, 22)
++
++/* QDMA descriptor rxd4 */
+ #define RX_DMA_L4_VALID               BIT(24)
+ #define RX_DMA_L4_VALID_PDMA  BIT(30)         /* when PDMA is used */
+ #define RX_DMA_FPORT_SHIFT    19
+@@ -798,6 +806,7 @@ struct mtk_soc_data {
+       u32             caps;
+       u32             required_clks;
+       bool            required_pctl;
++      u8              offload_version;
+       netdev_features_t hw_features;
+ };
+@@ -897,6 +906,8 @@ struct mtk_eth {
+       u32                             tx_int_status_reg;
+       u32                             rx_dma_l4_valid;
+       int                             ip_align;
++
++      struct mtk_ppe                  ppe;
+ };
+ /* struct mtk_mac -   the structure that holds the info about the MACs of the
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -0,0 +1,511 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/jiffies.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/etherdevice.h>
++#include <linux/platform_device.h>
++#include "mtk_ppe.h"
++#include "mtk_ppe_regs.h"
++
++static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++      writel(val, ppe->base + reg);
++}
++
++static u32 ppe_r32(struct mtk_ppe *ppe, u32 reg)
++{
++      return readl(ppe->base + reg);
++}
++
++static u32 ppe_m32(struct mtk_ppe *ppe, u32 reg, u32 mask, u32 set)
++{
++      u32 val;
++
++      val = ppe_r32(ppe, reg);
++      val &= ~mask;
++      val |= set;
++      ppe_w32(ppe, reg, val);
++
++      return val;
++}
++
++static u32 ppe_set(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++      return ppe_m32(ppe, reg, 0, val);
++}
++
++static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++      return ppe_m32(ppe, reg, val, 0);
++}
++
++static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
++{
++      unsigned long timeout = jiffies + HZ;
++
++      while (time_is_before_jiffies(timeout)) {
++              if (!(ppe_r32(ppe, MTK_PPE_GLO_CFG) & MTK_PPE_GLO_CFG_BUSY))
++                      return 0;
++
++              usleep_range(10, 20);
++      }
++
++      dev_err(ppe->dev, "PPE table busy");
++
++      return -ETIMEDOUT;
++}
++
++static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
++{
++      ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
++      ppe_clear(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
++}
++
++static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable)
++{
++      mtk_ppe_cache_clear(ppe);
++
++      ppe_m32(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_EN,
++              enable * MTK_PPE_CACHE_CTL_EN);
++}
++
++static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e)
++{
++      u32 hv1, hv2, hv3;
++      u32 hash;
++
++      switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
++              case MTK_PPE_PKT_TYPE_BRIDGE:
++                      hv1 = e->bridge.src_mac_lo;
++                      hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16);
++                      hv2 = e->bridge.src_mac_hi >> 16;
++                      hv2 ^= e->bridge.dest_mac_lo;
++                      hv3 = e->bridge.dest_mac_hi;
++                      break;
++              case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++              case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++                      hv1 = e->ipv4.orig.ports;
++                      hv2 = e->ipv4.orig.dest_ip;
++                      hv3 = e->ipv4.orig.src_ip;
++                      break;
++              case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++              case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++                      hv1 = e->ipv6.src_ip[3] ^ e->ipv6.dest_ip[3];
++                      hv1 ^= e->ipv6.ports;
++
++                      hv2 = e->ipv6.src_ip[2] ^ e->ipv6.dest_ip[2];
++                      hv2 ^= e->ipv6.dest_ip[0];
++
++                      hv3 = e->ipv6.src_ip[1] ^ e->ipv6.dest_ip[1];
++                      hv3 ^= e->ipv6.src_ip[0];
++                      break;
++              case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++              case MTK_PPE_PKT_TYPE_IPV6_6RD:
++              default:
++                      WARN_ON_ONCE(1);
++                      return MTK_PPE_HASH_MASK;
++      }
++
++      hash = (hv1 & hv2) | ((~hv1) & hv3);
++      hash = (hash >> 24) | ((hash & 0xffffff) << 8);
++      hash ^= hv1 ^ hv2 ^ hv3;
++      hash ^= hash >> 16;
++      hash <<= 1;
++      hash &= MTK_PPE_ENTRIES - 1;
++
++      return hash;
++}
++
++static inline struct mtk_foe_mac_info *
++mtk_foe_entry_l2(struct mtk_foe_entry *entry)
++{
++      int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++
++      if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++              return &entry->ipv6.l2;
++
++      return &entry->ipv4.l2;
++}
++
++static inline u32 *
++mtk_foe_entry_ib2(struct mtk_foe_entry *entry)
++{
++      int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++
++      if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++              return &entry->ipv6.ib2;
++
++      return &entry->ipv4.ib2;
++}
++
++int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
++                        u8 pse_port, u8 *src_mac, u8 *dest_mac)
++{
++      struct mtk_foe_mac_info *l2;
++      u32 ports_pad, val;
++
++      memset(entry, 0, sizeof(*entry));
++
++      val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
++            FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
++            FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
++            MTK_FOE_IB1_BIND_TTL |
++            MTK_FOE_IB1_BIND_CACHE;
++      entry->ib1 = val;
++
++      val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) |
++            FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) |
++            FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port);
++
++      if (is_multicast_ether_addr(dest_mac))
++              val |= MTK_FOE_IB2_MULTICAST;
++
++      ports_pad = 0xa5a5a500 | (l4proto & 0xff);
++      if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
++              entry->ipv4.orig.ports = ports_pad;
++      if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
++              entry->ipv6.ports = ports_pad;
++
++      if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++              entry->ipv6.ib2 = val;
++              l2 = &entry->ipv6.l2;
++      } else {
++              entry->ipv4.ib2 = val;
++              l2 = &entry->ipv4.l2;
++      }
++
++      l2->dest_mac_hi = get_unaligned_be32(dest_mac);
++      l2->dest_mac_lo = get_unaligned_be16(dest_mac + 4);
++      l2->src_mac_hi = get_unaligned_be32(src_mac);
++      l2->src_mac_lo = get_unaligned_be16(src_mac + 4);
++
++      if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
++              l2->etype = ETH_P_IPV6;
++      else
++              l2->etype = ETH_P_IP;
++
++      return 0;
++}
++
++int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port)
++{
++      u32 *ib2 = mtk_foe_entry_ib2(entry);
++      u32 val;
++
++      val = *ib2;
++      val &= ~MTK_FOE_IB2_DEST_PORT;
++      val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
++      *ib2 = val;
++
++      return 0;
++}
++
++int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress,
++                               __be32 src_addr, __be16 src_port,
++                               __be32 dest_addr, __be16 dest_port)
++{
++      int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++      struct mtk_ipv4_tuple *t;
++
++      switch (type) {
++      case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++              if (egress) {
++                      t = &entry->ipv4.new;
++                      break;
++              }
++              fallthrough;
++      case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++      case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++              t = &entry->ipv4.orig;
++              break;
++      case MTK_PPE_PKT_TYPE_IPV6_6RD:
++              entry->ipv6_6rd.tunnel_src_ip = be32_to_cpu(src_addr);
++              entry->ipv6_6rd.tunnel_dest_ip = be32_to_cpu(dest_addr);
++              return 0;
++      default:
++              WARN_ON_ONCE(1);
++              return -EINVAL;
++      }
++
++      t->src_ip = be32_to_cpu(src_addr);
++      t->dest_ip = be32_to_cpu(dest_addr);
++
++      if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
++              return 0;
++
++      t->src_port = be16_to_cpu(src_port);
++      t->dest_port = be16_to_cpu(dest_port);
++
++      return 0;
++}
++
++int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
++                               __be32 *src_addr, __be16 src_port,
++                               __be32 *dest_addr, __be16 dest_port)
++{
++      int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++      u32 *src, *dest;
++      int i;
++
++      switch (type) {
++      case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++              src = entry->dslite.tunnel_src_ip;
++              dest = entry->dslite.tunnel_dest_ip;
++              break;
++      case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++      case MTK_PPE_PKT_TYPE_IPV6_6RD:
++              entry->ipv6.src_port = be16_to_cpu(src_port);
++              entry->ipv6.dest_port = be16_to_cpu(dest_port);
++              fallthrough;
++      case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++              src = entry->ipv6.src_ip;
++              dest = entry->ipv6.dest_ip;
++              break;
++      default:
++              WARN_ON_ONCE(1);
++              return -EINVAL;
++      };
++
++      for (i = 0; i < 4; i++)
++              src[i] = be32_to_cpu(src_addr[i]);
++      for (i = 0; i < 4; i++)
++              dest[i] = be32_to_cpu(dest_addr[i]);
++
++      return 0;
++}
++
++int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port)
++{
++      struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++      l2->etype = BIT(port);
++
++      if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER))
++              entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++      else
++              l2->etype |= BIT(8);
++
++      entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG;
++
++      return 0;
++}
++
++int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid)
++{
++      struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++      switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) {
++      case 0:
++              entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG |
++                            FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++              l2->vlan1 = vid;
++              return 0;
++      case 1:
++              if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) {
++                      l2->vlan1 = vid;
++                      l2->etype |= BIT(8);
++              } else {
++                      l2->vlan2 = vid;
++                      entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++              }
++              return 0;
++      default:
++              return -ENOSPC;
++      }
++}
++
++int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid)
++{
++      struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++      if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) ||
++          (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG))
++              l2->etype = ETH_P_PPP_SES;
++
++      entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE;
++      l2->pppoe_id = sid;
++
++      return 0;
++}
++
++static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
++{
++      return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
++             FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
++}
++
++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
++                       u16 timestamp)
++{
++      struct mtk_foe_entry *hwe;
++      u32 hash;
++
++      timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
++      entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
++      entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
++
++      hash = mtk_ppe_hash_entry(entry);
++      hwe = &ppe->foe_table[hash];
++      if (!mtk_foe_entry_usable(hwe)) {
++              hwe++;
++              hash++;
++
++              if (!mtk_foe_entry_usable(hwe))
++                      return -ENOSPC;
++      }
++
++      memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
++      wmb();
++      hwe->ib1 = entry->ib1;
++
++      dma_wmb();
++
++      mtk_ppe_cache_clear(ppe);
++
++      return hash;
++}
++
++int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
++               int version)
++{
++      struct mtk_foe_entry *foe;
++
++      /* need to allocate a separate device, since it PPE DMA access is
++       * not coherent.
++       */
++      ppe->base = base;
++      ppe->dev = dev;
++      ppe->version = version;
++
++      foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
++                                &ppe->foe_phys, GFP_KERNEL);
++      if (!foe)
++              return -ENOMEM;
++
++      ppe->foe_table = foe;
++
++      mtk_ppe_debugfs_init(ppe);
++
++      return 0;
++}
++
++static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
++{
++      static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 };
++      int i, k;
++
++      memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(ppe->foe_table));
++
++      if (!IS_ENABLED(CONFIG_SOC_MT7621))
++              return;
++
++      /* skip all entries that cross the 1024 byte boundary */
++      for (i = 0; i < MTK_PPE_ENTRIES; i += 128)
++              for (k = 0; k < ARRAY_SIZE(skip); k++)
++                      ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC;
++}
++
++int mtk_ppe_start(struct mtk_ppe *ppe)
++{
++      u32 val;
++
++      mtk_ppe_init_foe_table(ppe);
++      ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
++
++      val = MTK_PPE_TB_CFG_ENTRY_80B |
++            MTK_PPE_TB_CFG_AGE_NON_L4 |
++            MTK_PPE_TB_CFG_AGE_UNBIND |
++            MTK_PPE_TB_CFG_AGE_TCP |
++            MTK_PPE_TB_CFG_AGE_UDP |
++            MTK_PPE_TB_CFG_AGE_TCP_FIN |
++            FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS,
++                       MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) |
++            FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE,
++                       MTK_PPE_KEEPALIVE_DISABLE) |
++            FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) |
++            FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE,
++                       MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) |
++            FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
++                       MTK_PPE_ENTRIES_SHIFT);
++      ppe_w32(ppe, MTK_PPE_TB_CFG, val);
++
++      ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
++              MTK_PPE_IP_PROTO_CHK_IPV4 | MTK_PPE_IP_PROTO_CHK_IPV6);
++
++      mtk_ppe_cache_enable(ppe, true);
++
++      val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
++            MTK_PPE_FLOW_CFG_IP4_UDP_FRAG |
++            MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
++            MTK_PPE_FLOW_CFG_IP6_5T_ROUTE |
++            MTK_PPE_FLOW_CFG_IP6_6RD |
++            MTK_PPE_FLOW_CFG_IP4_NAT |
++            MTK_PPE_FLOW_CFG_IP4_NAPT |
++            MTK_PPE_FLOW_CFG_IP4_DSLITE |
++            MTK_PPE_FLOW_CFG_L2_BRIDGE |
++            MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
++      ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
++
++      val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) |
++            FIELD_PREP(MTK_PPE_UNBIND_AGE_DELTA, 3);
++      ppe_w32(ppe, MTK_PPE_UNBIND_AGE, val);
++
++      val = FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_UDP, 12) |
++            FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_NON_L4, 1);
++      ppe_w32(ppe, MTK_PPE_BIND_AGE0, val);
++
++      val = FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
++            FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP, 7);
++      ppe_w32(ppe, MTK_PPE_BIND_AGE1, val);
++
++      val = MTK_PPE_BIND_LIMIT0_QUARTER | MTK_PPE_BIND_LIMIT0_HALF;
++      ppe_w32(ppe, MTK_PPE_BIND_LIMIT0, val);
++
++      val = MTK_PPE_BIND_LIMIT1_FULL |
++            FIELD_PREP(MTK_PPE_BIND_LIMIT1_NON_L4, 1);
++      ppe_w32(ppe, MTK_PPE_BIND_LIMIT1, val);
++
++      val = FIELD_PREP(MTK_PPE_BIND_RATE_BIND, 30) |
++            FIELD_PREP(MTK_PPE_BIND_RATE_PREBIND, 1);
++      ppe_w32(ppe, MTK_PPE_BIND_RATE, val);
++
++      /* enable PPE */
++      val = MTK_PPE_GLO_CFG_EN |
++            MTK_PPE_GLO_CFG_IP4_L4_CS_DROP |
++            MTK_PPE_GLO_CFG_IP4_CS_DROP |
++            MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE;
++      ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
++
++      ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
++
++      return 0;
++}
++
++int mtk_ppe_stop(struct mtk_ppe *ppe)
++{
++      u32 val;
++      int i;
++
++      for (i = 0; i < MTK_PPE_ENTRIES; i++)
++              ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
++                                                 MTK_FOE_STATE_INVALID);
++
++      mtk_ppe_cache_enable(ppe, false);
++
++      /* disable offload engine */
++      ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN);
++      ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0);
++
++      /* disable aging */
++      val = MTK_PPE_TB_CFG_AGE_NON_L4 |
++            MTK_PPE_TB_CFG_AGE_UNBIND |
++            MTK_PPE_TB_CFG_AGE_TCP |
++            MTK_PPE_TB_CFG_AGE_UDP |
++            MTK_PPE_TB_CFG_AGE_TCP_FIN;
++      ppe_clear(ppe, MTK_PPE_TB_CFG, val);
++
++      return mtk_ppe_wait_busy(ppe);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -0,0 +1,287 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#ifndef __MTK_PPE_H
++#define __MTK_PPE_H
++
++#include <linux/kernel.h>
++#include <linux/bitfield.h>
++
++#define MTK_ETH_PPE_BASE              0xc00
++
++#define MTK_PPE_ENTRIES_SHIFT         3
++#define MTK_PPE_ENTRIES                       (1024 << MTK_PPE_ENTRIES_SHIFT)
++#define MTK_PPE_HASH_MASK             (MTK_PPE_ENTRIES - 1)
++
++#define MTK_FOE_IB1_UNBIND_TIMESTAMP  GENMASK(7, 0)
++#define MTK_FOE_IB1_UNBIND_PACKETS    GENMASK(23, 8)
++#define MTK_FOE_IB1_UNBIND_PREBIND    BIT(24)
++
++#define MTK_FOE_IB1_BIND_TIMESTAMP    GENMASK(14, 0)
++#define MTK_FOE_IB1_BIND_KEEPALIVE    BIT(15)
++#define MTK_FOE_IB1_BIND_VLAN_LAYER   GENMASK(18, 16)
++#define MTK_FOE_IB1_BIND_PPPOE                BIT(19)
++#define MTK_FOE_IB1_BIND_VLAN_TAG     BIT(20)
++#define MTK_FOE_IB1_BIND_PKT_SAMPLE   BIT(21)
++#define MTK_FOE_IB1_BIND_CACHE                BIT(22)
++#define MTK_FOE_IB1_BIND_TUNNEL_DECAP BIT(23)
++#define MTK_FOE_IB1_BIND_TTL          BIT(24)
++
++#define MTK_FOE_IB1_PACKET_TYPE               GENMASK(27, 25)
++#define MTK_FOE_IB1_STATE             GENMASK(29, 28)
++#define MTK_FOE_IB1_UDP                       BIT(30)
++#define MTK_FOE_IB1_STATIC            BIT(31)
++
++enum {
++      MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0,
++      MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1,
++      MTK_PPE_PKT_TYPE_BRIDGE = 2,
++      MTK_PPE_PKT_TYPE_IPV4_DSLITE = 3,
++      MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T = 4,
++      MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T = 5,
++      MTK_PPE_PKT_TYPE_IPV6_6RD = 7,
++};
++
++#define MTK_FOE_IB2_QID                       GENMASK(3, 0)
++#define MTK_FOE_IB2_PSE_QOS           BIT(4)
++#define MTK_FOE_IB2_DEST_PORT         GENMASK(7, 5)
++#define MTK_FOE_IB2_MULTICAST         BIT(8)
++
++#define MTK_FOE_IB2_WHNAT_QID2                GENMASK(13, 12)
++#define MTK_FOE_IB2_WHNAT_DEVIDX      BIT(16)
++#define MTK_FOE_IB2_WHNAT_NAT         BIT(17)
++
++#define MTK_FOE_IB2_PORT_MG           GENMASK(17, 12)
++
++#define MTK_FOE_IB2_PORT_AG           GENMASK(23, 18)
++
++#define MTK_FOE_IB2_DSCP              GENMASK(31, 24)
++
++#define MTK_FOE_VLAN2_WHNAT_BSS               GEMMASK(5, 0)
++#define MTK_FOE_VLAN2_WHNAT_WCID      GENMASK(13, 6)
++#define MTK_FOE_VLAN2_WHNAT_RING      GENMASK(15, 14)
++
++enum {
++      MTK_FOE_STATE_INVALID,
++      MTK_FOE_STATE_UNBIND,
++      MTK_FOE_STATE_BIND,
++      MTK_FOE_STATE_FIN
++};
++
++struct mtk_foe_mac_info {
++      u16 vlan1;
++      u16 etype;
++
++      u32 dest_mac_hi;
++
++      u16 vlan2;
++      u16 dest_mac_lo;
++
++      u32 src_mac_hi;
++
++      u16 pppoe_id;
++      u16 src_mac_lo;
++};
++
++struct mtk_foe_bridge {
++      u32 dest_mac_hi;
++
++      u16 src_mac_lo;
++      u16 dest_mac_lo;
++
++      u32 src_mac_hi;
++
++      u32 ib2;
++
++      u32 _rsv[5];
++
++      u32 udf_tsid;
++      struct mtk_foe_mac_info l2;
++};
++
++struct mtk_ipv4_tuple {
++      u32 src_ip;
++      u32 dest_ip;
++      union {
++              struct {
++                      u16 dest_port;
++                      u16 src_port;
++              };
++              struct {
++                      u8 protocol;
++                      u8 _pad[3]; /* fill with 0xa5a5a5 */
++              };
++              u32 ports;
++      };
++};
++
++struct mtk_foe_ipv4 {
++      struct mtk_ipv4_tuple orig;
++
++      u32 ib2;
++
++      struct mtk_ipv4_tuple new;
++
++      u16 timestamp;
++      u16 _rsv0[3];
++
++      u32 udf_tsid;
++
++      struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv4_dslite {
++      struct mtk_ipv4_tuple ip4;
++
++      u32 tunnel_src_ip[4];
++      u32 tunnel_dest_ip[4];
++
++      u8 flow_label[3];
++      u8 priority;
++
++      u32 udf_tsid;
++
++      u32 ib2;
++
++      struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv6 {
++      u32 src_ip[4];
++      u32 dest_ip[4];
++
++      union {
++              struct {
++                      u8 protocol;
++                      u8 _pad[3]; /* fill with 0xa5a5a5 */
++              }; /* 3-tuple */
++              struct {
++                      u16 dest_port;
++                      u16 src_port;
++              }; /* 5-tuple */
++              u32 ports;
++      };
++
++      u32 _rsv[3];
++
++      u32 udf;
++
++      u32 ib2;
++      struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv6_6rd {
++      u32 src_ip[4];
++      u32 dest_ip[4];
++      u16 dest_port;
++      u16 src_port;
++
++      u32 tunnel_src_ip;
++      u32 tunnel_dest_ip;
++
++      u16 hdr_csum;
++      u8 dscp;
++      u8 ttl;
++
++      u8 flag;
++      u8 pad;
++      u8 per_flow_6rd_id;
++      u8 pad2;
++
++      u32 ib2;
++      struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_entry {
++      u32 ib1;
++
++      union {
++              struct mtk_foe_bridge bridge;
++              struct mtk_foe_ipv4 ipv4;
++              struct mtk_foe_ipv4_dslite dslite;
++              struct mtk_foe_ipv6 ipv6;
++              struct mtk_foe_ipv6_6rd ipv6_6rd;
++              u32 data[19];
++      };
++};
++
++enum {
++      MTK_PPE_CPU_REASON_TTL_EXCEEDED                 = 0x02,
++      MTK_PPE_CPU_REASON_OPTION_HEADER                = 0x03,
++      MTK_PPE_CPU_REASON_NO_FLOW                      = 0x07,
++      MTK_PPE_CPU_REASON_IPV4_FRAG                    = 0x08,
++      MTK_PPE_CPU_REASON_IPV4_DSLITE_FRAG             = 0x09,
++      MTK_PPE_CPU_REASON_IPV4_DSLITE_NO_TCP_UDP       = 0x0a,
++      MTK_PPE_CPU_REASON_IPV6_6RD_NO_TCP_UDP          = 0x0b,
++      MTK_PPE_CPU_REASON_TCP_FIN_SYN_RST              = 0x0c,
++      MTK_PPE_CPU_REASON_UN_HIT                       = 0x0d,
++      MTK_PPE_CPU_REASON_HIT_UNBIND                   = 0x0e,
++      MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED      = 0x0f,
++      MTK_PPE_CPU_REASON_HIT_BIND_TCP_FIN             = 0x10,
++      MTK_PPE_CPU_REASON_HIT_TTL_1                    = 0x11,
++      MTK_PPE_CPU_REASON_HIT_BIND_VLAN_VIOLATION      = 0x12,
++      MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR         = 0x13,
++      MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR         = 0x14,
++      MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR        = 0x15,
++      MTK_PPE_CPU_REASON_HIT_BIND_FORCE_CPU           = 0x16,
++      MTK_PPE_CPU_REASON_TUNNEL_OPTION_HEADER         = 0x17,
++      MTK_PPE_CPU_REASON_MULTICAST_TO_CPU             = 0x18,
++      MTK_PPE_CPU_REASON_MULTICAST_TO_GMAC1_CPU       = 0x19,
++      MTK_PPE_CPU_REASON_HIT_PRE_BIND                 = 0x1a,
++      MTK_PPE_CPU_REASON_PACKET_SAMPLING              = 0x1b,
++      MTK_PPE_CPU_REASON_EXCEED_MTU                   = 0x1c,
++      MTK_PPE_CPU_REASON_PPE_BYPASS                   = 0x1e,
++      MTK_PPE_CPU_REASON_INVALID                      = 0x1f,
++};
++
++struct mtk_ppe {
++      struct device *dev;
++      void __iomem *base;
++      int version;
++
++      struct mtk_foe_entry *foe_table;
++      dma_addr_t foe_phys;
++
++      void *acct_table;
++};
++
++int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
++               int version);
++int mtk_ppe_start(struct mtk_ppe *ppe);
++int mtk_ppe_stop(struct mtk_ppe *ppe);
++
++static inline void
++mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash)
++{
++      ppe->foe_table[hash].ib1 = 0;
++      dma_wmb();
++}
++
++static inline int
++mtk_foe_entry_timestamp(struct mtk_ppe *ppe, u16 hash)
++{
++      u32 ib1 = READ_ONCE(ppe->foe_table[hash].ib1);
++
++      if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND)
++              return -1;
++
++      return FIELD_GET(MTK_FOE_IB1_BIND_TIMESTAMP, ib1);
++}
++
++int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
++                        u8 pse_port, u8 *src_mac, u8 *dest_mac);
++int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port);
++int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig,
++                               __be32 src_addr, __be16 src_port,
++                               __be32 dest_addr, __be16 dest_port);
++int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
++                               __be32 *src_addr, __be16 src_port,
++                               __be32 *dest_addr, __be16 dest_port);
++int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
++int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
++int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
++                       u16 timestamp);
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
++
++#endif
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -0,0 +1,217 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/debugfs.h>
++#include "mtk_eth_soc.h"
++
++struct mtk_flow_addr_info
++{
++      void *src, *dest;
++      u16 *src_port, *dest_port;
++      bool ipv6;
++};
++
++static const char *mtk_foe_entry_state_str(int state)
++{
++      static const char * const state_str[] = {
++              [MTK_FOE_STATE_INVALID] = "INV",
++              [MTK_FOE_STATE_UNBIND] = "UNB",
++              [MTK_FOE_STATE_BIND] = "BND",
++              [MTK_FOE_STATE_FIN] = "FIN",
++      };
++
++      if (state >= ARRAY_SIZE(state_str) || !state_str[state])
++              return "UNK";
++
++      return state_str[state];
++}
++
++static const char *mtk_foe_pkt_type_str(int type)
++{
++      static const char * const type_str[] = {
++              [MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
++              [MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
++              [MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
++              [MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
++              [MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
++              [MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
++              [MTK_PPE_PKT_TYPE_IPV6_6RD] = "6RD",
++      };
++
++      if (type >= ARRAY_SIZE(type_str) || !type_str[type])
++              return "UNKNOWN";
++
++      return type_str[type];
++}
++
++static void
++mtk_print_addr(struct seq_file *m, u32 *addr, bool ipv6)
++{
++      u32 n_addr[4];
++      int i;
++
++      if (!ipv6) {
++              seq_printf(m, "%pI4h", addr);
++              return;
++      }
++
++      for (i = 0; i < ARRAY_SIZE(n_addr); i++)
++              n_addr[i] = htonl(addr[i]);
++      seq_printf(m, "%pI6", n_addr);
++}
++
++static void
++mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
++{
++      mtk_print_addr(m, ai->src, ai->ipv6);
++      if (ai->src_port)
++              seq_printf(m, ":%d", *ai->src_port);
++      seq_printf(m, "->");
++      mtk_print_addr(m, ai->dest, ai->ipv6);
++      if (ai->dest_port)
++              seq_printf(m, ":%d", *ai->dest_port);
++}
++
++static int
++mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
++{
++      struct mtk_ppe *ppe = m->private;
++      int i, count;
++
++      for (i = 0, count = 0; i < MTK_PPE_ENTRIES; i++) {
++              struct mtk_foe_entry *entry = &ppe->foe_table[i];
++              struct mtk_foe_mac_info *l2;
++              struct mtk_flow_addr_info ai = {};
++              unsigned char h_source[ETH_ALEN];
++              unsigned char h_dest[ETH_ALEN];
++              int type, state;
++              u32 ib2;
++
++
++              state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
++              if (!state)
++                      continue;
++
++              if (bind && state != MTK_FOE_STATE_BIND)
++                      continue;
++
++              type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++              seq_printf(m, "%05x %s %7s", i,
++                         mtk_foe_entry_state_str(state),
++                         mtk_foe_pkt_type_str(type));
++
++              switch (type) {
++              case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++              case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++                      ai.src_port = &entry->ipv4.orig.src_port;
++                      ai.dest_port = &entry->ipv4.orig.dest_port;
++                      fallthrough;
++              case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++                      ai.src = &entry->ipv4.orig.src_ip;
++                      ai.dest = &entry->ipv4.orig.dest_ip;
++                      break;
++              case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++                      ai.src_port = &entry->ipv6.src_port;
++                      ai.dest_port = &entry->ipv6.dest_port;
++                      fallthrough;
++              case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++              case MTK_PPE_PKT_TYPE_IPV6_6RD:
++                      ai.src = &entry->ipv6.src_ip;
++                      ai.dest = &entry->ipv6.dest_ip;
++                      ai.ipv6 = true;
++                      break;
++              }
++
++              seq_printf(m, " orig=");
++              mtk_print_addr_info(m, &ai);
++
++              switch (type) {
++              case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++              case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++                      ai.src_port = &entry->ipv4.new.src_port;
++                      ai.dest_port = &entry->ipv4.new.dest_port;
++                      fallthrough;
++              case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++                      ai.src = &entry->ipv4.new.src_ip;
++                      ai.dest = &entry->ipv4.new.dest_ip;
++                      seq_printf(m, " new=");
++                      mtk_print_addr_info(m, &ai);
++                      break;
++              }
++
++              if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++                      l2 = &entry->ipv6.l2;
++                      ib2 = entry->ipv6.ib2;
++              } else {
++                      l2 = &entry->ipv4.l2;
++                      ib2 = entry->ipv4.ib2;
++              }
++
++              *((__be32 *)h_source) = htonl(l2->src_mac_hi);
++              *((__be16 *)&h_source[4]) = htons(l2->src_mac_lo);
++              *((__be32 *)h_dest) = htonl(l2->dest_mac_hi);
++              *((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
++
++              seq_printf(m, " eth=%pM->%pM etype=%04x"
++                            " vlan=%d,%d ib1=%08x ib2=%08x\n",
++                         h_source, h_dest, ntohs(l2->etype),
++                         l2->vlan1, l2->vlan2, entry->ib1, ib2);
++      }
++
++      return 0;
++}
++
++static int
++mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private)
++{
++      return mtk_ppe_debugfs_foe_show(m, private, false);
++}
++
++static int
++mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private)
++{
++      return mtk_ppe_debugfs_foe_show(m, private, true);
++}
++
++static int
++mtk_ppe_debugfs_foe_open_all(struct inode *inode, struct file *file)
++{
++      return single_open(file, mtk_ppe_debugfs_foe_show_all,
++                         inode->i_private);
++}
++
++static int
++mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file)
++{
++      return single_open(file, mtk_ppe_debugfs_foe_show_bind,
++                         inode->i_private);
++}
++
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
++{
++      static const struct file_operations fops_all = {
++              .open = mtk_ppe_debugfs_foe_open_all,
++              .read = seq_read,
++              .llseek = seq_lseek,
++              .release = single_release,
++      };
++
++      static const struct file_operations fops_bind = {
++              .open = mtk_ppe_debugfs_foe_open_bind,
++              .read = seq_read,
++              .llseek = seq_lseek,
++              .release = single_release,
++      };
++
++      struct dentry *root;
++
++      root = debugfs_create_dir("mtk_ppe", NULL);
++      if (!root)
++              return -ENOMEM;
++
++      debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all);
++      debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind);
++
++      return 0;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+@@ -0,0 +1,144 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#ifndef __MTK_PPE_REGS_H
++#define __MTK_PPE_REGS_H
++
++#define MTK_PPE_GLO_CFG                               0x200
++#define MTK_PPE_GLO_CFG_EN                    BIT(0)
++#define MTK_PPE_GLO_CFG_TSID_EN                       BIT(1)
++#define MTK_PPE_GLO_CFG_IP4_L4_CS_DROP                BIT(2)
++#define MTK_PPE_GLO_CFG_IP4_CS_DROP           BIT(3)
++#define MTK_PPE_GLO_CFG_TTL0_DROP             BIT(4)
++#define MTK_PPE_GLO_CFG_PPE_BSWAP             BIT(5)
++#define MTK_PPE_GLO_CFG_PSE_HASH_OFS          BIT(6)
++#define MTK_PPE_GLO_CFG_MCAST_TB_EN           BIT(7)
++#define MTK_PPE_GLO_CFG_FLOW_DROP_KA          BIT(8)
++#define MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE      BIT(9)
++#define MTK_PPE_GLO_CFG_UDP_LITE_EN           BIT(10)
++#define MTK_PPE_GLO_CFG_UDP_LEN_DROP          BIT(11)
++#define MTK_PPE_GLO_CFG_MCAST_ENTRIES         GNEMASK(13, 12)
++#define MTK_PPE_GLO_CFG_BUSY                  BIT(31)
++
++#define MTK_PPE_FLOW_CFG                      0x204
++#define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG         BIT(6)
++#define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG         BIT(7)
++#define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE         BIT(8)
++#define MTK_PPE_FLOW_CFG_IP6_5T_ROUTE         BIT(9)
++#define MTK_PPE_FLOW_CFG_IP6_6RD              BIT(10)
++#define MTK_PPE_FLOW_CFG_IP4_NAT              BIT(12)
++#define MTK_PPE_FLOW_CFG_IP4_NAPT             BIT(13)
++#define MTK_PPE_FLOW_CFG_IP4_DSLITE           BIT(14)
++#define MTK_PPE_FLOW_CFG_L2_BRIDGE            BIT(15)
++#define MTK_PPE_FLOW_CFG_IP_PROTO_BLACKLIST   BIT(16)
++#define MTK_PPE_FLOW_CFG_IP4_NAT_FRAG         BIT(17)
++#define MTK_PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL  BIT(18)
++#define MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY     BIT(19)
++#define MTK_PPE_FLOW_CFG_IP6_HASH_GRE_KEY     BIT(20)
++
++#define MTK_PPE_IP_PROTO_CHK                  0x208
++#define MTK_PPE_IP_PROTO_CHK_IPV4             GENMASK(15, 0)
++#define MTK_PPE_IP_PROTO_CHK_IPV6             GENMASK(31, 16)
++
++#define MTK_PPE_TB_CFG                                0x21c
++#define MTK_PPE_TB_CFG_ENTRY_NUM              GENMASK(2, 0)
++#define MTK_PPE_TB_CFG_ENTRY_80B              BIT(3)
++#define MTK_PPE_TB_CFG_SEARCH_MISS            GENMASK(5, 4)
++#define MTK_PPE_TB_CFG_AGE_PREBIND            BIT(6)
++#define MTK_PPE_TB_CFG_AGE_NON_L4             BIT(7)
++#define MTK_PPE_TB_CFG_AGE_UNBIND             BIT(8)
++#define MTK_PPE_TB_CFG_AGE_TCP                        BIT(9)
++#define MTK_PPE_TB_CFG_AGE_UDP                        BIT(10)
++#define MTK_PPE_TB_CFG_AGE_TCP_FIN            BIT(11)
++#define MTK_PPE_TB_CFG_KEEPALIVE              GENMASK(13, 12)
++#define MTK_PPE_TB_CFG_HASH_MODE              GENMASK(15, 14)
++#define MTK_PPE_TB_CFG_SCAN_MODE              GENMASK(17, 16)
++#define MTK_PPE_TB_CFG_HASH_DEBUG             GENMASK(19, 18)
++
++enum {
++      MTK_PPE_SCAN_MODE_DISABLED,
++      MTK_PPE_SCAN_MODE_CHECK_AGE,
++      MTK_PPE_SCAN_MODE_KEEPALIVE_AGE,
++};
++
++enum {
++      MTK_PPE_KEEPALIVE_DISABLE,
++      MTK_PPE_KEEPALIVE_UNICAST_CPU,
++      MTK_PPE_KEEPALIVE_DUP_CPU = 3,
++};
++
++enum {
++      MTK_PPE_SEARCH_MISS_ACTION_DROP,
++      MTK_PPE_SEARCH_MISS_ACTION_FORWARD = 2,
++      MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD = 3,
++};
++
++#define MTK_PPE_TB_BASE                               0x220
++
++#define MTK_PPE_TB_USED                               0x224
++#define MTK_PPE_TB_USED_NUM                   GENMASK(13, 0)
++
++#define MTK_PPE_BIND_RATE                     0x228
++#define MTK_PPE_BIND_RATE_BIND                        GENMASK(15, 0)
++#define MTK_PPE_BIND_RATE_PREBIND             GENMASK(31, 16)
++
++#define MTK_PPE_BIND_LIMIT0                   0x22c
++#define MTK_PPE_BIND_LIMIT0_QUARTER           GENMASK(13, 0)
++#define MTK_PPE_BIND_LIMIT0_HALF              GENMASK(29, 16)
++
++#define MTK_PPE_BIND_LIMIT1                   0x230
++#define MTK_PPE_BIND_LIMIT1_FULL              GENMASK(13, 0)
++#define MTK_PPE_BIND_LIMIT1_NON_L4            GENMASK(23, 16)
++
++#define MTK_PPE_KEEPALIVE                     0x234
++#define MTK_PPE_KEEPALIVE_TIME                        GENMASK(15, 0)
++#define MTK_PPE_KEEPALIVE_TIME_TCP            GENMASK(23, 16)
++#define MTK_PPE_KEEPALIVE_TIME_UDP            GENMASK(31, 24)
++
++#define MTK_PPE_UNBIND_AGE                    0x238
++#define MTK_PPE_UNBIND_AGE_MIN_PACKETS                GENMASK(31, 16)
++#define MTK_PPE_UNBIND_AGE_DELTA              GENMASK(7, 0)
++
++#define MTK_PPE_BIND_AGE0                     0x23c
++#define MTK_PPE_BIND_AGE0_DELTA_NON_L4                GENMASK(30, 16)
++#define MTK_PPE_BIND_AGE0_DELTA_UDP           GENMASK(14, 0)
++
++#define MTK_PPE_BIND_AGE1                     0x240
++#define MTK_PPE_BIND_AGE1_DELTA_TCP_FIN               GENMASK(30, 16)
++#define MTK_PPE_BIND_AGE1_DELTA_TCP           GENMASK(14, 0)
++
++#define MTK_PPE_HASH_SEED                     0x244
++
++#define MTK_PPE_DEFAULT_CPU_PORT              0x248
++#define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n)     (GENMASK(2, 0) << ((_n) * 4))
++
++#define MTK_PPE_MTU_DROP                      0x308
++
++#define MTK_PPE_VLAN_MTU0                     0x30c
++#define MTK_PPE_VLAN_MTU0_NONE                        GENMASK(13, 0)
++#define MTK_PPE_VLAN_MTU0_1TAG                        GENMASK(29, 16)
++
++#define MTK_PPE_VLAN_MTU1                     0x310
++#define MTK_PPE_VLAN_MTU1_2TAG                        GENMASK(13, 0)
++#define MTK_PPE_VLAN_MTU1_3TAG                        GENMASK(29, 16)
++
++#define MTK_PPE_VPM_TPID                      0x318
++
++#define MTK_PPE_CACHE_CTL                     0x320
++#define MTK_PPE_CACHE_CTL_EN                  BIT(0)
++#define MTK_PPE_CACHE_CTL_LOCK_CLR            BIT(4)
++#define MTK_PPE_CACHE_CTL_REQ                 BIT(8)
++#define MTK_PPE_CACHE_CTL_CLEAR                       BIT(9)
++#define MTK_PPE_CACHE_CTL_CMD                 GENMASK(13, 12)
++
++#define MTK_PPE_MIB_CFG                               0x334
++#define MTK_PPE_MIB_CFG_EN                    BIT(0)
++#define MTK_PPE_MIB_CFG_RD_CLR                        BIT(1)
++
++#define MTK_PPE_MIB_TB_BASE                   0x338
++
++#define MTK_PPE_MIB_CACHE_CTL                 0x350
++#define MTK_PPE_MIB_CACHE_CTL_EN              BIT(0)
++#define MTK_PPE_MIB_CACHE_CTL_FLUSH           BIT(2)
++
++#endif
diff --git a/target/linux/generic/backport-5.10/610-v5.13-33-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch b/target/linux/generic/backport-5.10/610-v5.13-33-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch
new file mode 100644 (file)
index 0000000..82e3dde
--- /dev/null
@@ -0,0 +1,568 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 24 Mar 2021 02:30:54 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add flow offloading support
+
+This adds support for offloading IPv4 routed flows, including SNAT/DNAT,
+one VLAN, PPPoE and DSA.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -4,5 +4,5 @@
+ #
+ obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
+-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o
++mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
+ obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2813,6 +2813,7 @@ static const struct net_device_ops mtk_n
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = mtk_poll_controller,
+ #endif
++      .ndo_setup_tc           = mtk_eth_setup_tc,
+ };
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+@@ -3071,6 +3072,10 @@ static int mtk_probe(struct platform_dev
+                                  eth->base + MTK_ETH_PPE_BASE, 2);
+               if (err)
+                       goto err_free_dev;
++
++              err = mtk_eth_offload_init(eth);
++              if (err)
++                      goto err_free_dev;
+       }
+       for (i = 0; i < MTK_MAX_DEVS; i++) {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -15,6 +15,7 @@
+ #include <linux/u64_stats_sync.h>
+ #include <linux/refcount.h>
+ #include <linux/phylink.h>
++#include <linux/rhashtable.h>
+ #include "mtk_ppe.h"
+ #define MTK_QDMA_PAGE_SIZE    2048
+@@ -40,7 +41,8 @@
+                                NETIF_F_HW_VLAN_CTAG_RX | \
+                                NETIF_F_SG | NETIF_F_TSO | \
+                                NETIF_F_TSO6 | \
+-                               NETIF_F_IPV6_CSUM)
++                               NETIF_F_IPV6_CSUM |\
++                               NETIF_F_HW_TC)
+ #define MTK_HW_FEATURES_MT7628        (NETIF_F_SG | NETIF_F_RXCSUM)
+ #define NEXT_DESP_IDX(X, Y)   (((X) + 1) & ((Y) - 1))
+@@ -908,6 +910,7 @@ struct mtk_eth {
+       int                             ip_align;
+       struct mtk_ppe                  ppe;
++      struct rhashtable               flow_table;
+ };
+ /* struct mtk_mac -   the structure that holds the info about the MACs of the
+@@ -952,4 +955,9 @@ int mtk_gmac_sgmii_path_setup(struct mtk
+ int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
++int mtk_eth_offload_init(struct mtk_eth *eth);
++int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
++                   void *type_data);
++
++
+ #endif /* MTK_ETH_H */
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -0,0 +1,485 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ *  Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
++ */
++
++#include <linux/if_ether.h>
++#include <linux/rhashtable.h>
++#include <linux/if_ether.h>
++#include <linux/ip.h>
++#include <net/flow_offload.h>
++#include <net/pkt_cls.h>
++#include <net/dsa.h>
++#include "mtk_eth_soc.h"
++
++struct mtk_flow_data {
++      struct ethhdr eth;
++
++      union {
++              struct {
++                      __be32 src_addr;
++                      __be32 dst_addr;
++              } v4;
++      };
++
++      __be16 src_port;
++      __be16 dst_port;
++
++      struct {
++              u16 id;
++              __be16 proto;
++              u8 num;
++      } vlan;
++      struct {
++              u16 sid;
++              u8 num;
++      } pppoe;
++};
++
++struct mtk_flow_entry {
++      struct rhash_head node;
++      unsigned long cookie;
++      u16 hash;
++};
++
++static const struct rhashtable_params mtk_flow_ht_params = {
++      .head_offset = offsetof(struct mtk_flow_entry, node),
++      .head_offset = offsetof(struct mtk_flow_entry, cookie),
++      .key_len = sizeof(unsigned long),
++      .automatic_shrinking = true,
++};
++
++static u32
++mtk_eth_timestamp(struct mtk_eth *eth)
++{
++      return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
++}
++
++static int
++mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
++                     bool egress)
++{
++      return mtk_foe_entry_set_ipv4_tuple(foe, egress,
++                                          data->v4.src_addr, data->src_port,
++                                          data->v4.dst_addr, data->dst_port);
++}
++
++static void
++mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
++{
++      void *dest = eth + act->mangle.offset;
++      const void *src = &act->mangle.val;
++
++      if (act->mangle.offset > 8)
++              return;
++
++      if (act->mangle.mask == 0xffff) {
++              src += 2;
++              dest += 2;
++      }
++
++      memcpy(dest, src, act->mangle.mask ? 2 : 4);
++}
++
++
++static int
++mtk_flow_mangle_ports(const struct flow_action_entry *act,
++                    struct mtk_flow_data *data)
++{
++      u32 val = ntohl(act->mangle.val);
++
++      switch (act->mangle.offset) {
++      case 0:
++              if (act->mangle.mask == ~htonl(0xffff))
++                      data->dst_port = cpu_to_be16(val);
++              else
++                      data->src_port = cpu_to_be16(val >> 16);
++              break;
++      case 2:
++              data->dst_port = cpu_to_be16(val);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int
++mtk_flow_mangle_ipv4(const struct flow_action_entry *act,
++                   struct mtk_flow_data *data)
++{
++      __be32 *dest;
++
++      switch (act->mangle.offset) {
++      case offsetof(struct iphdr, saddr):
++              dest = &data->v4.src_addr;
++              break;
++      case offsetof(struct iphdr, daddr):
++              dest = &data->v4.dst_addr;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      memcpy(dest, &act->mangle.val, sizeof(u32));
++
++      return 0;
++}
++
++static int
++mtk_flow_get_dsa_port(struct net_device **dev)
++{
++#if IS_ENABLED(CONFIG_NET_DSA)
++      struct dsa_port *dp;
++
++      dp = dsa_port_from_netdev(*dev);
++      if (IS_ERR(dp))
++              return -ENODEV;
++
++      if (dp->cpu_dp->tag_ops->proto != DSA_TAG_PROTO_MTK)
++              return -ENODEV;
++
++      *dev = dp->cpu_dp->master;
++
++      return dp->index;
++#else
++      return -ENODEV;
++#endif
++}
++
++static int
++mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
++                         struct net_device *dev)
++{
++      int pse_port, dsa_port;
++
++      dsa_port = mtk_flow_get_dsa_port(&dev);
++      if (dsa_port >= 0)
++              mtk_foe_entry_set_dsa(foe, dsa_port);
++
++      if (dev == eth->netdev[0])
++              pse_port = 1;
++      else if (dev == eth->netdev[1])
++              pse_port = 2;
++      else
++              return -EOPNOTSUPP;
++
++      mtk_foe_entry_set_pse_port(foe, pse_port);
++
++      return 0;
++}
++
++static int
++mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++      struct flow_rule *rule = flow_cls_offload_flow_rule(f);
++      struct flow_action_entry *act;
++      struct mtk_flow_data data = {};
++      struct mtk_foe_entry foe;
++      struct net_device *odev = NULL;
++      struct mtk_flow_entry *entry;
++      int offload_type = 0;
++      u16 addr_type = 0;
++      u32 timestamp;
++      u8 l4proto = 0;
++      int err = 0;
++      int hash;
++      int i;
++
++      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
++              struct flow_match_meta match;
++
++              flow_rule_match_meta(rule, &match);
++      } else {
++              return -EOPNOTSUPP;
++      }
++
++      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
++              struct flow_match_control match;
++
++              flow_rule_match_control(rule, &match);
++              addr_type = match.key->addr_type;
++      } else {
++              return -EOPNOTSUPP;
++      }
++
++      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
++              struct flow_match_basic match;
++
++              flow_rule_match_basic(rule, &match);
++              l4proto = match.key->ip_proto;
++      } else {
++              return -EOPNOTSUPP;
++      }
++
++      flow_action_for_each(i, act, &rule->action) {
++              switch (act->id) {
++              case FLOW_ACTION_MANGLE:
++                      if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
++                              mtk_flow_offload_mangle_eth(act, &data.eth);
++                      break;
++              case FLOW_ACTION_REDIRECT:
++                      odev = act->dev;
++                      break;
++              case FLOW_ACTION_CSUM:
++                      break;
++              case FLOW_ACTION_VLAN_PUSH:
++                      if (data.vlan.num == 1 ||
++                          act->vlan.proto != htons(ETH_P_8021Q))
++                              return -EOPNOTSUPP;
++
++                      data.vlan.id = act->vlan.vid;
++                      data.vlan.proto = act->vlan.proto;
++                      data.vlan.num++;
++                      break;
++              case FLOW_ACTION_PPPOE_PUSH:
++                      if (data.pppoe.num == 1)
++                              return -EOPNOTSUPP;
++
++                      data.pppoe.sid = act->pppoe.sid;
++                      data.pppoe.num++;
++                      break;
++              default:
++                      return -EOPNOTSUPP;
++              }
++      }
++
++      switch (addr_type) {
++      case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
++              offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
++              break;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      if (!is_valid_ether_addr(data.eth.h_source) ||
++          !is_valid_ether_addr(data.eth.h_dest))
++              return -EINVAL;
++
++      err = mtk_foe_entry_prepare(&foe, offload_type, l4proto, 0,
++                                  data.eth.h_source,
++                                  data.eth.h_dest);
++      if (err)
++              return err;
++
++      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
++              struct flow_match_ports ports;
++
++              flow_rule_match_ports(rule, &ports);
++              data.src_port = ports.key->src;
++              data.dst_port = ports.key->dst;
++      } else {
++              return -EOPNOTSUPP;
++      }
++
++      if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
++              struct flow_match_ipv4_addrs addrs;
++
++              flow_rule_match_ipv4_addrs(rule, &addrs);
++
++              data.v4.src_addr = addrs.key->src;
++              data.v4.dst_addr = addrs.key->dst;
++
++              mtk_flow_set_ipv4_addr(&foe, &data, false);
++      }
++
++      flow_action_for_each(i, act, &rule->action) {
++              if (act->id != FLOW_ACTION_MANGLE)
++                      continue;
++
++              switch (act->mangle.htype) {
++              case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
++              case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
++                      err = mtk_flow_mangle_ports(act, &data);
++                      break;
++              case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
++                      err = mtk_flow_mangle_ipv4(act, &data);
++                      break;
++              case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
++                      /* handled earlier */
++                      break;
++              default:
++                      return -EOPNOTSUPP;
++              }
++
++              if (err)
++                      return err;
++      }
++
++      if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
++              err = mtk_flow_set_ipv4_addr(&foe, &data, true);
++              if (err)
++                      return err;
++      }
++
++      if (data.vlan.num == 1) {
++              if (data.vlan.proto != htons(ETH_P_8021Q))
++                      return -EOPNOTSUPP;
++
++              mtk_foe_entry_set_vlan(&foe, data.vlan.id);
++      }
++      if (data.pppoe.num == 1)
++              mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
++
++      err = mtk_flow_set_output_device(eth, &foe, odev);
++      if (err)
++              return err;
++
++      entry = kzalloc(sizeof(*entry), GFP_KERNEL);
++      if (!entry)
++              return -ENOMEM;
++
++      entry->cookie = f->cookie;
++      timestamp = mtk_eth_timestamp(eth);
++      hash = mtk_foe_entry_commit(&eth->ppe, &foe, timestamp);
++      if (hash < 0) {
++              err = hash;
++              goto free;
++      }
++
++      entry->hash = hash;
++      err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
++                                   mtk_flow_ht_params);
++      if (err < 0)
++              goto clear_flow;
++
++      return 0;
++clear_flow:
++      mtk_foe_entry_clear(&eth->ppe, hash);
++free:
++      kfree(entry);
++      return err;
++}
++
++static int
++mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++      struct mtk_flow_entry *entry;
++
++      entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
++                                mtk_flow_ht_params);
++      if (!entry)
++              return -ENOENT;
++
++      mtk_foe_entry_clear(&eth->ppe, entry->hash);
++      rhashtable_remove_fast(&eth->flow_table, &entry->node,
++                             mtk_flow_ht_params);
++      kfree(entry);
++
++      return 0;
++}
++
++static int
++mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++      struct mtk_flow_entry *entry;
++      int timestamp;
++      u32 idle;
++
++      entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
++                                mtk_flow_ht_params);
++      if (!entry)
++              return -ENOENT;
++
++      timestamp = mtk_foe_entry_timestamp(&eth->ppe, entry->hash);
++      if (timestamp < 0)
++              return -ETIMEDOUT;
++
++      idle = mtk_eth_timestamp(eth) - timestamp;
++      f->stats.lastused = jiffies - idle * HZ;
++
++      return 0;
++}
++
++static int
++mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
++{
++      struct flow_cls_offload *cls = type_data;
++      struct net_device *dev = cb_priv;
++      struct mtk_mac *mac = netdev_priv(dev);
++      struct mtk_eth *eth = mac->hw;
++
++      if (!tc_can_offload(dev))
++              return -EOPNOTSUPP;
++
++      if (type != TC_SETUP_CLSFLOWER)
++              return -EOPNOTSUPP;
++
++      switch (cls->command) {
++      case FLOW_CLS_REPLACE:
++              return mtk_flow_offload_replace(eth, cls);
++      case FLOW_CLS_DESTROY:
++              return mtk_flow_offload_destroy(eth, cls);
++      case FLOW_CLS_STATS:
++              return mtk_flow_offload_stats(eth, cls);
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      return 0;
++}
++
++static int
++mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
++{
++      struct mtk_mac *mac = netdev_priv(dev);
++      struct mtk_eth *eth = mac->hw;
++      static LIST_HEAD(block_cb_list);
++      struct flow_block_cb *block_cb;
++      flow_setup_cb_t *cb;
++
++      if (!eth->ppe.foe_table)
++              return -EOPNOTSUPP;
++
++      if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
++              return -EOPNOTSUPP;
++
++      cb = mtk_eth_setup_tc_block_cb;
++      f->driver_block_list = &block_cb_list;
++
++      switch (f->command) {
++      case FLOW_BLOCK_BIND:
++              block_cb = flow_block_cb_lookup(f->block, cb, dev);
++              if (block_cb) {
++                      flow_block_cb_incref(block_cb);
++                      return 0;
++              }
++              block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
++              if (IS_ERR(block_cb))
++                      return PTR_ERR(block_cb);
++
++              flow_block_cb_add(block_cb, f);
++              list_add_tail(&block_cb->driver_list, &block_cb_list);
++              return 0;
++      case FLOW_BLOCK_UNBIND:
++              block_cb = flow_block_cb_lookup(f->block, cb, dev);
++              if (!block_cb)
++                      return -ENOENT;
++
++              if (flow_block_cb_decref(block_cb)) {
++                      flow_block_cb_remove(block_cb, f);
++                      list_del(&block_cb->driver_list);
++              }
++              return 0;
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
++                   void *type_data)
++{
++      if (type == TC_SETUP_FT)
++              return mtk_eth_setup_tc_block(dev, type_data);
++
++      return -EOPNOTSUPP;
++}
++
++int mtk_eth_offload_init(struct mtk_eth *eth)
++{
++      if (!eth->ppe.foe_table)
++              return 0;
++
++      return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
++}
diff --git a/target/linux/generic/backport-5.10/610-v5.13-34-docs-nf_flowtable-update-documentation-with-enhancem.patch b/target/linux/generic/backport-5.10/610-v5.13-34-docs-nf_flowtable-update-documentation-with-enhancem.patch
new file mode 100644 (file)
index 0000000..2cea1eb
--- /dev/null
@@ -0,0 +1,236 @@
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 24 Mar 2021 02:30:55 +0100
+Subject: [PATCH] docs: nf_flowtable: update documentation with
+ enhancements
+
+This patch updates the flowtable documentation to describe recent
+enhancements:
+
+- Offload action is available after the first packets go through the
+  classic forwarding path.
+- IPv4 and IPv6 are supported. Only TCP and UDP layer 4 are supported at
+  this stage.
+- Tuple has been augmented to track VLAN id and PPPoE session id.
+- Bridge and IP forwarding integration, including bridge VLAN filtering
+  support.
+- Hardware offload support.
+- Describe the [OFFLOAD] and [HW_OFFLOAD] tags in the conntrack table
+  listing.
+- Replace 'flow offload' by 'flow add' in example rulesets (preferred
+  syntax).
+- Describe existing cache limitations.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+
+--- a/Documentation/networking/nf_flowtable.rst
++++ b/Documentation/networking/nf_flowtable.rst
+@@ -4,35 +4,38 @@
+ Netfilter's flowtable infrastructure
+ ====================================
+-This documentation describes the software flowtable infrastructure available in
+-Netfilter since Linux kernel 4.16.
++This documentation describes the Netfilter flowtable infrastructure which allows
++you to define a fastpath through the flowtable datapath. This infrastructure
++also provides hardware offload support. The flowtable supports for the layer 3
++IPv4 and IPv6 and the layer 4 TCP and UDP protocols.
+ Overview
+ --------
+-Initial packets follow the classic forwarding path, once the flow enters the
+-established state according to the conntrack semantics (ie. we have seen traffic
+-in both directions), then you can decide to offload the flow to the flowtable
+-from the forward chain via the 'flow offload' action available in nftables.
+-
+-Packets that find an entry in the flowtable (ie. flowtable hit) are sent to the
+-output netdevice via neigh_xmit(), hence, they bypass the classic forwarding
+-path (the visible effect is that you do not see these packets from any of the
+-netfilter hooks coming after the ingress). In case of flowtable miss, the packet
+-follows the classic forward path.
+-
+-The flowtable uses a resizable hashtable, lookups are based on the following
+-7-tuple selectors: source, destination, layer 3 and layer 4 protocols, source
+-and destination ports and the input interface (useful in case there are several
+-conntrack zones in place).
+-
+-Flowtables are populated via the 'flow offload' nftables action, so the user can
+-selectively specify what flows are placed into the flow table. Hence, packets
+-follow the classic forwarding path unless the user explicitly instruct packets
+-to use this new alternative forwarding path via nftables policy.
++Once the first packet of the flow successfully goes through the IP forwarding
++path, from the second packet on, you might decide to offload the flow to the
++flowtable through your ruleset. The flowtable infrastructure provides a rule
++action that allows you to specify when to add a flow to the flowtable.
++
++A packet that finds a matching entry in the flowtable (ie. flowtable hit) is
++transmitted to the output netdevice via neigh_xmit(), hence, packets bypass the
++classic IP forwarding path (the visible effect is that you do not see these
++packets from any of the Netfilter hooks coming after ingress). In case that
++there is no matching entry in the flowtable (ie. flowtable miss), the packet
++follows the classic IP forwarding path.
++
++The flowtable uses a resizable hashtable. Lookups are based on the following
++n-tuple selectors: layer 2 protocol encapsulation (VLAN and PPPoE), layer 3
++source and destination, layer 4 source and destination ports and the input
++interface (useful in case there are several conntrack zones in place).
++
++The 'flow add' action allows you to populate the flowtable, the user selectively
++specifies what flows are placed into the flowtable. Hence, packets follow the
++classic IP forwarding path unless the user explicitly instruct flows to use this
++new alternative forwarding path via policy.
+-This is represented in Fig.1, which describes the classic forwarding path
+-including the Netfilter hooks and the flowtable fastpath bypass.
++The flowtable datapath is represented in Fig.1, which describes the classic IP
++forwarding path including the Netfilter hooks and the flowtable fastpath bypass.
+ ::
+@@ -67,11 +70,13 @@ including the Netfilter hooks and the fl
+              Fig.1 Netfilter hooks and flowtable interactions
+ The flowtable entry also stores the NAT configuration, so all packets are
+-mangled according to the NAT policy that matches the initial packets that went
+-through the classic forwarding path. The TTL is decremented before calling
+-neigh_xmit(). Fragmented traffic is passed up to follow the classic forwarding
+-path given that the transport selectors are missing, therefore flowtable lookup
+-is not possible.
++mangled according to the NAT policy that is specified from the classic IP
++forwarding path. The TTL is decremented before calling neigh_xmit(). Fragmented
++traffic is passed up to follow the classic IP forwarding path given that the
++transport header is missing, in this case, flowtable lookups are not possible.
++TCP RST and FIN packets are also passed up to the classic IP forwarding path to
++release the flow gracefully. Packets that exceed the MTU are also passed up to
++the classic forwarding path to report packet-too-big ICMP errors to the sender.
+ Example configuration
+ ---------------------
+@@ -85,7 +90,7 @@ flowtable and add one rule to your forwa
+               }
+               chain y {
+                       type filter hook forward priority 0; policy accept;
+-                      ip protocol tcp flow offload @f
++                      ip protocol tcp flow add @f
+                       counter packets 0 bytes 0
+               }
+       }
+@@ -103,6 +108,117 @@ flow is offloaded, you will observe that
+ does not get updated for the packets that are being forwarded through the
+ forwarding bypass.
++You can identify offloaded flows through the [OFFLOAD] tag when listing your
++connection tracking table.
++
++::
++      # conntrack -L
++      tcp      6 src=10.141.10.2 dst=192.168.10.2 sport=52728 dport=5201 src=192.168.10.2 dst=192.168.10.1 sport=5201 dport=52728 [OFFLOAD] mark=0 use=2
++
++
++Layer 2 encapsulation
++---------------------
++
++Since Linux kernel 5.13, the flowtable infrastructure discovers the real
++netdevice behind VLAN and PPPoE netdevices. The flowtable software datapath
++parses the VLAN and PPPoE layer 2 headers to extract the ethertype and the
++VLAN ID / PPPoE session ID which are used for the flowtable lookups. The
++flowtable datapath also deals with layer 2 decapsulation.
++
++You do not need to add the PPPoE and the VLAN devices to your flowtable,
++instead the real device is sufficient for the flowtable to track your flows.
++
++Bridge and IP forwarding
++------------------------
++
++Since Linux kernel 5.13, you can add bridge ports to the flowtable. The
++flowtable infrastructure discovers the topology behind the bridge device. This
++allows the flowtable to define a fastpath bypass between the bridge ports
++(represented as eth1 and eth2 in the example figure below) and the gateway
++device (represented as eth0) in your switch/router.
++
++::
++                      fastpath bypass
++               .-------------------------.
++              /                           \
++              |           IP forwarding   |
++              |          /             \ \/
++              |       br0               eth0 ..... eth0
++              .       / \                          *host B*
++               -> eth1  eth2
++                   .           *switch/router*
++                   .
++                   .
++                 eth0
++               *host A*
++
++The flowtable infrastructure also supports for bridge VLAN filtering actions
++such as PVID and untagged. You can also stack a classic VLAN device on top of
++your bridge port.
++
++If you would like that your flowtable defines a fastpath between your bridge
++ports and your IP forwarding path, you have to add your bridge ports (as
++represented by the real netdevice) to your flowtable definition.
++
++Counters
++--------
++
++The flowtable can synchronize packet and byte counters with the existing
++connection tracking entry by specifying the counter statement in your flowtable
++definition, e.g.
++
++::
++      table inet x {
++              flowtable f {
++                      hook ingress priority 0; devices = { eth0, eth1 };
++                      counter
++              }
++              ...
++      }
++
++Counter support is available since Linux kernel 5.7.
++
++Hardware offload
++----------------
++
++If your network device provides hardware offload support, you can turn it on by
++means of the 'offload' flag in your flowtable definition, e.g.
++
++::
++      table inet x {
++              flowtable f {
++                      hook ingress priority 0; devices = { eth0, eth1 };
++                      flags offload;
++              }
++              ...
++      }
++
++There is a workqueue that adds the flows to the hardware. Note that a few
++packets might still run over the flowtable software path until the workqueue has
++a chance to offload the flow to the network device.
++
++You can identify hardware offloaded flows through the [HW_OFFLOAD] tag when
++listing your connection tracking table. Please, note that the [OFFLOAD] tag
++refers to the software offload mode, so there is a distinction between [OFFLOAD]
++which refers to the software flowtable fastpath and [HW_OFFLOAD] which refers
++to the hardware offload datapath being used by the flow.
++
++The flowtable hardware offload infrastructure also supports for the DSA
++(Distributed Switch Architecture).
++
++Limitations
++-----------
++
++The flowtable behaves like a cache. The flowtable entries might get stale if
++either the destination MAC address or the egress netdevice that is used for
++transmission changes.
++
++This might be a problem if:
++
++- You run the flowtable in software mode and you combine bridge and IP
++  forwarding in your setup.
++- Hardware offload is enabled.
++
+ More reading
+ ------------
index ecb81004cbabd387a1ce621ed4cae40b27f4c1e9..e6126dbd911d67ea50d84e962d89dc361ce63789 100644 (file)
@@ -25,7 +25,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 
 --- a/net/dsa/slave.c
 +++ b/net/dsa/slave.c
-@@ -2075,7 +2075,9 @@ static void dsa_slave_switchdev_event_wo
+@@ -2109,7 +2109,9 @@ static void dsa_slave_switchdev_event_wo
  
                err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid);
                if (err) {
@@ -36,7 +36,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
                        break;
                }
                fdb_info->offloaded = true;
-@@ -2090,9 +2092,11 @@ static void dsa_slave_switchdev_event_wo
+@@ -2124,9 +2126,11 @@ static void dsa_slave_switchdev_event_wo
  
                err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid);
                if (err) {
index bb943f1312b38a8d59e64d6139cb226e2d0889f8..f542bb4d116410425c7dbdab207811b60a6c871d 100644 (file)
@@ -54,7 +54,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
        struct sk_buff *        (*xmit)(struct sk_buff *skb,
 --- a/net/dsa/slave.c
 +++ b/net/dsa/slave.c
-@@ -2050,76 +2050,66 @@ static int dsa_slave_netdevice_event(str
+@@ -2084,76 +2084,66 @@ static int dsa_slave_netdevice_event(str
        return NOTIFY_DONE;
  }
  
@@ -167,7 +167,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  }
  
  /* Called under rcu_read_lock() */
-@@ -2127,7 +2117,9 @@ static int dsa_slave_switchdev_event(str
+@@ -2161,7 +2151,9 @@ static int dsa_slave_switchdev_event(str
                                     unsigned long event, void *ptr)
  {
        struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
@@ -177,7 +177,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
        int err;
  
        if (event == SWITCHDEV_PORT_ATTR_SET) {
-@@ -2140,20 +2132,32 @@ static int dsa_slave_switchdev_event(str
+@@ -2174,20 +2166,32 @@ static int dsa_slave_switchdev_event(str
        if (!dsa_slave_dev_check(dev))
                return NOTIFY_DONE;
  
@@ -213,7 +213,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
                dev_hold(dev);
                break;
        default:
-@@ -2163,10 +2167,6 @@ static int dsa_slave_switchdev_event(str
+@@ -2197,10 +2201,6 @@ static int dsa_slave_switchdev_event(str
  
        dsa_schedule_work(&switchdev_work->work);
        return NOTIFY_OK;
index 48c2af770bece63b55a2fb50ae8530afb29aa41d..d2acb3c89565529d4ae597fff92f7a2962507c1e 100644 (file)
@@ -20,7 +20,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 
 --- a/net/dsa/slave.c
 +++ b/net/dsa/slave.c
-@@ -2122,31 +2122,29 @@ static int dsa_slave_switchdev_event(str
+@@ -2156,31 +2156,29 @@ static int dsa_slave_switchdev_event(str
        struct dsa_port *dp;
        int err;
  
@@ -68,7 +68,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
                fdb_info = ptr;
  
                if (!fdb_info->added_by_user) {
-@@ -2159,13 +2157,12 @@ static int dsa_slave_switchdev_event(str
+@@ -2193,13 +2191,12 @@ static int dsa_slave_switchdev_event(str
                switchdev_work->vid = fdb_info->vid;
  
                dev_hold(dev);
index a1f56353ea9835035b6ca99f1a094859350f33ec..591950a49e6823eede3c58cbf73108120eb3c643 100644 (file)
@@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 
 --- a/net/dsa/slave.c
 +++ b/net/dsa/slave.c
-@@ -2135,6 +2135,9 @@ static int dsa_slave_switchdev_event(str
+@@ -2169,6 +2169,9 @@ static int dsa_slave_switchdev_event(str
  
                dp = dsa_slave_to_port(dev);
  
index e576ff8ce264454af23b6b7f83df9a4b363f1eab..91e170964f6fac28b136579c6c5e460077c3f67a 100644 (file)
@@ -170,7 +170,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
         */
 --- a/net/dsa/slave.c
 +++ b/net/dsa/slave.c
-@@ -2112,6 +2112,28 @@ static void dsa_slave_switchdev_event_wo
+@@ -2146,6 +2146,28 @@ static void dsa_slave_switchdev_event_wo
                dev_put(dp->slave);
  }
  
@@ -199,7 +199,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  /* Called under rcu_read_lock() */
  static int dsa_slave_switchdev_event(struct notifier_block *unused,
                                     unsigned long event, void *ptr)
-@@ -2130,10 +2152,37 @@ static int dsa_slave_switchdev_event(str
+@@ -2164,10 +2186,37 @@ static int dsa_slave_switchdev_event(str
                return notifier_from_errno(err);
        case SWITCHDEV_FDB_ADD_TO_DEVICE:
        case SWITCHDEV_FDB_DEL_TO_DEVICE:
@@ -240,7 +240,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  
                if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del)
                        return NOTIFY_DONE;
-@@ -2148,18 +2197,13 @@ static int dsa_slave_switchdev_event(str
+@@ -2182,18 +2231,13 @@ static int dsa_slave_switchdev_event(str
                switchdev_work->port = dp->index;
                switchdev_work->event = event;
  
index 97aa7a673b22e09f04b9f79ae335b13c830e6c19..7d5f847435d0c44cdf9f6a9d28187d59c746a78e 100644 (file)
@@ -812,9 +812,9 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  void flow_offload_teardown(struct flow_offload *flow);
  
 +int nf_flow_table_iterate(struct nf_flowtable *flow_table,
-+                      void (*iter)(struct flow_offload *flow, void *data),
-+                      void *data);
++                        void (*iter)(struct flow_offload *flow, void *data),
++                        void *data);
 +
int nf_flow_snat_port(const struct flow_offload *flow,
-                     struct sk_buff *skb, unsigned int thoff,
-                     u8 protocol, enum flow_offload_tuple_dir dir);
void nf_flow_snat_port(const struct flow_offload *flow,
+                      struct sk_buff *skb, unsigned int thoff,
+                      u8 protocol, enum flow_offload_tuple_dir dir);
diff --git a/target/linux/generic/pending-5.10/640-00-netfilter-flowtable-add-hash-offset-field-to-tuple.patch b/target/linux/generic/pending-5.10/640-00-netfilter-flowtable-add-hash-offset-field-to-tuple.patch
deleted file mode 100644 (file)
index c881ccf..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Fri, 20 Nov 2020 13:49:13 +0100
-Subject: [PATCH] netfilter: flowtable: add hash offset field to tuple
-
-Add a placeholder field to calculate hash tuple offset. Similar to
-2c407aca6497 ("netfilter: conntrack: avoid gcc-10 zero-length-bounds
-warning").
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -107,6 +107,10 @@ struct flow_offload_tuple {
-       u8                              l3proto;
-       u8                              l4proto;
-+
-+      /* All members above are keys for lookups, see flow_offload_hash(). */
-+      struct { }                      __hash;
-+
-       u8                              dir;
-       u16                             mtu;
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -191,14 +191,14 @@ static u32 flow_offload_hash(const void
- {
-       const struct flow_offload_tuple *tuple = data;
--      return jhash(tuple, offsetof(struct flow_offload_tuple, dir), seed);
-+      return jhash(tuple, offsetof(struct flow_offload_tuple, __hash), seed);
- }
- static u32 flow_offload_hash_obj(const void *data, u32 len, u32 seed)
- {
-       const struct flow_offload_tuple_rhash *tuplehash = data;
--      return jhash(&tuplehash->tuple, offsetof(struct flow_offload_tuple, dir), seed);
-+      return jhash(&tuplehash->tuple, offsetof(struct flow_offload_tuple, __hash), seed);
- }
- static int flow_offload_hash_cmp(struct rhashtable_compare_arg *arg,
-@@ -207,7 +207,7 @@ static int flow_offload_hash_cmp(struct
-       const struct flow_offload_tuple *tuple = arg->key;
-       const struct flow_offload_tuple_rhash *x = ptr;
--      if (memcmp(&x->tuple, tuple, offsetof(struct flow_offload_tuple, dir)))
-+      if (memcmp(&x->tuple, tuple, offsetof(struct flow_offload_tuple, __hash)))
-               return 1;
-       return 0;
diff --git a/target/linux/generic/pending-5.10/640-01-netfilter-flowtable-add-xmit-path-types.patch b/target/linux/generic/pending-5.10/640-01-netfilter-flowtable-add-xmit-path-types.patch
deleted file mode 100644 (file)
index e47d03e..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Fri, 20 Nov 2020 13:49:14 +0100
-Subject: [PATCH] netfilter: flowtable: add xmit path types
-
-Add the xmit_type field that defines the two supported xmit paths in the
-flowtable data plane, which are the neighbour and the xfrm xmit paths.
-This patch prepares for new flowtable xmit path types to come.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -89,6 +89,11 @@ enum flow_offload_tuple_dir {
-       FLOW_OFFLOAD_DIR_MAX = IP_CT_DIR_MAX
- };
-+enum flow_offload_xmit_type {
-+      FLOW_OFFLOAD_XMIT_NEIGH         = 0,
-+      FLOW_OFFLOAD_XMIT_XFRM,
-+};
-+
- struct flow_offload_tuple {
-       union {
-               struct in_addr          src_v4;
-@@ -111,7 +116,8 @@ struct flow_offload_tuple {
-       /* All members above are keys for lookups, see flow_offload_hash(). */
-       struct { }                      __hash;
--      u8                              dir;
-+      u8                              dir:6,
-+                                      xmit_type:2;
-       u16                             mtu;
-@@ -158,7 +164,8 @@ static inline __s32 nf_flow_timeout_delt
- struct nf_flow_route {
-       struct {
--              struct dst_entry        *dst;
-+              struct dst_entry                *dst;
-+              enum flow_offload_xmit_type     xmit_type;
-       } tuple[FLOW_OFFLOAD_DIR_MAX];
- };
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -95,6 +95,7 @@ static int flow_offload_fill_route(struc
-       }
-       flow_tuple->iifidx = other_dst->dev->ifindex;
-+      flow_tuple->xmit_type = route->tuple[dir].xmit_type;
-       flow_tuple->dst_cache = dst;
-       return 0;
---- a/net/netfilter/nf_flow_table_ip.c
-+++ b/net/netfilter/nf_flow_table_ip.c
-@@ -220,10 +220,20 @@ static bool nf_flow_exceeds_mtu(const st
-       return true;
- }
--static int nf_flow_offload_dst_check(struct dst_entry *dst)
-+static inline struct dst_entry *
-+nft_flow_dst(struct flow_offload_tuple_rhash *tuplehash)
- {
--      if (unlikely(dst_xfrm(dst)))
-+      return tuplehash->tuple.dst_cache;
-+}
-+
-+static int nf_flow_offload_dst_check(struct flow_offload_tuple_rhash *tuplehash)
-+{
-+      struct dst_entry *dst;
-+
-+      if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
-+              dst = nft_flow_dst(tuplehash);
-               return dst_check(dst, 0) ? 0 : -1;
-+      }
-       return 0;
- }
-@@ -265,8 +275,6 @@ nf_flow_offload_ip_hook(void *priv, stru
-       dir = tuplehash->tuple.dir;
-       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
--      rt = (struct rtable *)flow->tuplehash[dir].tuple.dst_cache;
--      outdev = rt->dst.dev;
-       if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
-               return NF_ACCEPT;
-@@ -280,7 +288,7 @@ nf_flow_offload_ip_hook(void *priv, stru
-       flow_offload_refresh(flow_table, flow);
--      if (nf_flow_offload_dst_check(&rt->dst)) {
-+      if (nf_flow_offload_dst_check(tuplehash)) {
-               flow_offload_teardown(flow);
-               return NF_ACCEPT;
-       }
-@@ -295,13 +303,16 @@ nf_flow_offload_ip_hook(void *priv, stru
-       if (flow_table->flags & NF_FLOWTABLE_COUNTER)
-               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
--      if (unlikely(dst_xfrm(&rt->dst))) {
-+      rt = (struct rtable *)tuplehash->tuple.dst_cache;
-+
-+      if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
-               memset(skb->cb, 0, sizeof(struct inet_skb_parm));
-               IPCB(skb)->iif = skb->dev->ifindex;
-               IPCB(skb)->flags = IPSKB_FORWARDED;
-               return nf_flow_xmit_xfrm(skb, state, &rt->dst);
-       }
-+      outdev = rt->dst.dev;
-       skb->dev = outdev;
-       nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
-       skb_dst_set_noref(skb, &rt->dst);
-@@ -506,8 +517,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       dir = tuplehash->tuple.dir;
-       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
--      rt = (struct rt6_info *)flow->tuplehash[dir].tuple.dst_cache;
--      outdev = rt->dst.dev;
-       if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
-               return NF_ACCEPT;
-@@ -518,7 +527,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       flow_offload_refresh(flow_table, flow);
--      if (nf_flow_offload_dst_check(&rt->dst)) {
-+      if (nf_flow_offload_dst_check(tuplehash)) {
-               flow_offload_teardown(flow);
-               return NF_ACCEPT;
-       }
-@@ -536,13 +545,16 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       if (flow_table->flags & NF_FLOWTABLE_COUNTER)
-               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
--      if (unlikely(dst_xfrm(&rt->dst))) {
-+      rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
-+
-+      if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
-               memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
-               IP6CB(skb)->iif = skb->dev->ifindex;
-               IP6CB(skb)->flags = IP6SKB_FORWARDED;
-               return nf_flow_xmit_xfrm(skb, state, &rt->dst);
-       }
-+      outdev = rt->dst.dev;
-       skb->dev = outdev;
-       nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
-       skb_dst_set_noref(skb, &rt->dst);
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -19,6 +19,22 @@ struct nft_flow_offload {
-       struct nft_flowtable    *flowtable;
- };
-+static enum flow_offload_xmit_type nft_xmit_type(struct dst_entry *dst)
-+{
-+      if (dst_xfrm(dst))
-+              return FLOW_OFFLOAD_XMIT_XFRM;
-+
-+      return FLOW_OFFLOAD_XMIT_NEIGH;
-+}
-+
-+static void nft_default_forward_path(struct nf_flow_route *route,
-+                                   struct dst_entry *dst_cache,
-+                                   enum ip_conntrack_dir dir)
-+{
-+      route->tuple[dir].dst           = dst_cache;
-+      route->tuple[dir].xmit_type     = nft_xmit_type(dst_cache);
-+}
-+
- static int nft_flow_route(const struct nft_pktinfo *pkt,
-                         const struct nf_conn *ct,
-                         struct nf_flow_route *route,
-@@ -44,8 +60,8 @@ static int nft_flow_route(const struct n
-       if (!other_dst)
-               return -ENOENT;
--      route->tuple[dir].dst           = this_dst;
--      route->tuple[!dir].dst          = other_dst;
-+      nft_default_forward_path(route, this_dst, dir);
-+      nft_default_forward_path(route, other_dst, !dir);
-       return 0;
- }
diff --git a/target/linux/generic/pending-5.10/640-02-net-resolve-forwarding-path-from-virtual-netdevice-a.patch b/target/linux/generic/pending-5.10/640-02-net-resolve-forwarding-path-from-virtual-netdevice-a.patch
deleted file mode 100644 (file)
index 224831d..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Thu, 4 Mar 2021 23:18:11 +0100
-Subject: [PATCH] net: resolve forwarding path from virtual netdevice and
- HW destination address
-
-This patch adds dev_fill_forward_path() which resolves the path to reach
-the real netdevice from the IP forwarding side. This function takes as
-input the netdevice and the destination hardware address and it walks
-down the devices calling .ndo_fill_forward_path() for each device until
-the real device is found.
-
-For instance, assuming the following topology:
-
-               IP forwarding
-              /             \
-           br0              eth0
-           / \
-       eth1  eth2
-        .
-        .
-        .
-       ethX
- ab:cd:ef:ab:cd:ef
-
-where eth1 and eth2 are bridge ports and eth0 provides WAN connectivity.
-ethX is the interface in another box which is connected to the eth1
-bridge port.
-
-For packets going through IP forwarding to br0 whose destination MAC
-address is ab:cd:ef:ab:cd:ef, dev_fill_forward_path() provides the
-following path:
-
-       br0 -> eth1
-
-.ndo_fill_forward_path for br0 looks up at the FDB for the bridge port
-from the destination MAC address to get the bridge port eth1.
-
-This information allows to create a fast path that bypasses the classic
-bridge and IP forwarding paths, so packets go directly from the bridge
-port eth1 to eth0 (wan interface) and vice versa.
-
-             fast path
-      .------------------------.
-     /                          \
-    |           IP forwarding   |
-    |          /             \  \/
-    |       br0               eth0
-    .       / \
-     -> eth1  eth2
-        .
-        .
-        .
-       ethX
- ab:cd:ef:ab:cd:ef
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -827,6 +827,27 @@ typedef u16 (*select_queue_fallback_t)(s
-                                      struct sk_buff *skb,
-                                      struct net_device *sb_dev);
-+enum net_device_path_type {
-+      DEV_PATH_ETHERNET = 0,
-+};
-+
-+struct net_device_path {
-+      enum net_device_path_type       type;
-+      const struct net_device         *dev;
-+};
-+
-+#define NET_DEVICE_PATH_STACK_MAX     5
-+
-+struct net_device_path_stack {
-+      int                     num_paths;
-+      struct net_device_path  path[NET_DEVICE_PATH_STACK_MAX];
-+};
-+
-+struct net_device_path_ctx {
-+      const struct net_device *dev;
-+      const u8                *daddr;
-+};
-+
- enum tc_setup_type {
-       TC_SETUP_QDISC_MQPRIO,
-       TC_SETUP_CLSU32,
-@@ -1273,6 +1294,8 @@ struct netdev_net_notifier {
-  * struct net_device *(*ndo_get_peer_dev)(struct net_device *dev);
-  *    If a device is paired with a peer device, return the peer instance.
-  *    The caller must be under RCU read context.
-+ * int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, struct net_device_path *path);
-+ *     Get the forwarding path to reach the real device from the HW destination address
-  */
- struct net_device_ops {
-       int                     (*ndo_init)(struct net_device *dev);
-@@ -1481,6 +1504,8 @@ struct net_device_ops {
-       int                     (*ndo_tunnel_ctl)(struct net_device *dev,
-                                                 struct ip_tunnel_parm *p, int cmd);
-       struct net_device *     (*ndo_get_peer_dev)(struct net_device *dev);
-+      int                     (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx,
-+                                                         struct net_device_path *path);
- };
- /**
-@@ -2795,6 +2820,8 @@ void dev_remove_offload(struct packet_of
- int dev_get_iflink(const struct net_device *dev);
- int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
-+int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
-+                        struct net_device_path_stack *stack);
- struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
-                                     unsigned short mask);
- struct net_device *dev_get_by_name(struct net *net, const char *name);
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -847,6 +847,52 @@ int dev_fill_metadata_dst(struct net_dev
- }
- EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
-+static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
-+{
-+      int k = stack->num_paths++;
-+
-+      if (WARN_ON_ONCE(k >= NET_DEVICE_PATH_STACK_MAX))
-+              return NULL;
-+
-+      return &stack->path[k];
-+}
-+
-+int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
-+                        struct net_device_path_stack *stack)
-+{
-+      const struct net_device *last_dev;
-+      struct net_device_path_ctx ctx = {
-+              .dev    = dev,
-+              .daddr  = daddr,
-+      };
-+      struct net_device_path *path;
-+      int ret = 0;
-+
-+      stack->num_paths = 0;
-+      while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
-+              last_dev = ctx.dev;
-+              path = dev_fwd_path(stack);
-+              if (!path)
-+                      return -1;
-+
-+              memset(path, 0, sizeof(struct net_device_path));
-+              ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
-+              if (ret < 0)
-+                      return -1;
-+
-+              if (WARN_ON_ONCE(last_dev == ctx.dev))
-+                      return -1;
-+      }
-+      path = dev_fwd_path(stack);
-+      if (!path)
-+              return -1;
-+      path->type = DEV_PATH_ETHERNET;
-+      path->dev = ctx.dev;
-+
-+      return ret;
-+}
-+EXPORT_SYMBOL_GPL(dev_fill_forward_path);
-+
- /**
-  *    __dev_get_by_name       - find a device by its name
-  *    @net: the applicable net namespace
diff --git a/target/linux/generic/pending-5.10/640-03-net-8021q-resolve-forwarding-path-for-vlan-devices.patch b/target/linux/generic/pending-5.10/640-03-net-8021q-resolve-forwarding-path-for-vlan-devices.patch
deleted file mode 100644 (file)
index 69e025b..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Fri, 20 Nov 2020 13:49:16 +0100
-Subject: [PATCH] net: 8021q: resolve forwarding path for vlan devices
-
-Add .ndo_fill_forward_path for vlan devices.
-
-For instance, assuming the following topology:
-
-                   IP forwarding
-                  /             \
-            eth0.100             eth0
-            |
-            eth0
-            .
-            .
-            .
-           ethX
-     ab:cd:ef:ab:cd:ef
-
-For packets going through IP forwarding to eth0.100 whose destination
-MAC address is ab:cd:ef:ab:cd:ef, dev_fill_forward_path() provides the
-following path:
-
-        eth0.100 -> eth0
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -829,11 +829,18 @@ typedef u16 (*select_queue_fallback_t)(s
- enum net_device_path_type {
-       DEV_PATH_ETHERNET = 0,
-+      DEV_PATH_VLAN,
- };
- struct net_device_path {
-       enum net_device_path_type       type;
-       const struct net_device         *dev;
-+      union {
-+              struct {
-+                      u16             id;
-+                      __be16          proto;
-+              } encap;
-+      };
- };
- #define NET_DEVICE_PATH_STACK_MAX     5
---- a/net/8021q/vlan_dev.c
-+++ b/net/8021q/vlan_dev.c
-@@ -767,6 +767,20 @@ static int vlan_dev_get_iflink(const str
-       return real_dev->ifindex;
- }
-+static int vlan_dev_fill_forward_path(struct net_device_path_ctx *ctx,
-+                                    struct net_device_path *path)
-+{
-+      struct vlan_dev_priv *vlan = vlan_dev_priv(ctx->dev);
-+
-+      path->type = DEV_PATH_VLAN;
-+      path->encap.id = vlan->vlan_id;
-+      path->encap.proto = vlan->vlan_proto;
-+      path->dev = ctx->dev;
-+      ctx->dev = vlan->real_dev;
-+
-+      return 0;
-+}
-+
- static const struct ethtool_ops vlan_ethtool_ops = {
-       .get_link_ksettings     = vlan_ethtool_get_link_ksettings,
-       .get_drvinfo            = vlan_ethtool_get_drvinfo,
-@@ -805,6 +819,7 @@ static const struct net_device_ops vlan_
- #endif
-       .ndo_fix_features       = vlan_dev_fix_features,
-       .ndo_get_iflink         = vlan_dev_get_iflink,
-+      .ndo_fill_forward_path  = vlan_dev_fill_forward_path,
- };
- static void vlan_dev_free(struct net_device *dev)
diff --git a/target/linux/generic/pending-5.10/640-04-bridge-resolve-forwarding-path-for-bridge-devices.patch b/target/linux/generic/pending-5.10/640-04-bridge-resolve-forwarding-path-for-bridge-devices.patch
deleted file mode 100644 (file)
index cfb817f..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Fri, 20 Nov 2020 13:49:17 +0100
-Subject: [PATCH] bridge: resolve forwarding path for bridge devices
-
-Add .ndo_fill_forward_path for bridge devices.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -830,6 +830,7 @@ typedef u16 (*select_queue_fallback_t)(s
- enum net_device_path_type {
-       DEV_PATH_ETHERNET = 0,
-       DEV_PATH_VLAN,
-+      DEV_PATH_BRIDGE,
- };
- struct net_device_path {
---- a/net/bridge/br_device.c
-+++ b/net/bridge/br_device.c
-@@ -398,6 +398,32 @@ static int br_del_slave(struct net_devic
-       return br_del_if(br, slave_dev);
- }
-+static int br_fill_forward_path(struct net_device_path_ctx *ctx,
-+                              struct net_device_path *path)
-+{
-+      struct net_bridge_fdb_entry *f;
-+      struct net_bridge_port *dst;
-+      struct net_bridge *br;
-+
-+      if (netif_is_bridge_port(ctx->dev))
-+              return -1;
-+
-+      br = netdev_priv(ctx->dev);
-+      f = br_fdb_find_rcu(br, ctx->daddr, 0);
-+      if (!f || !f->dst)
-+              return -1;
-+
-+      dst = READ_ONCE(f->dst);
-+      if (!dst)
-+              return -1;
-+
-+      path->type = DEV_PATH_BRIDGE;
-+      path->dev = dst->br->dev;
-+      ctx->dev = dst->dev;
-+
-+      return 0;
-+}
-+
- static const struct ethtool_ops br_ethtool_ops = {
-       .get_drvinfo             = br_getinfo,
-       .get_link                = ethtool_op_get_link,
-@@ -432,6 +458,7 @@ static const struct net_device_ops br_ne
-       .ndo_bridge_setlink      = br_setlink,
-       .ndo_bridge_dellink      = br_dellink,
-       .ndo_features_check      = passthru_features_check,
-+      .ndo_fill_forward_path   = br_fill_forward_path,
- };
- static struct device_type br_type = {
diff --git a/target/linux/generic/pending-5.10/640-05-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch b/target/linux/generic/pending-5.10/640-05-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch
deleted file mode 100644 (file)
index 1f61cff..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Fri, 20 Nov 2020 13:49:18 +0100
-Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
- obtain ingress device
-
-Obtain the ingress device in the tuple from the route in the reply
-direction. Use dev_fill_forward_path() instead to get the real ingress
-device for this flow.
-
-Fall back to use the ingress device that the IP forwarding route
-provides if:
-
-- dev_fill_forward_path() finds no real ingress device.
-- the ingress device that is obtained is not part of the flowtable
-  devices.
-- this route has a xfrm policy.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -165,6 +165,9 @@ static inline __s32 nf_flow_timeout_delt
- struct nf_flow_route {
-       struct {
-               struct dst_entry                *dst;
-+              struct {
-+                      u32                     ifindex;
-+              } in;
-               enum flow_offload_xmit_type     xmit_type;
-       } tuple[FLOW_OFFLOAD_DIR_MAX];
- };
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -79,7 +79,6 @@ static int flow_offload_fill_route(struc
-                                  enum flow_offload_tuple_dir dir)
- {
-       struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
--      struct dst_entry *other_dst = route->tuple[!dir].dst;
-       struct dst_entry *dst = route->tuple[dir].dst;
-       if (!dst_hold_safe(route->tuple[dir].dst))
-@@ -94,7 +93,7 @@ static int flow_offload_fill_route(struc
-               break;
-       }
--      flow_tuple->iifidx = other_dst->dev->ifindex;
-+      flow_tuple->iifidx = route->tuple[dir].in.ifindex;
-       flow_tuple->xmit_type = route->tuple[dir].xmit_type;
-       flow_tuple->dst_cache = dst;
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -31,14 +31,104 @@ static void nft_default_forward_path(str
-                                    struct dst_entry *dst_cache,
-                                    enum ip_conntrack_dir dir)
- {
-+      route->tuple[!dir].in.ifindex   = dst_cache->dev->ifindex;
-       route->tuple[dir].dst           = dst_cache;
-       route->tuple[dir].xmit_type     = nft_xmit_type(dst_cache);
- }
-+static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
-+                                   const struct dst_entry *dst_cache,
-+                                   const struct nf_conn *ct,
-+                                   enum ip_conntrack_dir dir,
-+                                   struct net_device_path_stack *stack)
-+{
-+      const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
-+      struct net_device *dev = dst_cache->dev;
-+      unsigned char ha[ETH_ALEN];
-+      struct neighbour *n;
-+      u8 nud_state;
-+
-+      n = dst_neigh_lookup(dst_cache, daddr);
-+      if (!n)
-+              return -1;
-+
-+      read_lock_bh(&n->lock);
-+      nud_state = n->nud_state;
-+      ether_addr_copy(ha, n->ha);
-+      read_unlock_bh(&n->lock);
-+      neigh_release(n);
-+
-+      if (!(nud_state & NUD_VALID))
-+              return -1;
-+
-+      return dev_fill_forward_path(dev, ha, stack);
-+}
-+
-+struct nft_forward_info {
-+      const struct net_device *indev;
-+};
-+
-+static void nft_dev_path_info(const struct net_device_path_stack *stack,
-+                            struct nft_forward_info *info)
-+{
-+      const struct net_device_path *path;
-+      int i;
-+
-+      for (i = 0; i < stack->num_paths; i++) {
-+              path = &stack->path[i];
-+              switch (path->type) {
-+              case DEV_PATH_ETHERNET:
-+                      info->indev = path->dev;
-+                      break;
-+              case DEV_PATH_VLAN:
-+              case DEV_PATH_BRIDGE:
-+              default:
-+                      info->indev = NULL;
-+                      break;
-+              }
-+      }
-+}
-+
-+static bool nft_flowtable_find_dev(const struct net_device *dev,
-+                                 struct nft_flowtable *ft)
-+{
-+      struct nft_hook *hook;
-+      bool found = false;
-+
-+      list_for_each_entry_rcu(hook, &ft->hook_list, list) {
-+              if (hook->ops.dev != dev)
-+                      continue;
-+
-+              found = true;
-+              break;
-+      }
-+
-+      return found;
-+}
-+
-+static void nft_dev_forward_path(struct nf_flow_route *route,
-+                               const struct nf_conn *ct,
-+                               enum ip_conntrack_dir dir,
-+                               struct nft_flowtable *ft)
-+{
-+      const struct dst_entry *dst = route->tuple[dir].dst;
-+      struct net_device_path_stack stack;
-+      struct nft_forward_info info = {};
-+
-+      if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
-+              nft_dev_path_info(&stack, &info);
-+
-+      if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
-+              return;
-+
-+      route->tuple[!dir].in.ifindex = info.indev->ifindex;
-+}
-+
- static int nft_flow_route(const struct nft_pktinfo *pkt,
-                         const struct nf_conn *ct,
-                         struct nf_flow_route *route,
--                        enum ip_conntrack_dir dir)
-+                        enum ip_conntrack_dir dir,
-+                        struct nft_flowtable *ft)
- {
-       struct dst_entry *this_dst = skb_dst(pkt->skb);
-       struct dst_entry *other_dst = NULL;
-@@ -63,6 +153,12 @@ static int nft_flow_route(const struct n
-       nft_default_forward_path(route, this_dst, dir);
-       nft_default_forward_path(route, other_dst, !dir);
-+      if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH &&
-+          route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
-+              nft_dev_forward_path(route, ct, dir, ft);
-+              nft_dev_forward_path(route, ct, !dir, ft);
-+      }
-+
-       return 0;
- }
-@@ -90,8 +186,8 @@ static void nft_flow_offload_eval(const
-       struct nft_flow_offload *priv = nft_expr_priv(expr);
-       struct nf_flowtable *flowtable = &priv->flowtable->data;
-       struct tcphdr _tcph, *tcph = NULL;
-+      struct nf_flow_route route = {};
-       enum ip_conntrack_info ctinfo;
--      struct nf_flow_route route;
-       struct flow_offload *flow;
-       enum ip_conntrack_dir dir;
-       struct nf_conn *ct;
-@@ -128,7 +224,7 @@ static void nft_flow_offload_eval(const
-               goto out;
-       dir = CTINFO2DIR(ctinfo);
--      if (nft_flow_route(pkt, ct, &route, dir) < 0)
-+      if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
-               goto err_flow_route;
-       flow = flow_offload_alloc(ct);
diff --git a/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch b/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch
deleted file mode 100644 (file)
index ada14e6..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Thu, 4 Mar 2021 03:26:35 +0100
-Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
- obtain egress device
-
-The egress device in the tuple is obtained from route. Use
-dev_fill_forward_path() instead to provide the real egress device for
-this flow whenever this is available.
-
-The new FLOW_OFFLOAD_XMIT_DIRECT type uses dev_queue_xmit() to transmit
-ethernet frames. Cache the source and destination hardware address to
-use dev_queue_xmit() to transfer packets.
-
-The FLOW_OFFLOAD_XMIT_DIRECT replaces FLOW_OFFLOAD_XMIT_NEIGH if
-dev_fill_forward_path() finds a direct transmit path.
-
-In case of topology updates, if peer is moved to different bridge port,
-the connection will time out, reconnect will result in a new entry with
-the correct path. Snooping fdb updates would allow for cleaning up stale
-flowtable entries.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -92,6 +92,7 @@ enum flow_offload_tuple_dir {
- enum flow_offload_xmit_type {
-       FLOW_OFFLOAD_XMIT_NEIGH         = 0,
-       FLOW_OFFLOAD_XMIT_XFRM,
-+      FLOW_OFFLOAD_XMIT_DIRECT,
- };
- struct flow_offload_tuple {
-@@ -120,8 +121,14 @@ struct flow_offload_tuple {
-                                       xmit_type:2;
-       u16                             mtu;
--
--      struct dst_entry                *dst_cache;
-+      union {
-+              struct dst_entry        *dst_cache;
-+              struct {
-+                      u32             ifidx;
-+                      u8              h_source[ETH_ALEN];
-+                      u8              h_dest[ETH_ALEN];
-+              } out;
-+      };
- };
- struct flow_offload_tuple_rhash {
-@@ -168,6 +175,11 @@ struct nf_flow_route {
-               struct {
-                       u32                     ifindex;
-               } in;
-+              struct {
-+                      u32                     ifindex;
-+                      u8                      h_source[ETH_ALEN];
-+                      u8                      h_dest[ETH_ALEN];
-+              } out;
-               enum flow_offload_xmit_type     xmit_type;
-       } tuple[FLOW_OFFLOAD_DIR_MAX];
- };
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -81,9 +81,6 @@ static int flow_offload_fill_route(struc
-       struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
-       struct dst_entry *dst = route->tuple[dir].dst;
--      if (!dst_hold_safe(route->tuple[dir].dst))
--              return -1;
--
-       switch (flow_tuple->l3proto) {
-       case NFPROTO_IPV4:
-               flow_tuple->mtu = ip_dst_mtu_maybe_forward(dst, true);
-@@ -94,12 +91,36 @@ static int flow_offload_fill_route(struc
-       }
-       flow_tuple->iifidx = route->tuple[dir].in.ifindex;
-+
-+      switch (route->tuple[dir].xmit_type) {
-+      case FLOW_OFFLOAD_XMIT_DIRECT:
-+              memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest,
-+                     ETH_ALEN);
-+              memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
-+                     ETH_ALEN);
-+              flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
-+              break;
-+      case FLOW_OFFLOAD_XMIT_XFRM:
-+      case FLOW_OFFLOAD_XMIT_NEIGH:
-+              if (!dst_hold_safe(route->tuple[dir].dst))
-+                      return -1;
-+
-+              flow_tuple->dst_cache = dst;
-+              break;
-+      }
-       flow_tuple->xmit_type = route->tuple[dir].xmit_type;
--      flow_tuple->dst_cache = dst;
-       return 0;
- }
-+static void nft_flow_dst_release(struct flow_offload *flow,
-+                               enum flow_offload_tuple_dir dir)
-+{
-+      if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
-+          flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
-+              dst_release(flow->tuplehash[dir].tuple.dst_cache);
-+}
-+
- int flow_offload_route_init(struct flow_offload *flow,
-                           const struct nf_flow_route *route)
- {
-@@ -118,7 +139,7 @@ int flow_offload_route_init(struct flow_
-       return 0;
- err_route_reply:
--      dst_release(route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].dst);
-+      nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_ORIGINAL);
-       return err;
- }
-@@ -169,8 +190,8 @@ static void flow_offload_fixup_ct(struct
- static void flow_offload_route_release(struct flow_offload *flow)
- {
--      dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_cache);
--      dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_cache);
-+      nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_ORIGINAL);
-+      nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_REPLY);
- }
- void flow_offload_free(struct flow_offload *flow)
---- a/net/netfilter/nf_flow_table_ip.c
-+++ b/net/netfilter/nf_flow_table_ip.c
-@@ -248,6 +248,24 @@ static unsigned int nf_flow_xmit_xfrm(st
-       return NF_STOLEN;
- }
-+static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
-+                                     const struct flow_offload_tuple_rhash *tuplehash,
-+                                     unsigned short type)
-+{
-+      struct net_device *outdev;
-+
-+      outdev = dev_get_by_index_rcu(net, tuplehash->tuple.out.ifidx);
-+      if (!outdev)
-+              return NF_DROP;
-+
-+      skb->dev = outdev;
-+      dev_hard_header(skb, skb->dev, type, tuplehash->tuple.out.h_dest,
-+                      tuplehash->tuple.out.h_source, skb->len);
-+      dev_queue_xmit(skb);
-+
-+      return NF_STOLEN;
-+}
-+
- unsigned int
- nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
-                       const struct nf_hook_state *state)
-@@ -262,6 +280,7 @@ nf_flow_offload_ip_hook(void *priv, stru
-       unsigned int thoff;
-       struct iphdr *iph;
-       __be32 nexthop;
-+      int ret;
-       if (skb->protocol != htons(ETH_P_IP))
-               return NF_ACCEPT;
-@@ -303,22 +322,32 @@ nf_flow_offload_ip_hook(void *priv, stru
-       if (flow_table->flags & NF_FLOWTABLE_COUNTER)
-               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
--      rt = (struct rtable *)tuplehash->tuple.dst_cache;
--
-       if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
-+              rt = (struct rtable *)tuplehash->tuple.dst_cache;
-               memset(skb->cb, 0, sizeof(struct inet_skb_parm));
-               IPCB(skb)->iif = skb->dev->ifindex;
-               IPCB(skb)->flags = IPSKB_FORWARDED;
-               return nf_flow_xmit_xfrm(skb, state, &rt->dst);
-       }
--      outdev = rt->dst.dev;
--      skb->dev = outdev;
--      nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
--      skb_dst_set_noref(skb, &rt->dst);
--      neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb);
-+      switch (tuplehash->tuple.xmit_type) {
-+      case FLOW_OFFLOAD_XMIT_NEIGH:
-+              rt = (struct rtable *)tuplehash->tuple.dst_cache;
-+              outdev = rt->dst.dev;
-+              skb->dev = outdev;
-+              nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
-+              skb_dst_set_noref(skb, &rt->dst);
-+              neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb);
-+              ret = NF_STOLEN;
-+              break;
-+      case FLOW_OFFLOAD_XMIT_DIRECT:
-+              ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IP);
-+              if (ret == NF_DROP)
-+                      flow_offload_teardown(flow);
-+              break;
-+      }
--      return NF_STOLEN;
-+      return ret;
- }
- EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
-@@ -504,6 +533,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       struct net_device *outdev;
-       struct ipv6hdr *ip6h;
-       struct rt6_info *rt;
-+      int ret;
-       if (skb->protocol != htons(ETH_P_IPV6))
-               return NF_ACCEPT;
-@@ -545,21 +575,31 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       if (flow_table->flags & NF_FLOWTABLE_COUNTER)
-               nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
--      rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
--
-       if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
-+              rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
-               memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
-               IP6CB(skb)->iif = skb->dev->ifindex;
-               IP6CB(skb)->flags = IP6SKB_FORWARDED;
-               return nf_flow_xmit_xfrm(skb, state, &rt->dst);
-       }
--      outdev = rt->dst.dev;
--      skb->dev = outdev;
--      nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
--      skb_dst_set_noref(skb, &rt->dst);
--      neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
-+      switch (tuplehash->tuple.xmit_type) {
-+      case FLOW_OFFLOAD_XMIT_NEIGH:
-+              rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
-+              outdev = rt->dst.dev;
-+              skb->dev = outdev;
-+              nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
-+              skb_dst_set_noref(skb, &rt->dst);
-+              neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
-+              ret = NF_STOLEN;
-+              break;
-+      case FLOW_OFFLOAD_XMIT_DIRECT:
-+              ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IPV6);
-+              if (ret == NF_DROP)
-+                      flow_offload_teardown(flow);
-+              break;
-+      }
--      return NF_STOLEN;
-+      return ret;
- }
- EXPORT_SYMBOL_GPL(nf_flow_offload_ipv6_hook);
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -39,12 +39,11 @@ static void nft_default_forward_path(str
- static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
-                                    const struct dst_entry *dst_cache,
-                                    const struct nf_conn *ct,
--                                   enum ip_conntrack_dir dir,
-+                                   enum ip_conntrack_dir dir, u8 *ha,
-                                    struct net_device_path_stack *stack)
- {
-       const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
-       struct net_device *dev = dst_cache->dev;
--      unsigned char ha[ETH_ALEN];
-       struct neighbour *n;
-       u8 nud_state;
-@@ -66,27 +65,43 @@ static int nft_dev_fill_forward_path(con
- struct nft_forward_info {
-       const struct net_device *indev;
-+      const struct net_device *outdev;
-+      u8 h_source[ETH_ALEN];
-+      u8 h_dest[ETH_ALEN];
-+      enum flow_offload_xmit_type xmit_type;
- };
- static void nft_dev_path_info(const struct net_device_path_stack *stack,
--                            struct nft_forward_info *info)
-+                            struct nft_forward_info *info,
-+                            unsigned char *ha)
- {
-       const struct net_device_path *path;
-       int i;
-+      memcpy(info->h_dest, ha, ETH_ALEN);
-+
-       for (i = 0; i < stack->num_paths; i++) {
-               path = &stack->path[i];
-               switch (path->type) {
-               case DEV_PATH_ETHERNET:
-                       info->indev = path->dev;
-+                      if (is_zero_ether_addr(info->h_source))
-+                              memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
-                       break;
--              case DEV_PATH_VLAN:
-               case DEV_PATH_BRIDGE:
-+                      if (is_zero_ether_addr(info->h_source))
-+                              memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
-+
-+                      info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
-+                      break;
-+              case DEV_PATH_VLAN:
-               default:
-                       info->indev = NULL;
-                       break;
-               }
-       }
-+      if (!info->outdev)
-+              info->outdev = info->indev;
- }
- static bool nft_flowtable_find_dev(const struct net_device *dev,
-@@ -114,14 +129,22 @@ static void nft_dev_forward_path(struct
-       const struct dst_entry *dst = route->tuple[dir].dst;
-       struct net_device_path_stack stack;
-       struct nft_forward_info info = {};
-+      unsigned char ha[ETH_ALEN];
--      if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
--              nft_dev_path_info(&stack, &info);
-+      if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
-+              nft_dev_path_info(&stack, &info, ha);
-       if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
-               return;
-       route->tuple[!dir].in.ifindex = info.indev->ifindex;
-+
-+      if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
-+              memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
-+              memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
-+              route->tuple[dir].out.ifindex = info.outdev->ifindex;
-+              route->tuple[dir].xmit_type = info.xmit_type;
-+      }
- }
- static int nft_flow_route(const struct nft_pktinfo *pkt,
diff --git a/target/linux/generic/pending-5.10/640-07-netfilter-flowtable-add-vlan-support.patch b/target/linux/generic/pending-5.10/640-07-netfilter-flowtable-add-vlan-support.patch
deleted file mode 100644 (file)
index fea1b59..0000000
+++ /dev/null
@@ -1,391 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Fri, 20 Nov 2020 13:49:20 +0100
-Subject: [PATCH] netfilter: flowtable: add vlan support
-
-Add the vlan id and protocol to the flow tuple to uniquely identify
-flows from the receive path. For the transmit path, dev_hard_header() on
-the vlan device push the headers. This patch includes support for two
-VLAN headers (QinQ) from the ingress path.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -95,6 +95,8 @@ enum flow_offload_xmit_type {
-       FLOW_OFFLOAD_XMIT_DIRECT,
- };
-+#define NF_FLOW_TABLE_ENCAP_MAX               2
-+
- struct flow_offload_tuple {
-       union {
-               struct in_addr          src_v4;
-@@ -113,13 +115,17 @@ struct flow_offload_tuple {
-       u8                              l3proto;
-       u8                              l4proto;
-+      struct {
-+              u16                     id;
-+              __be16                  proto;
-+      } encap[NF_FLOW_TABLE_ENCAP_MAX];
-       /* All members above are keys for lookups, see flow_offload_hash(). */
-       struct { }                      __hash;
--      u8                              dir:6,
--                                      xmit_type:2;
--
-+      u8                              dir:4,
-+                                      xmit_type:2,
-+                                      encap_num:2;
-       u16                             mtu;
-       union {
-               struct dst_entry        *dst_cache;
-@@ -174,6 +180,11 @@ struct nf_flow_route {
-               struct dst_entry                *dst;
-               struct {
-                       u32                     ifindex;
-+                      struct {
-+                              u16             id;
-+                              __be16          proto;
-+                      } encap[NF_FLOW_TABLE_ENCAP_MAX];
-+                      u8                      num_encaps;
-               } in;
-               struct {
-                       u32                     ifindex;
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -80,6 +80,7 @@ static int flow_offload_fill_route(struc
- {
-       struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
-       struct dst_entry *dst = route->tuple[dir].dst;
-+      int i, j = 0;
-       switch (flow_tuple->l3proto) {
-       case NFPROTO_IPV4:
-@@ -91,6 +92,12 @@ static int flow_offload_fill_route(struc
-       }
-       flow_tuple->iifidx = route->tuple[dir].in.ifindex;
-+      for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
-+              flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
-+              flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
-+              j++;
-+      }
-+      flow_tuple->encap_num = route->tuple[dir].in.num_encaps;
-       switch (route->tuple[dir].xmit_type) {
-       case FLOW_OFFLOAD_XMIT_DIRECT:
---- a/net/netfilter/nf_flow_table_ip.c
-+++ b/net/netfilter/nf_flow_table_ip.c
-@@ -159,17 +159,38 @@ static bool ip_has_options(unsigned int
-       return thoff != sizeof(struct iphdr);
- }
-+static void nf_flow_tuple_encap(struct sk_buff *skb,
-+                              struct flow_offload_tuple *tuple)
-+{
-+      int i = 0;
-+
-+      if (skb_vlan_tag_present(skb)) {
-+              tuple->encap[i].id = skb_vlan_tag_get(skb);
-+              tuple->encap[i].proto = skb->vlan_proto;
-+              i++;
-+      }
-+      if (skb->protocol == htons(ETH_P_8021Q)) {
-+              struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
-+
-+              tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
-+              tuple->encap[i].proto = skb->protocol;
-+      }
-+}
-+
- static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
-                           struct flow_offload_tuple *tuple)
- {
--      unsigned int thoff, hdrsize;
-+      unsigned int thoff, hdrsize, offset = 0;
-       struct flow_ports *ports;
-       struct iphdr *iph;
--      if (!pskb_may_pull(skb, sizeof(*iph)))
-+      if (skb->protocol == htons(ETH_P_8021Q))
-+              offset += VLAN_HLEN;
-+
-+      if (!pskb_may_pull(skb, sizeof(*iph) + offset))
-               return -1;
--      iph = ip_hdr(skb);
-+      iph = (struct iphdr *)(skb_network_header(skb) + offset);
-       thoff = iph->ihl * 4;
-       if (ip_is_fragment(iph) ||
-@@ -191,11 +212,11 @@ static int nf_flow_tuple_ip(struct sk_bu
-               return -1;
-       thoff = iph->ihl * 4;
--      if (!pskb_may_pull(skb, thoff + hdrsize))
-+      if (!pskb_may_pull(skb, thoff + hdrsize + offset))
-               return -1;
--      iph = ip_hdr(skb);
--      ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
-+      iph = (struct iphdr *)(skb_network_header(skb) + offset);
-+      ports = (struct flow_ports *)(skb_network_header(skb) + thoff + offset);
-       tuple->src_v4.s_addr    = iph->saddr;
-       tuple->dst_v4.s_addr    = iph->daddr;
-@@ -204,6 +225,7 @@ static int nf_flow_tuple_ip(struct sk_bu
-       tuple->l3proto          = AF_INET;
-       tuple->l4proto          = iph->protocol;
-       tuple->iifidx           = dev->ifindex;
-+      nf_flow_tuple_encap(skb, tuple);
-       return 0;
- }
-@@ -248,6 +270,40 @@ static unsigned int nf_flow_xmit_xfrm(st
-       return NF_STOLEN;
- }
-+static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto)
-+{
-+      if (skb->protocol == htons(ETH_P_8021Q)) {
-+              struct vlan_ethhdr *veth;
-+
-+              veth = (struct vlan_ethhdr *)skb_mac_header(skb);
-+              if (veth->h_vlan_encapsulated_proto == proto)
-+                      return true;
-+      }
-+
-+      return false;
-+}
-+
-+static void nf_flow_encap_pop(struct sk_buff *skb,
-+                            struct flow_offload_tuple_rhash *tuplehash)
-+{
-+      struct vlan_hdr *vlan_hdr;
-+      int i;
-+
-+      for (i = 0; i < tuplehash->tuple.encap_num; i++) {
-+              if (skb_vlan_tag_present(skb)) {
-+                      __vlan_hwaccel_clear_tag(skb);
-+                      continue;
-+              }
-+              if (skb->protocol == htons(ETH_P_8021Q)) {
-+                      vlan_hdr = (struct vlan_hdr *)skb->data;
-+                      __skb_pull(skb, VLAN_HLEN);
-+                      vlan_set_encap_proto(skb, vlan_hdr);
-+                      skb_reset_network_header(skb);
-+                      break;
-+              }
-+      }
-+}
-+
- static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
-                                      const struct flow_offload_tuple_rhash *tuplehash,
-                                      unsigned short type)
-@@ -276,13 +332,15 @@ nf_flow_offload_ip_hook(void *priv, stru
-       enum flow_offload_tuple_dir dir;
-       struct flow_offload *flow;
-       struct net_device *outdev;
-+      unsigned int thoff, mtu;
-       struct rtable *rt;
--      unsigned int thoff;
-       struct iphdr *iph;
-       __be32 nexthop;
-+      u32 offset = 0;
-       int ret;
--      if (skb->protocol != htons(ETH_P_IP))
-+      if (skb->protocol != htons(ETH_P_IP) &&
-+          !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IP)))
-               return NF_ACCEPT;
-       if (nf_flow_tuple_ip(skb, state->in, &tuple) < 0)
-@@ -295,14 +353,19 @@ nf_flow_offload_ip_hook(void *priv, stru
-       dir = tuplehash->tuple.dir;
-       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
--      if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
-+      mtu = flow->tuplehash[dir].tuple.mtu + offset;
-+      if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
-               return NF_ACCEPT;
--      if (skb_try_make_writable(skb, sizeof(*iph)))
-+      if (skb->protocol == htons(ETH_P_8021Q))
-+              offset += VLAN_HLEN;
-+
-+      if (skb_try_make_writable(skb, sizeof(*iph) + offset))
-               return NF_DROP;
--      thoff = ip_hdr(skb)->ihl * 4;
--      if (nf_flow_state_check(flow, ip_hdr(skb)->protocol, skb, thoff))
-+      iph = (struct iphdr *)(skb_network_header(skb) + offset);
-+      thoff = (iph->ihl * 4) + offset;
-+      if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
-               return NF_ACCEPT;
-       flow_offload_refresh(flow_table, flow);
-@@ -312,6 +375,9 @@ nf_flow_offload_ip_hook(void *priv, stru
-               return NF_ACCEPT;
-       }
-+      nf_flow_encap_pop(skb, tuplehash);
-+      thoff -= offset;
-+
-       if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
-               return NF_DROP;
-@@ -479,14 +545,17 @@ static int nf_flow_nat_ipv6(const struct
- static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
-                             struct flow_offload_tuple *tuple)
- {
--      unsigned int thoff, hdrsize;
-+      unsigned int thoff, hdrsize, offset = 0;
-       struct flow_ports *ports;
-       struct ipv6hdr *ip6h;
--      if (!pskb_may_pull(skb, sizeof(*ip6h)))
-+      if (skb->protocol == htons(ETH_P_8021Q))
-+              offset += VLAN_HLEN;
-+
-+      if (!pskb_may_pull(skb, sizeof(*ip6h) + offset))
-               return -1;
--      ip6h = ipv6_hdr(skb);
-+      ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
-       switch (ip6h->nexthdr) {
-       case IPPROTO_TCP:
-@@ -503,11 +572,11 @@ static int nf_flow_tuple_ipv6(struct sk_
-               return -1;
-       thoff = sizeof(*ip6h);
--      if (!pskb_may_pull(skb, thoff + hdrsize))
-+      if (!pskb_may_pull(skb, thoff + hdrsize + offset))
-               return -1;
--      ip6h = ipv6_hdr(skb);
--      ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
-+      ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
-+      ports = (struct flow_ports *)(skb_network_header(skb) + thoff + offset);
-       tuple->src_v6           = ip6h->saddr;
-       tuple->dst_v6           = ip6h->daddr;
-@@ -516,6 +585,7 @@ static int nf_flow_tuple_ipv6(struct sk_
-       tuple->l3proto          = AF_INET6;
-       tuple->l4proto          = ip6h->nexthdr;
-       tuple->iifidx           = dev->ifindex;
-+      nf_flow_tuple_encap(skb, tuple);
-       return 0;
- }
-@@ -533,9 +603,12 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       struct net_device *outdev;
-       struct ipv6hdr *ip6h;
-       struct rt6_info *rt;
-+      unsigned int mtu;
-+      u32 offset = 0;
-       int ret;
--      if (skb->protocol != htons(ETH_P_IPV6))
-+      if (skb->protocol != htons(ETH_P_IPV6) &&
-+          !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IPV6)))
-               return NF_ACCEPT;
-       if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0)
-@@ -548,11 +621,15 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       dir = tuplehash->tuple.dir;
-       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
--      if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
-+      mtu = flow->tuplehash[dir].tuple.mtu + offset;
-+      if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
-               return NF_ACCEPT;
--      if (nf_flow_state_check(flow, ipv6_hdr(skb)->nexthdr, skb,
--                              sizeof(*ip6h)))
-+      if (skb->protocol == htons(ETH_P_8021Q))
-+              offset += VLAN_HLEN;
-+
-+      ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
-+      if (nf_flow_state_check(flow, ip6h->nexthdr, skb, sizeof(*ip6h)))
-               return NF_ACCEPT;
-       flow_offload_refresh(flow_table, flow);
-@@ -562,6 +639,8 @@ nf_flow_offload_ipv6_hook(void *priv, st
-               return NF_ACCEPT;
-       }
-+      nf_flow_encap_pop(skb, tuplehash);
-+
-       if (skb_try_make_writable(skb, sizeof(*ip6h)))
-               return NF_DROP;
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -66,6 +66,11 @@ static int nft_dev_fill_forward_path(con
- struct nft_forward_info {
-       const struct net_device *indev;
-       const struct net_device *outdev;
-+      struct id {
-+              __u16   id;
-+              __be16  proto;
-+      } encap[NF_FLOW_TABLE_ENCAP_MAX];
-+      u8 num_encaps;
-       u8 h_source[ETH_ALEN];
-       u8 h_dest[ETH_ALEN];
-       enum flow_offload_xmit_type xmit_type;
-@@ -84,9 +89,23 @@ static void nft_dev_path_info(const stru
-               path = &stack->path[i];
-               switch (path->type) {
-               case DEV_PATH_ETHERNET:
-+              case DEV_PATH_VLAN:
-                       info->indev = path->dev;
-                       if (is_zero_ether_addr(info->h_source))
-                               memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
-+
-+                      if (path->type == DEV_PATH_ETHERNET)
-+                              break;
-+
-+                      /* DEV_PATH_VLAN */
-+                      if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
-+                              info->indev = NULL;
-+                              break;
-+                      }
-+                      info->outdev = path->dev;
-+                      info->encap[info->num_encaps].id = path->encap.id;
-+                      info->encap[info->num_encaps].proto = path->encap.proto;
-+                      info->num_encaps++;
-                       break;
-               case DEV_PATH_BRIDGE:
-                       if (is_zero_ether_addr(info->h_source))
-@@ -94,7 +113,6 @@ static void nft_dev_path_info(const stru
-                       info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
-                       break;
--              case DEV_PATH_VLAN:
-               default:
-                       info->indev = NULL;
-                       break;
-@@ -130,6 +148,7 @@ static void nft_dev_forward_path(struct
-       struct net_device_path_stack stack;
-       struct nft_forward_info info = {};
-       unsigned char ha[ETH_ALEN];
-+      int i;
-       if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
-               nft_dev_path_info(&stack, &info, ha);
-@@ -138,6 +157,11 @@ static void nft_dev_forward_path(struct
-               return;
-       route->tuple[!dir].in.ifindex = info.indev->ifindex;
-+      for (i = 0; i < info.num_encaps; i++) {
-+              route->tuple[!dir].in.encap[i].id = info.encap[i].id;
-+              route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
-+      }
-+      route->tuple[!dir].in.num_encaps = info.num_encaps;
-       if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
-               memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
diff --git a/target/linux/generic/pending-5.10/640-08-selftests-netfilter-flowtable-bridge-and-VLAN-suppor.patch b/target/linux/generic/pending-5.10/640-08-selftests-netfilter-flowtable-bridge-and-VLAN-suppor.patch
deleted file mode 100644 (file)
index 1ee015c..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Fri, 20 Nov 2020 13:49:21 +0100
-Subject: [PATCH] selftests: netfilter: flowtable bridge and VLAN support
-
-This patch adds two new tests to cover bridge and VLAN support:
-
-- Add a bridge device to the Router1 (nsr1) container and attach the
-  veth0 device to the bridge. Set the IP address to the bridge device
-  to exercise the bridge forwarding path.
-
-- Add VLAN encapsulation between to the bridge device in the Router1 and
-  one of the sender containers (ns1).
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/tools/testing/selftests/netfilter/nft_flowtable.sh
-+++ b/tools/testing/selftests/netfilter/nft_flowtable.sh
-@@ -371,6 +371,88 @@ else
-       ip netns exec nsr1 nft list ruleset
- fi
-+# Another test:
-+# Add bridge interface br0 to Router1, with NAT enabled.
-+ip -net nsr1 link add name br0 type bridge
-+ip -net nsr1 addr flush dev veth0
-+ip -net nsr1 link set up dev veth0
-+ip -net nsr1 link set veth0 master br0
-+ip -net nsr1 addr add 10.0.1.1/24 dev br0
-+ip -net nsr1 addr add dead:1::1/64 dev br0
-+ip -net nsr1 link set up dev br0
-+
-+ip netns exec nsr1 sysctl net.ipv4.conf.br0.forwarding=1 > /dev/null
-+
-+# br0 with NAT enabled.
-+ip netns exec nsr1 nft -f - <<EOF
-+flush table ip nat
-+table ip nat {
-+   chain prerouting {
-+      type nat hook prerouting priority 0; policy accept;
-+      meta iif "br0" ip daddr 10.6.6.6 tcp dport 1666 counter dnat ip to 10.0.2.99:12345
-+   }
-+
-+   chain postrouting {
-+      type nat hook postrouting priority 0; policy accept;
-+      meta oifname "veth1" counter masquerade
-+   }
-+}
-+EOF
-+
-+if test_tcp_forwarding_nat ns1 ns2; then
-+      echo "PASS: flow offloaded for ns1/ns2 with bridge NAT"
-+else
-+      echo "FAIL: flow offload for ns1/ns2 with bridge NAT" 1>&2
-+      ip netns exec nsr1 nft list ruleset
-+      ret=1
-+fi
-+
-+# Another test:
-+# Add bridge interface br0 to Router1, with NAT and VLAN.
-+ip -net nsr1 link set veth0 nomaster
-+ip -net nsr1 link set down dev veth0
-+ip -net nsr1 link add link veth0 name veth0.10 type vlan id 10
-+ip -net nsr1 link set up dev veth0
-+ip -net nsr1 link set up dev veth0.10
-+ip -net nsr1 link set veth0.10 master br0
-+
-+ip -net ns1 addr flush dev eth0
-+ip -net ns1 link add link eth0 name eth0.10 type vlan id 10
-+ip -net ns1 link set eth0 up
-+ip -net ns1 link set eth0.10 up
-+ip -net ns1 addr add 10.0.1.99/24 dev eth0.10
-+ip -net ns1 route add default via 10.0.1.1
-+ip -net ns1 addr add dead:1::99/64 dev eth0.10
-+
-+if test_tcp_forwarding_nat ns1 ns2; then
-+      echo "PASS: flow offloaded for ns1/ns2 with bridge NAT and VLAN"
-+else
-+      echo "FAIL: flow offload for ns1/ns2 with bridge NAT and VLAN" 1>&2
-+      ip netns exec nsr1 nft list ruleset
-+      ret=1
-+fi
-+
-+# restore test topology (remove bridge and VLAN)
-+ip -net nsr1 link set veth0 nomaster
-+ip -net nsr1 link set veth0 down
-+ip -net nsr1 link set veth0.10 down
-+ip -net nsr1 link delete veth0.10 type vlan
-+ip -net nsr1 link delete br0 type bridge
-+ip -net ns1 addr flush dev eth0.10
-+ip -net ns1 link set eth0.10 down
-+ip -net ns1 link set eth0 down
-+ip -net ns1 link delete eth0.10 type vlan
-+
-+# restore address in ns1 and nsr1
-+ip -net ns1 link set eth0 up
-+ip -net ns1 addr add 10.0.1.99/24 dev eth0
-+ip -net ns1 route add default via 10.0.1.1
-+ip -net ns1 addr add dead:1::99/64 dev eth0
-+ip -net ns1 route add default via dead:1::1
-+ip -net nsr1 addr add 10.0.1.1/24 dev veth0
-+ip -net nsr1 addr add dead:1::1/64 dev veth0
-+ip -net nsr1 link set up dev veth0
-+
- KEY_SHA="0x"$(ps -xaf | sha1sum | cut -d " " -f 1)
- KEY_AES="0x"$(ps -xaf | md5sum | cut -d " " -f 1)
- SPI1=$RANDOM
diff --git a/target/linux/generic/pending-5.10/640-09-net-bridge-resolve-VLAN-tag-actions-in-forwarding-pa.patch b/target/linux/generic/pending-5.10/640-09-net-bridge-resolve-VLAN-tag-actions-in-forwarding-pa.patch
deleted file mode 100644 (file)
index 8f8d58b..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 7 Dec 2020 20:31:45 +0100
-Subject: [PATCH] net: bridge: resolve VLAN tag actions in forwarding
- path for bridge devices
-
-Depending on the VLAN settings of the bridge and the port, the bridge can
-either add or remove a tag. When vlan filtering is enabled, the fdb lookup
-also needs to know the VLAN tag/proto for the destination address
-To provide this, keep track of the stack of VLAN tags for the path in the
-lookup context
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -841,10 +841,20 @@ struct net_device_path {
-                       u16             id;
-                       __be16          proto;
-               } encap;
-+              struct {
-+                      enum {
-+                              DEV_PATH_BR_VLAN_KEEP,
-+                              DEV_PATH_BR_VLAN_TAG,
-+                              DEV_PATH_BR_VLAN_UNTAG,
-+                      }               vlan_mode;
-+                      u16             vlan_id;
-+                      __be16          vlan_proto;
-+              } bridge;
-       };
- };
- #define NET_DEVICE_PATH_STACK_MAX     5
-+#define NET_DEVICE_PATH_VLAN_MAX      2
- struct net_device_path_stack {
-       int                     num_paths;
-@@ -854,6 +864,12 @@ struct net_device_path_stack {
- struct net_device_path_ctx {
-       const struct net_device *dev;
-       const u8                *daddr;
-+
-+      int                     num_vlans;
-+      struct {
-+              u16             id;
-+              __be16          proto;
-+      } vlan[NET_DEVICE_PATH_VLAN_MAX];
- };
- enum tc_setup_type {
---- a/net/8021q/vlan_dev.c
-+++ b/net/8021q/vlan_dev.c
-@@ -777,6 +777,12 @@ static int vlan_dev_fill_forward_path(st
-       path->encap.proto = vlan->vlan_proto;
-       path->dev = ctx->dev;
-       ctx->dev = vlan->real_dev;
-+      if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
-+              return -ENOSPC;
-+
-+      ctx->vlan[ctx->num_vlans].id = vlan->vlan_id;
-+      ctx->vlan[ctx->num_vlans].proto = vlan->vlan_proto;
-+      ctx->num_vlans++;
-       return 0;
- }
---- a/net/bridge/br_device.c
-+++ b/net/bridge/br_device.c
-@@ -409,7 +409,10 @@ static int br_fill_forward_path(struct n
-               return -1;
-       br = netdev_priv(ctx->dev);
--      f = br_fdb_find_rcu(br, ctx->daddr, 0);
-+
-+      br_vlan_fill_forward_path_pvid(br, ctx, path);
-+
-+      f = br_fdb_find_rcu(br, ctx->daddr, path->bridge.vlan_id);
-       if (!f || !f->dst)
-               return -1;
-@@ -417,10 +420,28 @@ static int br_fill_forward_path(struct n
-       if (!dst)
-               return -1;
-+      if (br_vlan_fill_forward_path_mode(br, dst, path))
-+              return -1;
-+
-       path->type = DEV_PATH_BRIDGE;
-       path->dev = dst->br->dev;
-       ctx->dev = dst->dev;
-+      switch (path->bridge.vlan_mode) {
-+      case DEV_PATH_BR_VLAN_TAG:
-+              if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
-+                      return -ENOSPC;
-+              ctx->vlan[ctx->num_vlans].id = path->bridge.vlan_id;
-+              ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto;
-+              ctx->num_vlans++;
-+              break;
-+      case DEV_PATH_BR_VLAN_UNTAG:
-+              ctx->num_vlans--;
-+              break;
-+      case DEV_PATH_BR_VLAN_KEEP:
-+              break;
-+      }
-+
-       return 0;
- }
---- a/net/bridge/br_private.h
-+++ b/net/bridge/br_private.h
-@@ -1095,6 +1095,13 @@ void br_vlan_notify(const struct net_bri
- bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
-                            const struct net_bridge_vlan *range_end);
-+void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
-+                                  struct net_device_path_ctx *ctx,
-+                                  struct net_device_path *path);
-+int br_vlan_fill_forward_path_mode(struct net_bridge *br,
-+                                 struct net_bridge_port *dst,
-+                                 struct net_device_path *path);
-+
- static inline struct net_bridge_vlan_group *br_vlan_group(
-                                       const struct net_bridge *br)
- {
-@@ -1252,6 +1259,19 @@ static inline int nbp_get_num_vlan_infos
- {
-       return 0;
- }
-+
-+static inline void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
-+                                                struct net_device_path_ctx *ctx,
-+                                                struct net_device_path *path)
-+{
-+}
-+
-+static inline int br_vlan_fill_forward_path_mode(struct net_bridge *br,
-+                                               struct net_bridge_port *dst,
-+                                               struct net_device_path *path)
-+{
-+      return 0;
-+}
- static inline struct net_bridge_vlan_group *br_vlan_group(
-                                       const struct net_bridge *br)
---- a/net/bridge/br_vlan.c
-+++ b/net/bridge/br_vlan.c
-@@ -1327,6 +1327,59 @@ int br_vlan_get_pvid_rcu(const struct ne
- }
- EXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
-+void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
-+                                  struct net_device_path_ctx *ctx,
-+                                  struct net_device_path *path)
-+{
-+      struct net_bridge_vlan_group *vg;
-+      int idx = ctx->num_vlans - 1;
-+      u16 vid;
-+
-+      path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
-+
-+      if (!br_opt_get(br, BROPT_VLAN_ENABLED))
-+              return;
-+
-+      vg = br_vlan_group(br);
-+
-+      if (idx >= 0 &&
-+          ctx->vlan[idx].proto == br->vlan_proto) {
-+              vid = ctx->vlan[idx].id;
-+      } else {
-+              path->bridge.vlan_mode = DEV_PATH_BR_VLAN_TAG;
-+              vid = br_get_pvid(vg);
-+      }
-+
-+      path->bridge.vlan_id = vid;
-+      path->bridge.vlan_proto = br->vlan_proto;
-+}
-+
-+int br_vlan_fill_forward_path_mode(struct net_bridge *br,
-+                                 struct net_bridge_port *dst,
-+                                 struct net_device_path *path)
-+{
-+      struct net_bridge_vlan_group *vg;
-+      struct net_bridge_vlan *v;
-+
-+      if (!br_opt_get(br, BROPT_VLAN_ENABLED))
-+              return 0;
-+
-+      vg = nbp_vlan_group_rcu(dst);
-+      v = br_vlan_find(vg, path->bridge.vlan_id);
-+      if (!v || !br_vlan_should_use(v))
-+              return -EINVAL;
-+
-+      if (!(v->flags & BRIDGE_VLAN_INFO_UNTAGGED))
-+              return 0;
-+
-+      if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
-+              path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
-+      else
-+              path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;
-+
-+      return 0;
-+}
-+
- int br_vlan_get_info(const struct net_device *dev, u16 vid,
-                    struct bridge_vlan_info *p_vinfo)
- {
diff --git a/target/linux/generic/pending-5.10/640-10-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch b/target/linux/generic/pending-5.10/640-10-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch
deleted file mode 100644 (file)
index 86fd6bf..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Sun, 24 Jan 2021 18:01:34 +0100
-Subject: [PATCH] netfilter: nft_flow_offload: add bridge vlan filtering
- support
-
-Add the vlan tag based when PVID is set on.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -111,6 +111,18 @@ static void nft_dev_path_info(const stru
-                       if (is_zero_ether_addr(info->h_source))
-                               memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
-+                      switch (path->bridge.vlan_mode) {
-+                      case DEV_PATH_BR_VLAN_TAG:
-+                              info->encap[info->num_encaps].id = path->bridge.vlan_id;
-+                              info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
-+                              info->num_encaps++;
-+                              break;
-+                      case DEV_PATH_BR_VLAN_UNTAG:
-+                              info->num_encaps--;
-+                              break;
-+                      case DEV_PATH_BR_VLAN_KEEP:
-+                              break;
-+                      }
-                       info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
-                       break;
-               default:
diff --git a/target/linux/generic/pending-5.10/640-11-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch b/target/linux/generic/pending-5.10/640-11-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch
deleted file mode 100644 (file)
index 88c89b2..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Tue, 2 Mar 2021 21:45:16 +0100
-Subject: [PATCH] net: ppp: resolve forwarding path for bridge pppoe
- devices
-
-Pass on the PPPoE session ID and the real device.
----
-
---- a/drivers/net/ppp/ppp_generic.c
-+++ b/drivers/net/ppp/ppp_generic.c
-@@ -1450,12 +1450,34 @@ static void ppp_dev_priv_destructor(stru
-               ppp_destroy_interface(ppp);
- }
-+static int ppp_fill_forward_path(struct net_device_path_ctx *ctx,
-+                               struct net_device_path *path)
-+{
-+      struct ppp *ppp = netdev_priv(path->dev);
-+      struct ppp_channel *chan;
-+      struct channel *pch;
-+
-+      if (ppp->flags & SC_MULTILINK)
-+              return -EOPNOTSUPP;
-+
-+      if (list_empty(&ppp->channels))
-+              return -ENODEV;
-+
-+      pch = list_first_entry(&ppp->channels, struct channel, clist);
-+      chan = pch->chan;
-+      if (!chan->ops->fill_forward_path)
-+              return -EOPNOTSUPP;
-+
-+      return chan->ops->fill_forward_path(ctx, path, chan);
-+}
-+
- static const struct net_device_ops ppp_netdev_ops = {
-       .ndo_init        = ppp_dev_init,
-       .ndo_uninit      = ppp_dev_uninit,
-       .ndo_start_xmit  = ppp_start_xmit,
-       .ndo_do_ioctl    = ppp_net_ioctl,
-       .ndo_get_stats64 = ppp_get_stats64,
-+      .ndo_fill_forward_path = ppp_fill_forward_path,
- };
- static struct device_type ppp_type = {
---- a/drivers/net/ppp/pppoe.c
-+++ b/drivers/net/ppp/pppoe.c
-@@ -972,8 +972,30 @@ static int pppoe_xmit(struct ppp_channel
-       return __pppoe_xmit(sk, skb);
- }
-+static int pppoe_fill_forward_path(struct net_device_path_ctx *ctx,
-+                                 struct net_device_path *path,
-+                                 const struct ppp_channel *chan)
-+{
-+      struct sock *sk = (struct sock *)chan->private;
-+      struct pppox_sock *po = pppox_sk(sk);
-+      struct net_device *dev = po->pppoe_dev;
-+
-+      if (sock_flag(sk, SOCK_DEAD) ||
-+          !(sk->sk_state & PPPOX_CONNECTED) || !dev)
-+              return -1;
-+
-+      path->type = DEV_PATH_PPPOE;
-+      path->encap.proto = htons(ETH_P_PPP_SES);
-+      path->encap.id = be16_to_cpu(po->num);
-+      path->dev = ctx->dev;
-+      ctx->dev = dev;
-+
-+      return 0;
-+}
-+
- static const struct ppp_channel_ops pppoe_chan_ops = {
-       .start_xmit = pppoe_xmit,
-+      .fill_forward_path = pppoe_fill_forward_path,
- };
- static int pppoe_recvmsg(struct socket *sock, struct msghdr *m,
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -831,6 +831,7 @@ enum net_device_path_type {
-       DEV_PATH_ETHERNET = 0,
-       DEV_PATH_VLAN,
-       DEV_PATH_BRIDGE,
-+      DEV_PATH_PPPOE,
- };
- struct net_device_path {
---- a/include/linux/ppp_channel.h
-+++ b/include/linux/ppp_channel.h
-@@ -28,6 +28,9 @@ struct ppp_channel_ops {
-       int     (*start_xmit)(struct ppp_channel *, struct sk_buff *);
-       /* Handle an ioctl call that has come in via /dev/ppp. */
-       int     (*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
-+      int     (*fill_forward_path)(struct net_device_path_ctx *,
-+                                   struct net_device_path *,
-+                                   const struct ppp_channel *);
- };
- struct ppp_channel {
diff --git a/target/linux/generic/pending-5.10/640-12-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch b/target/linux/generic/pending-5.10/640-12-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch
deleted file mode 100644 (file)
index 0b85742..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 4 Mar 2021 23:19:06 +0100
-Subject: [PATCH] net: dsa: resolve forwarding path for dsa slave ports
-
-Add .ndo_fill_forward_path for dsa slave port devices
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -832,6 +832,7 @@ enum net_device_path_type {
-       DEV_PATH_VLAN,
-       DEV_PATH_BRIDGE,
-       DEV_PATH_PPPOE,
-+      DEV_PATH_DSA,
- };
- struct net_device_path {
-@@ -851,6 +852,10 @@ struct net_device_path {
-                       u16             vlan_id;
-                       __be16          vlan_proto;
-               } bridge;
-+              struct {
-+                      int port;
-+                      u16 proto;
-+              } dsa;
-       };
- };
---- a/net/dsa/slave.c
-+++ b/net/dsa/slave.c
-@@ -1617,6 +1617,21 @@ static struct devlink_port *dsa_slave_ge
-       return dp->ds->devlink ? &dp->devlink_port : NULL;
- }
-+static int dsa_slave_fill_forward_path(struct net_device_path_ctx *ctx,
-+                                     struct net_device_path *path)
-+{
-+      struct dsa_port *dp = dsa_slave_to_port(ctx->dev);
-+      struct dsa_port *cpu_dp = dp->cpu_dp;
-+
-+      path->dev = ctx->dev;
-+      path->type = DEV_PATH_DSA;
-+      path->dsa.proto = cpu_dp->tag_ops->proto;
-+      path->dsa.port = dp->index;
-+      ctx->dev = cpu_dp->master;
-+
-+      return 0;
-+}
-+
- static const struct net_device_ops dsa_slave_netdev_ops = {
-       .ndo_open               = dsa_slave_open,
-       .ndo_stop               = dsa_slave_close,
-@@ -1642,6 +1657,7 @@ static const struct net_device_ops dsa_s
-       .ndo_vlan_rx_kill_vid   = dsa_slave_vlan_rx_kill_vid,
-       .ndo_get_devlink_port   = dsa_slave_get_devlink_port,
-       .ndo_change_mtu         = dsa_slave_change_mtu,
-+      .ndo_fill_forward_path  = dsa_slave_fill_forward_path,
- };
- static struct device_type dsa_type = {
diff --git a/target/linux/generic/pending-5.10/640-13-netfilter-flowtable-add-pppoe-support.patch b/target/linux/generic/pending-5.10/640-13-netfilter-flowtable-add-pppoe-support.patch
deleted file mode 100644 (file)
index 524f466..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Mon, 1 Mar 2021 23:52:49 +0100
-Subject: [PATCH] netfilter: flowtable: add pppoe support
-
----
-
---- a/drivers/net/ppp/ppp_generic.c
-+++ b/drivers/net/ppp/ppp_generic.c
-@@ -1453,7 +1453,7 @@ static void ppp_dev_priv_destructor(stru
- static int ppp_fill_forward_path(struct net_device_path_ctx *ctx,
-                                struct net_device_path *path)
- {
--      struct ppp *ppp = netdev_priv(path->dev);
-+      struct ppp *ppp = netdev_priv(ctx->dev);
-       struct ppp_channel *chan;
-       struct channel *pch;
---- a/drivers/net/ppp/pppoe.c
-+++ b/drivers/net/ppp/pppoe.c
-@@ -987,6 +987,7 @@ static int pppoe_fill_forward_path(struc
-       path->type = DEV_PATH_PPPOE;
-       path->encap.proto = htons(ETH_P_PPP_SES);
-       path->encap.id = be16_to_cpu(po->num);
-+      memcpy(path->encap.h_dest, po->pppoe_pa.remote, ETH_ALEN);
-       path->dev = ctx->dev;
-       ctx->dev = dev;
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -842,6 +842,7 @@ struct net_device_path {
-               struct {
-                       u16             id;
-                       __be16          proto;
-+                      u8              h_dest[ETH_ALEN];
-               } encap;
-               struct {
-                       enum {
---- a/net/netfilter/nf_flow_table_ip.c
-+++ b/net/netfilter/nf_flow_table_ip.c
-@@ -7,6 +7,9 @@
- #include <linux/ip.h>
- #include <linux/ipv6.h>
- #include <linux/netdevice.h>
-+#include <linux/if_ether.h>
-+#include <linux/if_pppox.h>
-+#include <linux/ppp_defs.h>
- #include <net/ip.h>
- #include <net/ipv6.h>
- #include <net/ip6_route.h>
-@@ -162,6 +165,8 @@ static bool ip_has_options(unsigned int
- static void nf_flow_tuple_encap(struct sk_buff *skb,
-                               struct flow_offload_tuple *tuple)
- {
-+      struct vlan_ethhdr *veth;
-+      struct pppoe_hdr *phdr;
-       int i = 0;
-       if (skb_vlan_tag_present(skb)) {
-@@ -169,23 +174,35 @@ static void nf_flow_tuple_encap(struct s
-               tuple->encap[i].proto = skb->vlan_proto;
-               i++;
-       }
--      if (skb->protocol == htons(ETH_P_8021Q)) {
--              struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
--
-+      switch (skb->protocol) {
-+      case htons(ETH_P_8021Q):
-+              veth = (struct vlan_ethhdr *)skb_mac_header(skb);
-               tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
-               tuple->encap[i].proto = skb->protocol;
-+              break;
-+      case htons(ETH_P_PPP_SES):
-+              phdr = (struct pppoe_hdr *)skb_mac_header(skb);
-+              tuple->encap[i].id = ntohs(phdr->sid);
-+              tuple->encap[i].proto = skb->protocol;
-+              break;
-       }
- }
- static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
--                          struct flow_offload_tuple *tuple)
-+                          struct flow_offload_tuple *tuple, u32 *nhoff)
- {
-       unsigned int thoff, hdrsize, offset = 0;
-       struct flow_ports *ports;
-       struct iphdr *iph;
--      if (skb->protocol == htons(ETH_P_8021Q))
-+      switch (skb->protocol) {
-+      case htons(ETH_P_8021Q):
-               offset += VLAN_HLEN;
-+              break;
-+      case htons(ETH_P_PPP_SES):
-+              offset += PPPOE_SES_HLEN;
-+              break;
-+      }
-       if (!pskb_may_pull(skb, sizeof(*iph) + offset))
-               return -1;
-@@ -226,6 +243,7 @@ static int nf_flow_tuple_ip(struct sk_bu
-       tuple->l4proto          = iph->protocol;
-       tuple->iifidx           = dev->ifindex;
-       nf_flow_tuple_encap(skb, tuple);
-+      *nhoff = offset;
-       return 0;
- }
-@@ -270,14 +288,36 @@ static unsigned int nf_flow_xmit_xfrm(st
-       return NF_STOLEN;
- }
-+static inline __be16 nf_flow_pppoe_proto(const struct sk_buff *skb)
-+{
-+      __be16 proto;
-+
-+      proto = *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
-+                           sizeof(struct pppoe_hdr)));
-+      switch (proto) {
-+      case htons(PPP_IP):
-+              return htons(ETH_P_IP);
-+      case htons(PPP_IPV6):
-+              return htons(ETH_P_IPV6);
-+      }
-+
-+      return 0;
-+}
-+
- static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto)
- {
--      if (skb->protocol == htons(ETH_P_8021Q)) {
--              struct vlan_ethhdr *veth;
-+      struct vlan_ethhdr *veth;
-+      switch (skb->protocol) {
-+      case htons(ETH_P_8021Q):
-               veth = (struct vlan_ethhdr *)skb_mac_header(skb);
-               if (veth->h_vlan_encapsulated_proto == proto)
-                       return true;
-+              break;
-+      case htons(ETH_P_PPP_SES):
-+              if (nf_flow_pppoe_proto(skb) == proto)
-+                      return true;
-+              break;
-       }
-       return false;
-@@ -294,12 +334,18 @@ static void nf_flow_encap_pop(struct sk_
-                       __vlan_hwaccel_clear_tag(skb);
-                       continue;
-               }
--              if (skb->protocol == htons(ETH_P_8021Q)) {
-+              switch (skb->protocol) {
-+              case htons(ETH_P_8021Q):
-                       vlan_hdr = (struct vlan_hdr *)skb->data;
-                       __skb_pull(skb, VLAN_HLEN);
-                       vlan_set_encap_proto(skb, vlan_hdr);
-                       skb_reset_network_header(skb);
-                       break;
-+              case htons(ETH_P_PPP_SES):
-+                      skb->protocol = nf_flow_pppoe_proto(skb);
-+                      skb_pull(skb, PPPOE_SES_HLEN);
-+                      skb_reset_network_header(skb);
-+                      break;
-               }
-       }
- }
-@@ -343,7 +389,7 @@ nf_flow_offload_ip_hook(void *priv, stru
-           !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IP)))
-               return NF_ACCEPT;
--      if (nf_flow_tuple_ip(skb, state->in, &tuple) < 0)
-+      if (nf_flow_tuple_ip(skb, state->in, &tuple, &offset) < 0)
-               return NF_ACCEPT;
-       tuplehash = flow_offload_lookup(flow_table, &tuple);
-@@ -357,9 +403,6 @@ nf_flow_offload_ip_hook(void *priv, stru
-       if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
-               return NF_ACCEPT;
--      if (skb->protocol == htons(ETH_P_8021Q))
--              offset += VLAN_HLEN;
--
-       if (skb_try_make_writable(skb, sizeof(*iph) + offset))
-               return NF_DROP;
-@@ -543,14 +586,20 @@ static int nf_flow_nat_ipv6(const struct
- }
- static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
--                            struct flow_offload_tuple *tuple)
-+                            struct flow_offload_tuple *tuple, u32 *nhoff)
- {
-       unsigned int thoff, hdrsize, offset = 0;
-       struct flow_ports *ports;
-       struct ipv6hdr *ip6h;
--      if (skb->protocol == htons(ETH_P_8021Q))
-+      switch (skb->protocol) {
-+      case htons(ETH_P_8021Q):
-               offset += VLAN_HLEN;
-+              break;
-+      case htons(ETH_P_PPP_SES):
-+              offset += PPPOE_SES_HLEN;
-+              break;
-+      }
-       if (!pskb_may_pull(skb, sizeof(*ip6h) + offset))
-               return -1;
-@@ -586,6 +635,7 @@ static int nf_flow_tuple_ipv6(struct sk_
-       tuple->l4proto          = ip6h->nexthdr;
-       tuple->iifidx           = dev->ifindex;
-       nf_flow_tuple_encap(skb, tuple);
-+      *nhoff = offset;
-       return 0;
- }
-@@ -611,7 +661,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
-           !nf_flow_skb_encap_protocol(skb, htons(ETH_P_IPV6)))
-               return NF_ACCEPT;
--      if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0)
-+      if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &offset) < 0)
-               return NF_ACCEPT;
-       tuplehash = flow_offload_lookup(flow_table, &tuple);
-@@ -625,9 +675,6 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
-               return NF_ACCEPT;
--      if (skb->protocol == htons(ETH_P_8021Q))
--              offset += VLAN_HLEN;
--
-       ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
-       if (nf_flow_state_check(flow, ip6h->nexthdr, skb, sizeof(*ip6h)))
-               return NF_ACCEPT;
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -90,6 +90,7 @@ static void nft_dev_path_info(const stru
-               switch (path->type) {
-               case DEV_PATH_ETHERNET:
-               case DEV_PATH_VLAN:
-+              case DEV_PATH_PPPOE:
-                       info->indev = path->dev;
-                       if (is_zero_ether_addr(info->h_source))
-                               memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
-@@ -97,7 +98,7 @@ static void nft_dev_path_info(const stru
-                       if (path->type == DEV_PATH_ETHERNET)
-                               break;
--                      /* DEV_PATH_VLAN */
-+                      /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
-                       if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
-                               info->indev = NULL;
-                               break;
-@@ -106,6 +107,8 @@ static void nft_dev_path_info(const stru
-                       info->encap[info->num_encaps].id = path->encap.id;
-                       info->encap[info->num_encaps].proto = path->encap.proto;
-                       info->num_encaps++;
-+                      if (path->type == DEV_PATH_PPPOE)
-+                              memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN);
-                       break;
-               case DEV_PATH_BRIDGE:
-                       if (is_zero_ether_addr(info->h_source))
diff --git a/target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-dsa-support.patch b/target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-dsa-support.patch
deleted file mode 100644 (file)
index 7ff5dad..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Thu, 4 Mar 2021 19:22:55 +0100
-Subject: [PATCH] netfilter: nft_flow_offload: add dsa support
-
-Replace the master ethernet device by the dsa slave port.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -89,6 +89,7 @@ static void nft_dev_path_info(const stru
-               path = &stack->path[i];
-               switch (path->type) {
-               case DEV_PATH_ETHERNET:
-+              case DEV_PATH_DSA:
-               case DEV_PATH_VLAN:
-               case DEV_PATH_PPPOE:
-                       info->indev = path->dev;
-@@ -97,6 +98,10 @@ static void nft_dev_path_info(const stru
-                       if (path->type == DEV_PATH_ETHERNET)
-                               break;
-+                      if (path->type == DEV_PATH_DSA) {
-+                              i = stack->num_paths;
-+                              break;
-+                      }
-                       /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
-                       if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
diff --git a/target/linux/generic/pending-5.10/640-15-netfilter-flowtable-add-offload-support-for-xmit-pat.patch b/target/linux/generic/pending-5.10/640-15-netfilter-flowtable-add-offload-support-for-xmit-pat.patch
deleted file mode 100644 (file)
index 696ea03..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Mon, 7 Dec 2020 20:31:44 +0100
-Subject: [PATCH] netfilter: flowtable: add offload support for xmit path
- types
-
-When the flow tuple xmit_type is set to FLOW_OFFLOAD_XMIT_DIRECT, the
-dst_cache pointer is not valid, and the h_source/h_dest/ifidx out fields
-need to be used.
-
-This patch also adds the FLOW_ACTION_VLAN_PUSH action to pass the VLAN
-tag to the driver.
----
-
---- a/net/netfilter/nf_flow_table_offload.c
-+++ b/net/netfilter/nf_flow_table_offload.c
-@@ -175,28 +175,45 @@ static int flow_offload_eth_src(struct n
-                               enum flow_offload_tuple_dir dir,
-                               struct nf_flow_rule *flow_rule)
- {
--      const struct flow_offload_tuple *tuple = &flow->tuplehash[!dir].tuple;
-       struct flow_action_entry *entry0 = flow_action_entry_next(flow_rule);
-       struct flow_action_entry *entry1 = flow_action_entry_next(flow_rule);
--      struct net_device *dev;
-+      const struct flow_offload_tuple *other_tuple, *this_tuple;
-+      struct net_device *dev = NULL;
-+      const unsigned char *addr;
-       u32 mask, val;
-       u16 val16;
--      dev = dev_get_by_index(net, tuple->iifidx);
--      if (!dev)
--              return -ENOENT;
-+      this_tuple = &flow->tuplehash[dir].tuple;
-+
-+      switch (this_tuple->xmit_type) {
-+      case FLOW_OFFLOAD_XMIT_DIRECT:
-+              addr = this_tuple->out.h_source;
-+              break;
-+      case FLOW_OFFLOAD_XMIT_NEIGH:
-+              other_tuple = &flow->tuplehash[!dir].tuple;
-+              dev = dev_get_by_index(net, other_tuple->iifidx);
-+              if (!dev)
-+                      return -ENOENT;
-+
-+              addr = dev->dev_addr;
-+              break;
-+      default:
-+              return -EOPNOTSUPP;
-+      }
-       mask = ~0xffff0000;
--      memcpy(&val16, dev->dev_addr, 2);
-+      memcpy(&val16, addr, 2);
-       val = val16 << 16;
-       flow_offload_mangle(entry0, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4,
-                           &val, &mask);
-       mask = ~0xffffffff;
--      memcpy(&val, dev->dev_addr + 2, 4);
-+      memcpy(&val, addr + 2, 4);
-       flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 8,
-                           &val, &mask);
--      dev_put(dev);
-+
-+      if (dev)
-+              dev_put(dev);
-       return 0;
- }
-@@ -208,27 +225,40 @@ static int flow_offload_eth_dst(struct n
- {
-       struct flow_action_entry *entry0 = flow_action_entry_next(flow_rule);
-       struct flow_action_entry *entry1 = flow_action_entry_next(flow_rule);
--      const void *daddr = &flow->tuplehash[!dir].tuple.src_v4;
-+      const struct flow_offload_tuple *other_tuple, *this_tuple;
-       const struct dst_entry *dst_cache;
-       unsigned char ha[ETH_ALEN];
-       struct neighbour *n;
-+      const void *daddr;
-       u32 mask, val;
-       u8 nud_state;
-       u16 val16;
--      dst_cache = flow->tuplehash[dir].tuple.dst_cache;
--      n = dst_neigh_lookup(dst_cache, daddr);
--      if (!n)
--              return -ENOENT;
--
--      read_lock_bh(&n->lock);
--      nud_state = n->nud_state;
--      ether_addr_copy(ha, n->ha);
--      read_unlock_bh(&n->lock);
-+      this_tuple = &flow->tuplehash[dir].tuple;
--      if (!(nud_state & NUD_VALID)) {
-+      switch (this_tuple->xmit_type) {
-+      case FLOW_OFFLOAD_XMIT_DIRECT:
-+              ether_addr_copy(ha, this_tuple->out.h_dest);
-+              break;
-+      case FLOW_OFFLOAD_XMIT_NEIGH:
-+              other_tuple = &flow->tuplehash[!dir].tuple;
-+              daddr = &other_tuple->src_v4;
-+              dst_cache = this_tuple->dst_cache;
-+              n = dst_neigh_lookup(dst_cache, daddr);
-+              if (!n)
-+                      return -ENOENT;
-+
-+              read_lock_bh(&n->lock);
-+              nud_state = n->nud_state;
-+              ether_addr_copy(ha, n->ha);
-+              read_unlock_bh(&n->lock);
-               neigh_release(n);
--              return -ENOENT;
-+
-+              if (!(nud_state & NUD_VALID))
-+                      return -ENOENT;
-+              break;
-+      default:
-+              return -EOPNOTSUPP;
-       }
-       mask = ~0xffffffff;
-@@ -241,7 +271,6 @@ static int flow_offload_eth_dst(struct n
-       val = val16;
-       flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4,
-                           &val, &mask);
--      neigh_release(n);
-       return 0;
- }
-@@ -463,27 +492,52 @@ static void flow_offload_ipv4_checksum(s
-       }
- }
--static void flow_offload_redirect(const struct flow_offload *flow,
-+static void flow_offload_redirect(struct net *net,
-+                                const struct flow_offload *flow,
-                                 enum flow_offload_tuple_dir dir,
-                                 struct nf_flow_rule *flow_rule)
- {
--      struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
--      struct rtable *rt;
-+      const struct flow_offload_tuple *this_tuple, *other_tuple;
-+      struct flow_action_entry *entry;
-+      struct net_device *dev;
-+      int ifindex;
-+
-+      this_tuple = &flow->tuplehash[dir].tuple;
-+      switch (this_tuple->xmit_type) {
-+      case FLOW_OFFLOAD_XMIT_DIRECT:
-+              this_tuple = &flow->tuplehash[dir].tuple;
-+              ifindex = this_tuple->out.ifidx;
-+              break;
-+      case FLOW_OFFLOAD_XMIT_NEIGH:
-+              other_tuple = &flow->tuplehash[!dir].tuple;
-+              ifindex = other_tuple->iifidx;
-+              break;
-+      default:
-+              return;
-+      }
--      rt = (struct rtable *)flow->tuplehash[dir].tuple.dst_cache;
-+      dev = dev_get_by_index(net, ifindex);
-+      if (!dev)
-+              return;
-+
-+      entry = flow_action_entry_next(flow_rule);
-       entry->id = FLOW_ACTION_REDIRECT;
--      entry->dev = rt->dst.dev;
--      dev_hold(rt->dst.dev);
-+      entry->dev = dev;
- }
- static void flow_offload_encap_tunnel(const struct flow_offload *flow,
-                                     enum flow_offload_tuple_dir dir,
-                                     struct nf_flow_rule *flow_rule)
- {
-+      const struct flow_offload_tuple *this_tuple;
-       struct flow_action_entry *entry;
-       struct dst_entry *dst;
--      dst = flow->tuplehash[dir].tuple.dst_cache;
-+      this_tuple = &flow->tuplehash[dir].tuple;
-+      if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
-+              return;
-+
-+      dst = this_tuple->dst_cache;
-       if (dst && dst->lwtstate) {
-               struct ip_tunnel_info *tun_info;
-@@ -500,10 +554,15 @@ static void flow_offload_decap_tunnel(co
-                                     enum flow_offload_tuple_dir dir,
-                                     struct nf_flow_rule *flow_rule)
- {
-+      const struct flow_offload_tuple *other_tuple;
-       struct flow_action_entry *entry;
-       struct dst_entry *dst;
--      dst = flow->tuplehash[!dir].tuple.dst_cache;
-+      other_tuple = &flow->tuplehash[!dir].tuple;
-+      if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
-+              return;
-+
-+      dst = other_tuple->dst_cache;
-       if (dst && dst->lwtstate) {
-               struct ip_tunnel_info *tun_info;
-@@ -515,10 +574,14 @@ static void flow_offload_decap_tunnel(co
-       }
- }
--int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
--                          enum flow_offload_tuple_dir dir,
--                          struct nf_flow_rule *flow_rule)
-+static int
-+nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
-+                        enum flow_offload_tuple_dir dir,
-+                        struct nf_flow_rule *flow_rule)
- {
-+      const struct flow_offload_tuple *other_tuple;
-+      int i;
-+
-       flow_offload_decap_tunnel(flow, dir, flow_rule);
-       flow_offload_encap_tunnel(flow, dir, flow_rule);
-@@ -526,6 +589,26 @@ int nf_flow_rule_route_ipv4(struct net *
-           flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
-               return -1;
-+      other_tuple = &flow->tuplehash[!dir].tuple;
-+
-+      for (i = 0; i < other_tuple->encap_num; i++) {
-+              struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
-+
-+              entry->id = FLOW_ACTION_VLAN_PUSH;
-+              entry->vlan.vid = other_tuple->encap[i].id;
-+              entry->vlan.proto = other_tuple->encap[i].proto;
-+      }
-+
-+      return 0;
-+}
-+
-+int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
-+                          enum flow_offload_tuple_dir dir,
-+                          struct nf_flow_rule *flow_rule)
-+{
-+      if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
-+              return -1;
-+
-       if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
-               flow_offload_ipv4_snat(net, flow, dir, flow_rule);
-               flow_offload_port_snat(net, flow, dir, flow_rule);
-@@ -538,7 +621,7 @@ int nf_flow_rule_route_ipv4(struct net *
-           test_bit(NF_FLOW_DNAT, &flow->flags))
-               flow_offload_ipv4_checksum(net, flow, flow_rule);
--      flow_offload_redirect(flow, dir, flow_rule);
-+      flow_offload_redirect(net, flow, dir, flow_rule);
-       return 0;
- }
-@@ -548,11 +631,7 @@ int nf_flow_rule_route_ipv6(struct net *
-                           enum flow_offload_tuple_dir dir,
-                           struct nf_flow_rule *flow_rule)
- {
--      flow_offload_decap_tunnel(flow, dir, flow_rule);
--      flow_offload_encap_tunnel(flow, dir, flow_rule);
--
--      if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
--          flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
-+      if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
-               return -1;
-       if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
-@@ -564,7 +643,7 @@ int nf_flow_rule_route_ipv6(struct net *
-               flow_offload_port_dnat(net, flow, dir, flow_rule);
-       }
--      flow_offload_redirect(flow, dir, flow_rule);
-+      flow_offload_redirect(net, flow, dir, flow_rule);
-       return 0;
- }
-@@ -578,10 +657,10 @@ nf_flow_offload_rule_alloc(struct net *n
-                          enum flow_offload_tuple_dir dir)
- {
-       const struct nf_flowtable *flowtable = offload->flowtable;
-+      const struct flow_offload_tuple *tuple, *other_tuple;
-       const struct flow_offload *flow = offload->flow;
--      const struct flow_offload_tuple *tuple;
-+      struct dst_entry *other_dst = NULL;
-       struct nf_flow_rule *flow_rule;
--      struct dst_entry *other_dst;
-       int err = -ENOMEM;
-       flow_rule = kzalloc(sizeof(*flow_rule), GFP_KERNEL);
-@@ -597,7 +676,10 @@ nf_flow_offload_rule_alloc(struct net *n
-       flow_rule->rule->match.key = &flow_rule->match.key;
-       tuple = &flow->tuplehash[dir].tuple;
--      other_dst = flow->tuplehash[!dir].tuple.dst_cache;
-+      other_tuple = &flow->tuplehash[!dir].tuple;
-+      if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH)
-+              other_dst = other_tuple->dst_cache;
-+
-       err = nf_flow_rule_match(&flow_rule->match, tuple, other_dst);
-       if (err < 0)
-               goto err_flow_match;
diff --git a/target/linux/generic/pending-5.10/640-16-dsa-slave-add-support-for-TC_SETUP_FT.patch b/target/linux/generic/pending-5.10/640-16-dsa-slave-add-support-for-TC_SETUP_FT.patch
deleted file mode 100644 (file)
index 4dade26..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Mon, 18 Jan 2021 22:39:17 +0100
-Subject: [PATCH] dsa: slave: add support for TC_SETUP_FT
-
-The dsa infrastructure provides a well-defined hierarchy of devices,
-pass up the call to set up the flow block to the master device. From the
-software dataplane, the netfilter infrastructure uses the dsa slave
-devices to refer to the input and output device for the given skbuff.
-Similarly, the flowtable definition in the ruleset refers to the dsa
-slave port devices.
-
-This patch adds the glue code to call ndo_setup_tc with TC_SETUP_FT
-with the master device via the dsa slave devices.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
-
---- a/net/dsa/slave.c
-+++ b/net/dsa/slave.c
-@@ -1237,14 +1237,32 @@ static int dsa_slave_setup_tc_block(stru
-       }
- }
-+static int dsa_slave_setup_ft_block(struct dsa_switch *ds, int port,
-+                                  void *type_data)
-+{
-+      struct dsa_port *cpu_dp = dsa_to_port(ds, port)->cpu_dp;
-+      struct net_device *master = cpu_dp->master;
-+
-+      if (!master->netdev_ops->ndo_setup_tc)
-+              return -EOPNOTSUPP;
-+
-+      return master->netdev_ops->ndo_setup_tc(master, TC_SETUP_FT, type_data);
-+}
-+
- static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type,
-                             void *type_data)
- {
-       struct dsa_port *dp = dsa_slave_to_port(dev);
-       struct dsa_switch *ds = dp->ds;
--      if (type == TC_SETUP_BLOCK)
-+      switch (type) {
-+      case TC_SETUP_BLOCK:
-               return dsa_slave_setup_tc_block(dev, type_data);
-+      case TC_SETUP_FT:
-+              return dsa_slave_setup_ft_block(ds, dp->index, type_data);
-+      default:
-+              break;
-+      }
-       if (!ds->ops->port_setup_tc)
-               return -EOPNOTSUPP;
diff --git a/target/linux/generic/pending-5.10/640-17-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch b/target/linux/generic/pending-5.10/640-17-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch
deleted file mode 100644 (file)
index 26d172b..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Thu, 4 Mar 2021 19:24:11 +0100
-Subject: [PATCH] netfilter: nft_flow_offload: use direct xmit if
- hardware offload is enabled
-
-If there is a forward path to reach an ethernet device and hardware
-offload is enabled, then use the direct xmit path.
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -131,6 +131,7 @@ struct flow_offload_tuple {
-               struct dst_entry        *dst_cache;
-               struct {
-                       u32             ifidx;
-+                      u32             hw_ifidx;
-                       u8              h_source[ETH_ALEN];
-                       u8              h_dest[ETH_ALEN];
-               } out;
-@@ -188,6 +189,7 @@ struct nf_flow_route {
-               } in;
-               struct {
-                       u32                     ifindex;
-+                      u32                     hw_ifindex;
-                       u8                      h_source[ETH_ALEN];
-                       u8                      h_dest[ETH_ALEN];
-               } out;
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -106,6 +106,7 @@ static int flow_offload_fill_route(struc
-               memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
-                      ETH_ALEN);
-               flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
-+              flow_tuple->out.hw_ifidx = route->tuple[dir].out.hw_ifindex;
-               break;
-       case FLOW_OFFLOAD_XMIT_XFRM:
-       case FLOW_OFFLOAD_XMIT_NEIGH:
---- a/net/netfilter/nf_flow_table_offload.c
-+++ b/net/netfilter/nf_flow_table_offload.c
-@@ -506,7 +506,7 @@ static void flow_offload_redirect(struct
-       switch (this_tuple->xmit_type) {
-       case FLOW_OFFLOAD_XMIT_DIRECT:
-               this_tuple = &flow->tuplehash[dir].tuple;
--              ifindex = this_tuple->out.ifidx;
-+              ifindex = this_tuple->out.hw_ifidx;
-               break;
-       case FLOW_OFFLOAD_XMIT_NEIGH:
-               other_tuple = &flow->tuplehash[!dir].tuple;
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -66,6 +66,7 @@ static int nft_dev_fill_forward_path(con
- struct nft_forward_info {
-       const struct net_device *indev;
-       const struct net_device *outdev;
-+      const struct net_device *hw_outdev;
-       struct id {
-               __u16   id;
-               __be16  proto;
-@@ -76,9 +77,18 @@ struct nft_forward_info {
-       enum flow_offload_xmit_type xmit_type;
- };
-+static bool nft_is_valid_ether_device(const struct net_device *dev)
-+{
-+      if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
-+          dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr))
-+              return false;
-+
-+      return true;
-+}
-+
- static void nft_dev_path_info(const struct net_device_path_stack *stack,
-                             struct nft_forward_info *info,
--                            unsigned char *ha)
-+                            unsigned char *ha, struct nf_flowtable *flowtable)
- {
-       const struct net_device_path *path;
-       int i;
-@@ -140,6 +150,12 @@ static void nft_dev_path_info(const stru
-       }
-       if (!info->outdev)
-               info->outdev = info->indev;
-+
-+      info->hw_outdev = info->indev;
-+
-+      if (nf_flowtable_hw_offload(flowtable) &&
-+          nft_is_valid_ether_device(info->indev))
-+              info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
- }
- static bool nft_flowtable_find_dev(const struct net_device *dev,
-@@ -171,7 +187,7 @@ static void nft_dev_forward_path(struct
-       int i;
-       if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
--              nft_dev_path_info(&stack, &info, ha);
-+              nft_dev_path_info(&stack, &info, ha, &ft->data);
-       if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
-               return;
-@@ -187,6 +203,7 @@ static void nft_dev_forward_path(struct
-               memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
-               memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
-               route->tuple[dir].out.ifindex = info.outdev->ifindex;
-+              route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex;
-               route->tuple[dir].xmit_type = info.xmit_type;
-       }
- }
diff --git a/target/linux/generic/pending-5.10/640-18-netfilter-nf_flow_table-fix-untagging-with-hardware-.patch b/target/linux/generic/pending-5.10/640-18-netfilter-nf_flow_table-fix-untagging-with-hardware-.patch
deleted file mode 100644 (file)
index 18f4319..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 8 Mar 2021 12:06:44 +0100
-Subject: [PATCH] netfilter: nf_flow_table: fix untagging with
- hardware-offloaded bridge vlan_filtering
-
-When switchdev offloading is enabled, treat an untagged VLAN as tagged for
-ingress only
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -849,6 +849,7 @@ struct net_device_path {
-                               DEV_PATH_BR_VLAN_KEEP,
-                               DEV_PATH_BR_VLAN_TAG,
-                               DEV_PATH_BR_VLAN_UNTAG,
-+                              DEV_PATH_BR_VLAN_UNTAG_HW,
-                       }               vlan_mode;
-                       u16             vlan_id;
-                       __be16          vlan_proto;
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -123,9 +123,10 @@ struct flow_offload_tuple {
-       /* All members above are keys for lookups, see flow_offload_hash(). */
-       struct { }                      __hash;
--      u8                              dir:4,
-+      u8                              dir:2,
-                                       xmit_type:2,
--                                      encap_num:2;
-+                                      encap_num:2,
-+                                      in_vlan_ingress:2;
-       u16                             mtu;
-       union {
-               struct dst_entry        *dst_cache;
-@@ -185,7 +186,8 @@ struct nf_flow_route {
-                               u16             id;
-                               __be16          proto;
-                       } encap[NF_FLOW_TABLE_ENCAP_MAX];
--                      u8                      num_encaps;
-+                      u8                      num_encaps:2,
-+                                              ingress_vlans:2;
-               } in;
-               struct {
-                       u32                     ifindex;
---- a/net/bridge/br_device.c
-+++ b/net/bridge/br_device.c
-@@ -435,6 +435,7 @@ static int br_fill_forward_path(struct n
-               ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto;
-               ctx->num_vlans++;
-               break;
-+      case DEV_PATH_BR_VLAN_UNTAG_HW:
-       case DEV_PATH_BR_VLAN_UNTAG:
-               ctx->num_vlans--;
-               break;
---- a/net/bridge/br_vlan.c
-+++ b/net/bridge/br_vlan.c
-@@ -1374,6 +1374,8 @@ int br_vlan_fill_forward_path_mode(struc
-       if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
-               path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
-+      else if (v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
-+              path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG_HW;
-       else
-               path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -95,6 +95,8 @@ static int flow_offload_fill_route(struc
-       for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
-               flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
-               flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
-+              if (route->tuple[dir].in.ingress_vlans & BIT(i))
-+                      flow_tuple->in_vlan_ingress |= BIT(j);
-               j++;
-       }
-       flow_tuple->encap_num = route->tuple[dir].in.num_encaps;
---- a/net/netfilter/nf_flow_table_offload.c
-+++ b/net/netfilter/nf_flow_table_offload.c
-@@ -592,8 +592,12 @@ nf_flow_rule_route_common(struct net *ne
-       other_tuple = &flow->tuplehash[!dir].tuple;
-       for (i = 0; i < other_tuple->encap_num; i++) {
--              struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
-+              struct flow_action_entry *entry;
-+              if (other_tuple->in_vlan_ingress & BIT(i))
-+                      continue;
-+
-+              entry = flow_action_entry_next(flow_rule);
-               entry->id = FLOW_ACTION_VLAN_PUSH;
-               entry->vlan.vid = other_tuple->encap[i].id;
-               entry->vlan.proto = other_tuple->encap[i].proto;
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -72,6 +72,7 @@ struct nft_forward_info {
-               __be16  proto;
-       } encap[NF_FLOW_TABLE_ENCAP_MAX];
-       u8 num_encaps;
-+      u8 ingress_vlans;
-       u8 h_source[ETH_ALEN];
-       u8 h_dest[ETH_ALEN];
-       enum flow_offload_xmit_type xmit_type;
-@@ -130,6 +131,9 @@ static void nft_dev_path_info(const stru
-                               memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
-                       switch (path->bridge.vlan_mode) {
-+                      case DEV_PATH_BR_VLAN_UNTAG_HW:
-+                              info->ingress_vlans |= BIT(info->num_encaps - 1);
-+                              break;
-                       case DEV_PATH_BR_VLAN_TAG:
-                               info->encap[info->num_encaps].id = path->bridge.vlan_id;
-                               info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
-@@ -198,6 +202,7 @@ static void nft_dev_forward_path(struct
-               route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
-       }
-       route->tuple[!dir].in.num_encaps = info.num_encaps;
-+      route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
-       if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
-               memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
diff --git a/target/linux/generic/pending-5.10/640-19-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch b/target/linux/generic/pending-5.10/640-19-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch
deleted file mode 100644 (file)
index 3eaad27..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Tue, 2 Mar 2021 00:51:31 +0100
-Subject: [PATCH] net: flow_offload: add FLOW_ACTION_PPPOE_PUSH
-
----
-
---- a/include/net/flow_offload.h
-+++ b/include/net/flow_offload.h
-@@ -147,6 +147,7 @@ enum flow_action_id {
-       FLOW_ACTION_MPLS_POP,
-       FLOW_ACTION_MPLS_MANGLE,
-       FLOW_ACTION_GATE,
-+      FLOW_ACTION_PPPOE_PUSH,
-       NUM_FLOW_ACTIONS,
- };
-@@ -271,6 +272,9 @@ struct flow_action_entry {
-                       u32             num_entries;
-                       struct action_gate_entry *entries;
-               } gate;
-+              struct {                                /* FLOW_ACTION_PPPOE_PUSH */
-+                      u16             sid;
-+              } pppoe;
-       };
-       struct flow_action_cookie *cookie; /* user defined action cookie */
- };
diff --git a/target/linux/generic/pending-5.10/640-20-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch b/target/linux/generic/pending-5.10/640-20-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch
deleted file mode 100644 (file)
index fcafff1..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Tue, 2 Mar 2021 01:01:50 +0100
-Subject: [PATCH] netfilter: flowtable: support for
- FLOW_ACTION_PPPOE_PUSH
-
----
-
---- a/net/netfilter/nf_flow_table_offload.c
-+++ b/net/netfilter/nf_flow_table_offload.c
-@@ -598,9 +598,18 @@ nf_flow_rule_route_common(struct net *ne
-                       continue;
-               entry = flow_action_entry_next(flow_rule);
--              entry->id = FLOW_ACTION_VLAN_PUSH;
--              entry->vlan.vid = other_tuple->encap[i].id;
--              entry->vlan.proto = other_tuple->encap[i].proto;
-+
-+              switch (other_tuple->encap[i].proto) {
-+              case htons(ETH_P_PPP_SES):
-+                      entry->id = FLOW_ACTION_PPPOE_PUSH;
-+                      entry->pppoe.sid = other_tuple->encap[i].id;
-+                      break;
-+              case htons(ETH_P_8021Q):
-+                      entry->id = FLOW_ACTION_VLAN_PUSH;
-+                      entry->vlan.vid = other_tuple->encap[i].id;
-+                      entry->vlan.proto = other_tuple->encap[i].proto;
-+                      break;
-+              }
-       }
-       return 0;
index 85c859b3884d556e57a02346b5bc2511385a7e35..58cecb6026f09bea8444002d4023af4810d7ff98 100644 (file)
@@ -9,7 +9,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
 
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -857,7 +857,8 @@ static int txd_to_idx(struct mtk_tx_ring
+@@ -858,7 +858,8 @@ static int txd_to_idx(struct mtk_tx_ring
        return ((void *)dma - (void *)ring->dma) / sizeof(*dma);
  }
  
@@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  {
        if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
                if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
-@@ -889,8 +890,12 @@ static void mtk_tx_unmap(struct mtk_eth
+@@ -890,8 +891,12 @@ static void mtk_tx_unmap(struct mtk_eth
  
        tx_buf->flags = 0;
        if (tx_buf->skb &&
@@ -34,7 +34,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
        tx_buf->skb = NULL;
  }
  
-@@ -1068,7 +1073,7 @@ err_dma:
+@@ -1069,7 +1074,7 @@ err_dma:
                tx_buf = mtk_desc_to_tx_buf(ring, itxd);
  
                /* unmap dma */
index 922c35a3e477b5aa862d87f35bcf167679bb4a39..577b2eda162a90100d53f3d6d786fbcaca16ef52 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
 
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -85,7 +85,7 @@ static int mtk_mdio_busy_wait(struct mtk
+@@ -86,7 +86,7 @@ static int mtk_mdio_busy_wait(struct mtk
                        return 0;
                if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
                        break;
index cebfe32d959e009fc38799951b3599a6bb1c3615..9357e79c88dc9cd9cce573b47b1bac7d0dcbc799 100644 (file)
@@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
                skb_record_rx_queue(skb, 0);
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -295,6 +295,7 @@
+@@ -300,6 +300,7 @@
  #define RX_DMA_LSO            BIT(30)
  #define RX_DMA_PLEN0(_x)      (((_x) & 0x3fff) << 16)
  #define RX_DMA_GET_PLEN0(_x)  (((_x) >> 16) & 0x3fff)
index 82bdb10ebab2a19e4abf2a3ffc68ce373e58631f..eda10a452e7703e352e02bb9609d5a352417eaa4 100644 (file)
@@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
 
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1130,17 +1130,6 @@ static void mtk_wake_queue(struct mtk_et
+@@ -1131,17 +1131,6 @@ static void mtk_wake_queue(struct mtk_et
        }
  }
  
@@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
  {
        struct mtk_mac *mac = netdev_priv(dev);
-@@ -1161,7 +1150,7 @@ static netdev_tx_t mtk_start_xmit(struct
+@@ -1162,7 +1151,7 @@ static netdev_tx_t mtk_start_xmit(struct
  
        tx_num = mtk_cal_txd_req(skb);
        if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
@@ -39,7 +39,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
                netif_err(eth, tx_queued, dev,
                          "Tx Ring full when queue awake!\n");
                spin_unlock(&eth->page_lock);
-@@ -1187,7 +1176,7 @@ static netdev_tx_t mtk_start_xmit(struct
+@@ -1188,7 +1177,7 @@ static netdev_tx_t mtk_start_xmit(struct
                goto drop;
  
        if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
index 94c0510257f5808fd8a7daacfb895140ee902867..24e069b8ae5022afd345a749c36582acc9d40ce6 100644 (file)
@@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
                        MTK_QDMA_GLO_CFG);
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -197,7 +197,7 @@
+@@ -202,7 +202,7 @@
  #define MTK_RX_BT_32DWORDS    (3 << 11)
  #define MTK_NDP_CO_PRO                BIT(10)
  #define MTK_TX_WB_DDONE               BIT(6)
index f68cbc333c33139ad2af86ef1e9905f0a06a61a4..201b70b65d3982714d69478b4de334f9505fbb3d 100644 (file)
@@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
 
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -19,7 +19,7 @@
+@@ -21,7 +21,7 @@
  #define MTK_QDMA_PAGE_SIZE    2048
  #define       MTK_MAX_RX_LENGTH       1536
  #define MTK_TX_DMA_BUF_LEN    0x3fff
index 61a51ba33b4df31ab73959d93a0d73a714cd87a9..be82637e6dc998b58716f84ea120789edb3abc3e 100644 (file)
@@ -20,7 +20,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
          MediaTek SoC family.
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1232,12 +1232,13 @@ static void mtk_update_rx_cpu_idx(struct
+@@ -1233,12 +1233,13 @@ static void mtk_update_rx_cpu_idx(struct
  static int mtk_poll_rx(struct napi_struct *napi, int budget,
                       struct mtk_eth *eth)
  {
@@ -95,7 +95,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
        if (likely(napi_schedule_prep(&eth->tx_napi))) {
                __napi_schedule(&eth->tx_napi);
                mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
-@@ -2315,6 +2332,9 @@ static int mtk_stop(struct net_device *d
+@@ -2323,6 +2340,9 @@ static int mtk_stop(struct net_device *d
        napi_disable(&eth->tx_napi);
        napi_disable(&eth->rx_napi);
  
@@ -105,7 +105,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
        if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
                mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
        mtk_stop_dma(eth, MTK_PDMA_GLO_CFG);
-@@ -2364,6 +2384,64 @@ err_disable_clks:
+@@ -2375,6 +2395,64 @@ err_disable_clks:
        return ret;
  }
  
@@ -170,7 +170,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  static int mtk_hw_init(struct mtk_eth *eth)
  {
        int i, val, ret;
-@@ -2385,9 +2463,6 @@ static int mtk_hw_init(struct mtk_eth *e
+@@ -2396,9 +2474,6 @@ static int mtk_hw_init(struct mtk_eth *e
                        goto err_disable_pm;
                }
  
@@ -180,7 +180,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
                /* disable delay and normal interrupt */
                mtk_tx_irq_disable(eth, ~0);
                mtk_rx_irq_disable(eth, ~0);
-@@ -2426,11 +2501,10 @@ static int mtk_hw_init(struct mtk_eth *e
+@@ -2437,11 +2512,10 @@ static int mtk_hw_init(struct mtk_eth *e
        /* Enable RX VLan Offloading */
        mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
  
@@ -194,7 +194,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
        mtk_tx_irq_disable(eth, ~0);
        mtk_rx_irq_disable(eth, ~0);
  
-@@ -2934,6 +3008,13 @@ static int mtk_probe(struct platform_dev
+@@ -2946,6 +3020,13 @@ static int mtk_probe(struct platform_dev
        spin_lock_init(&eth->page_lock);
        spin_lock_init(&eth->tx_irq_lock);
        spin_lock_init(&eth->rx_irq_lock);
@@ -210,15 +210,15 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
                eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -15,6 +15,7 @@
- #include <linux/u64_stats_sync.h>
+@@ -16,6 +16,7 @@
  #include <linux/refcount.h>
  #include <linux/phylink.h>
+ #include <linux/rhashtable.h>
 +#include <linux/dim.h>
+ #include "mtk_ppe.h"
  
  #define MTK_QDMA_PAGE_SIZE    2048
- #define       MTK_MAX_RX_LENGTH       1536
-@@ -131,13 +132,18 @@
+@@ -136,13 +137,18 @@
  
  /* PDMA Delay Interrupt Register */
  #define MTK_PDMA_DELAY_INT            0xa0c
@@ -242,7 +242,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  
  /* PDMA Interrupt Status Register */
  #define MTK_PDMA_INT_STATUS   0xa20
-@@ -219,6 +225,7 @@
+@@ -224,6 +230,7 @@
  /* QDMA Interrupt Status Register */
  #define MTK_QDMA_INT_STATUS   0x1A18
  #define MTK_RX_DONE_DLY               BIT(30)
@@ -250,7 +250,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  #define MTK_RX_DONE_INT3      BIT(19)
  #define MTK_RX_DONE_INT2      BIT(18)
  #define MTK_RX_DONE_INT1      BIT(17)
-@@ -228,8 +235,7 @@
+@@ -233,8 +240,7 @@
  #define MTK_TX_DONE_INT1      BIT(1)
  #define MTK_TX_DONE_INT0      BIT(0)
  #define MTK_RX_DONE_INT               MTK_RX_DONE_DLY
@@ -260,7 +260,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  
  /* QDMA Interrupt grouping registers */
  #define MTK_QDMA_INT_GRP1     0x1a20
-@@ -892,6 +898,18 @@ struct mtk_eth {
+@@ -905,6 +911,18 @@ struct mtk_eth {
  
        const struct mtk_soc_data       *soc;
  
index 20181b8d125099f74622762c2ed6b3de6f7906b8..b4ae8dc2e8da3b031baf949eb39244080accf412 100644 (file)
@@ -49,7 +49,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
        } else {
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -624,6 +624,7 @@ struct mtk_tx_buf {
+@@ -636,6 +636,7 @@ struct mtk_tx_buf {
   * @phys:             The physical addr of tx_buf
   * @next_free:                Pointer to the next free descriptor
   * @last_free:                Pointer to the last free descriptor
@@ -57,7 +57,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
   * @thresh:           The threshold of minimum amount of free descriptors
   * @free_count:               QDMA uses a linked list. Track how many free descriptors
   *                    are present
-@@ -634,6 +635,7 @@ struct mtk_tx_ring {
+@@ -646,6 +647,7 @@ struct mtk_tx_ring {
        dma_addr_t phys;
        struct mtk_tx_dma *next_free;
        struct mtk_tx_dma *last_free;
index bbd8ae7be4a5c4093d22a326817e2e8c91b983b8..b744ac643ceed3b456f6e22bcf09f7bb533effbe 100644 (file)
@@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
 
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -776,13 +776,18 @@ static inline int mtk_max_buf_size(int f
+@@ -777,13 +777,18 @@ static inline int mtk_max_buf_size(int f
        return buf_size;
  }
  
@@ -32,7 +32,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  }
  
  /* the qdma core needs scratch memory to be setup */
-@@ -1254,8 +1259,7 @@ static int mtk_poll_rx(struct napi_struc
+@@ -1255,8 +1260,7 @@ static int mtk_poll_rx(struct napi_struc
                rxd = &ring->dma[idx];
                data = ring->data[idx];
  
diff --git a/target/linux/generic/pending-5.10/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch b/target/linux/generic/pending-5.10/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch
deleted file mode 100644 (file)
index d81d4bf..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 13 Sep 2020 08:17:02 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: fix parsing packets in GDM
-
-When using DSA, set the special tag in GDM ingress control to allow the MAC
-to parse packets properly earlier. This affects rx DMA source port reporting.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -19,6 +19,7 @@
- #include <linux/interrupt.h>
- #include <linux/pinctrl/devinfo.h>
- #include <linux/phylink.h>
-+#include <net/dsa.h>
- #include "mtk_eth_soc.h"
-@@ -1263,13 +1264,12 @@ static int mtk_poll_rx(struct napi_struc
-                       break;
-               /* find out which mac the packet come from. values start at 1 */
--              if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
-+              if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) ||
-+                  (trxd.rxd4 & RX_DMA_SPECIAL_TAG))
-                       mac = 0;
--              } else {
--                      mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
--                              RX_DMA_FPORT_MASK;
--                      mac--;
--              }
-+              else
-+                      mac = ((trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
-+                             RX_DMA_FPORT_MASK) - 1;
-               if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
-                            !eth->netdev[mac]))
-@@ -2251,6 +2251,9 @@ static void mtk_gdm_config(struct mtk_et
-               val |= config;
-+              if (!i && eth->netdev[0] && netdev_uses_dsa(eth->netdev[0]))
-+                      val |= MTK_GDMA_SPECIAL_TAG;
-+
-               mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
-       }
-       /* Reset and enable PSE */
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -82,6 +82,7 @@
- /* GDM Exgress Control Register */
- #define MTK_GDMA_FWD_CFG(x)   (0x500 + (x * 0x1000))
-+#define MTK_GDMA_SPECIAL_TAG  BIT(24)
- #define MTK_GDMA_ICS_EN               BIT(22)
- #define MTK_GDMA_TCS_EN               BIT(21)
- #define MTK_GDMA_UCS_EN               BIT(20)
-@@ -311,6 +312,7 @@
- #define RX_DMA_L4_VALID_PDMA  BIT(30)         /* when PDMA is used */
- #define RX_DMA_FPORT_SHIFT    19
- #define RX_DMA_FPORT_MASK     0x7
-+#define RX_DMA_SPECIAL_TAG    BIT(22)
- /* PHY Indirect Access Control registers */
- #define MTK_PHY_IAC           0x10004
diff --git a/target/linux/generic/pending-5.10/770-15-net-ethernet-mtk_eth_soc-add-support-for-initializin.patch b/target/linux/generic/pending-5.10/770-15-net-ethernet-mtk_eth_soc-add-support-for-initializin.patch
deleted file mode 100644 (file)
index 0928217..0000000
+++ /dev/null
@@ -1,1307 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 11 Oct 2020 22:23:08 +0200
-Subject: [PATCH] ethernet: mediatek: mtk_eth_soc: add support for
- initializing the PPE
-
-The PPE (packet processing engine) is used to offload NAT/routed or even
-bridged flows. This patch brings up the PPE and uses it to get a packet
-hash. It also contains some functionality that will be used to bring up
-flow offloading.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.c
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.h
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -4,5 +4,5 @@
- #
- obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
--mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
-+mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o
- obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -2284,12 +2284,17 @@ static int mtk_open(struct net_device *d
-       /* we run 2 netdevs on the same dma ring so we only bring it up once */
-       if (!refcount_read(&eth->dma_refcnt)) {
--              int err = mtk_start_dma(eth);
-+              u32 gdm_config = MTK_GDMA_TO_PDMA;
-+              int err;
-+              err = mtk_start_dma(eth);
-               if (err)
-                       return err;
--              mtk_gdm_config(eth, MTK_GDMA_TO_PDMA);
-+              if (eth->soc->offload_version && mtk_ppe_start(&eth->ppe) == 0)
-+                      gdm_config = MTK_GDMA_TO_PPE;
-+
-+              mtk_gdm_config(eth, gdm_config);
-               napi_enable(&eth->tx_napi);
-               napi_enable(&eth->rx_napi);
-@@ -2359,6 +2364,9 @@ static int mtk_stop(struct net_device *d
-       mtk_dma_free(eth);
-+      if (eth->soc->offload_version)
-+              mtk_ppe_stop(&eth->ppe);
-+
-       return 0;
- }
-@@ -3148,6 +3156,13 @@ static int mtk_probe(struct platform_dev
-                       goto err_free_dev;
-       }
-+      if (eth->soc->offload_version) {
-+              err = mtk_ppe_init(&eth->ppe, eth->dev,
-+                                 eth->base + MTK_ETH_PPE_BASE, 2);
-+              if (err)
-+                      goto err_free_dev;
-+      }
-+
-       for (i = 0; i < MTK_MAX_DEVS; i++) {
-               if (!eth->netdev[i])
-                       continue;
-@@ -3222,6 +3237,7 @@ static const struct mtk_soc_data mt7621_
-       .hw_features = MTK_HW_FEATURES,
-       .required_clks = MT7621_CLKS_BITMAP,
-       .required_pctl = false,
-+      .offload_version = 2,
- };
- static const struct mtk_soc_data mt7622_data = {
-@@ -3230,6 +3246,7 @@ static const struct mtk_soc_data mt7622_
-       .hw_features = MTK_HW_FEATURES,
-       .required_clks = MT7622_CLKS_BITMAP,
-       .required_pctl = false,
-+      .offload_version = 2,
- };
- static const struct mtk_soc_data mt7623_data = {
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -16,6 +16,7 @@
- #include <linux/refcount.h>
- #include <linux/phylink.h>
- #include <linux/dim.h>
-+#include "mtk_ppe.h"
- #define MTK_QDMA_PAGE_SIZE    2048
- #define       MTK_MAX_RX_LENGTH       1536
-@@ -87,6 +88,7 @@
- #define MTK_GDMA_TCS_EN               BIT(21)
- #define MTK_GDMA_UCS_EN               BIT(20)
- #define MTK_GDMA_TO_PDMA      0x0
-+#define MTK_GDMA_TO_PPE               0x4444
- #define MTK_GDMA_DROP_ALL       0x7777
- /* Unicast Filter MAC Address Register - Low */
-@@ -308,6 +310,12 @@
- #define RX_DMA_VID(_x)                ((_x) & 0xfff)
- /* QDMA descriptor rxd4 */
-+#define MTK_RXD4_FOE_ENTRY    GENMASK(13, 0)
-+#define MTK_RXD4_PPE_CPU_REASON       GENMASK(18, 14)
-+#define MTK_RXD4_SRC_PORT     GENMASK(21, 19)
-+#define MTK_RXD4_ALG          GENMASK(31, 22)
-+
-+/* QDMA descriptor rxd4 */
- #define RX_DMA_L4_VALID               BIT(24)
- #define RX_DMA_L4_VALID_PDMA  BIT(30)         /* when PDMA is used */
- #define RX_DMA_FPORT_SHIFT    19
-@@ -807,6 +815,7 @@ struct mtk_soc_data {
-       u32             caps;
-       u32             required_clks;
-       bool            required_pctl;
-+      u8              offload_version;
-       netdev_features_t hw_features;
- };
-@@ -918,6 +927,8 @@ struct mtk_eth {
-       u32                             tx_int_status_reg;
-       u32                             rx_dma_l4_valid;
-       int                             ip_align;
-+
-+      struct mtk_ppe                  ppe;
- };
- /* struct mtk_mac -   the structure that holds the info about the MACs of the
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -0,0 +1,511 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+
-+#include <linux/kernel.h>
-+#include <linux/jiffies.h>
-+#include <linux/delay.h>
-+#include <linux/io.h>
-+#include <linux/etherdevice.h>
-+#include <linux/platform_device.h>
-+#include "mtk_ppe.h"
-+#include "mtk_ppe_regs.h"
-+
-+static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
-+{
-+      writel(val, ppe->base + reg);
-+}
-+
-+static u32 ppe_r32(struct mtk_ppe *ppe, u32 reg)
-+{
-+      return readl(ppe->base + reg);
-+}
-+
-+static u32 ppe_m32(struct mtk_ppe *ppe, u32 reg, u32 mask, u32 set)
-+{
-+      u32 val;
-+
-+      val = ppe_r32(ppe, reg);
-+      val &= ~mask;
-+      val |= set;
-+      ppe_w32(ppe, reg, val);
-+
-+      return val;
-+}
-+
-+static u32 ppe_set(struct mtk_ppe *ppe, u32 reg, u32 val)
-+{
-+      return ppe_m32(ppe, reg, 0, val);
-+}
-+
-+static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val)
-+{
-+      return ppe_m32(ppe, reg, val, 0);
-+}
-+
-+static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
-+{
-+      unsigned long timeout = jiffies + HZ;
-+
-+      while (time_is_after_jiffies(timeout)) {
-+              if (!(ppe_r32(ppe, MTK_PPE_GLO_CFG) & MTK_PPE_GLO_CFG_BUSY))
-+                      return 0;
-+
-+              usleep_range(10, 20);
-+      }
-+
-+      dev_err(ppe->dev, "PPE table busy");
-+
-+      return -ETIMEDOUT;
-+}
-+
-+static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
-+{
-+      ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
-+      ppe_clear(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
-+}
-+
-+static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable)
-+{
-+      mtk_ppe_cache_clear(ppe);
-+
-+      ppe_m32(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_EN,
-+              enable * MTK_PPE_CACHE_CTL_EN);
-+}
-+
-+static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e)
-+{
-+      u32 hv1, hv2, hv3;
-+      u32 hash;
-+
-+      switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
-+              case MTK_PPE_PKT_TYPE_BRIDGE:
-+                      hv1 = e->bridge.src_mac_lo;
-+                      hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16);
-+                      hv2 = e->bridge.src_mac_hi >> 16;
-+                      hv2 ^= e->bridge.dest_mac_lo;
-+                      hv3 = e->bridge.dest_mac_hi;
-+                      break;
-+              case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
-+              case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
-+                      hv1 = e->ipv4.orig.ports;
-+                      hv2 = e->ipv4.orig.dest_ip;
-+                      hv3 = e->ipv4.orig.src_ip;
-+                      break;
-+              case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
-+              case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
-+                      hv1 = e->ipv6.src_ip[3] ^ e->ipv6.dest_ip[3];
-+                      hv1 ^= e->ipv6.ports;
-+
-+                      hv2 = e->ipv6.src_ip[2] ^ e->ipv6.dest_ip[2];
-+                      hv2 ^= e->ipv6.dest_ip[0];
-+
-+                      hv3 = e->ipv6.src_ip[1] ^ e->ipv6.dest_ip[1];
-+                      hv3 ^= e->ipv6.src_ip[0];
-+                      break;
-+              case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+              case MTK_PPE_PKT_TYPE_IPV6_6RD:
-+              default:
-+                      WARN_ON_ONCE(1);
-+                      return MTK_PPE_HASH_MASK;
-+      }
-+
-+      hash = (hv1 & hv2) | ((~hv1) & hv3);
-+      hash = (hash >> 24) | ((hash & 0xffffff) << 8);
-+      hash ^= hv1 ^ hv2 ^ hv3;
-+      hash ^= hash >> 16;
-+      hash <<= 1;
-+      hash &= MTK_PPE_ENTRIES - 1;
-+
-+      return hash;
-+}
-+
-+static inline struct mtk_foe_mac_info *
-+mtk_foe_entry_l2(struct mtk_foe_entry *entry)
-+{
-+      int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+
-+      if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
-+              return &entry->ipv6.l2;
-+
-+      return &entry->ipv4.l2;
-+}
-+
-+static inline u32 *
-+mtk_foe_entry_ib2(struct mtk_foe_entry *entry)
-+{
-+      int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+
-+      if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
-+              return &entry->ipv6.ib2;
-+
-+      return &entry->ipv4.ib2;
-+}
-+
-+int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
-+                        u8 pse_port, u8 *src_mac, u8 *dest_mac)
-+{
-+      struct mtk_foe_mac_info *l2;
-+      u32 ports_pad, val;
-+
-+      memset(entry, 0, sizeof(*entry));
-+
-+      val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
-+            FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
-+            FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
-+            MTK_FOE_IB1_BIND_TTL |
-+            MTK_FOE_IB1_BIND_CACHE;
-+      entry->ib1 = val;
-+
-+      val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) |
-+            FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) |
-+            FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port);
-+
-+      if (is_multicast_ether_addr(dest_mac))
-+              val |= MTK_FOE_IB2_MULTICAST;
-+
-+      ports_pad = 0xa5a5a500 | (l4proto & 0xff);
-+      if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
-+              entry->ipv4.orig.ports = ports_pad;
-+      if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
-+              entry->ipv6.ports = ports_pad;
-+
-+      if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
-+              entry->ipv6.ib2 = val;
-+              l2 = &entry->ipv6.l2;
-+      } else {
-+              entry->ipv4.ib2 = val;
-+              l2 = &entry->ipv4.l2;
-+      }
-+
-+      l2->dest_mac_hi = get_unaligned_be32(dest_mac);
-+      l2->dest_mac_lo = get_unaligned_be16(dest_mac + 4);
-+      l2->src_mac_hi = get_unaligned_be32(src_mac);
-+      l2->src_mac_lo = get_unaligned_be16(src_mac + 4);
-+
-+      if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
-+              l2->etype = ETH_P_IPV6;
-+      else
-+              l2->etype = ETH_P_IP;
-+
-+      return 0;
-+}
-+
-+int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port)
-+{
-+      u32 *ib2 = mtk_foe_entry_ib2(entry);
-+      u32 val;
-+
-+      val = *ib2;
-+      val &= ~MTK_FOE_IB2_DEST_PORT;
-+      val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
-+      *ib2 = val;
-+
-+      return 0;
-+}
-+
-+int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress,
-+                               __be32 src_addr, __be16 src_port,
-+                               __be32 dest_addr, __be16 dest_port)
-+{
-+      int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+      struct mtk_ipv4_tuple *t;
-+
-+      switch (type) {
-+      case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
-+              if (egress) {
-+                      t = &entry->ipv4.new;
-+                      break;
-+              }
-+              fallthrough;
-+      case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+      case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
-+              t = &entry->ipv4.orig;
-+              break;
-+      case MTK_PPE_PKT_TYPE_IPV6_6RD:
-+              entry->ipv6_6rd.tunnel_src_ip = be32_to_cpu(src_addr);
-+              entry->ipv6_6rd.tunnel_dest_ip = be32_to_cpu(dest_addr);
-+              return 0;
-+      default:
-+              WARN_ON_ONCE(1);
-+              return -EINVAL;
-+      }
-+
-+      t->src_ip = be32_to_cpu(src_addr);
-+      t->dest_ip = be32_to_cpu(dest_addr);
-+
-+      if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
-+              return 0;
-+
-+      t->src_port = be16_to_cpu(src_port);
-+      t->dest_port = be16_to_cpu(dest_port);
-+
-+      return 0;
-+}
-+
-+int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
-+                               __be32 *src_addr, __be16 src_port,
-+                               __be32 *dest_addr, __be16 dest_port)
-+{
-+      int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+      u32 *src, *dest;
-+      int i;
-+
-+      switch (type) {
-+      case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+              src = entry->dslite.tunnel_src_ip;
-+              dest = entry->dslite.tunnel_dest_ip;
-+              break;
-+      case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
-+      case MTK_PPE_PKT_TYPE_IPV6_6RD:
-+              entry->ipv6.src_port = be16_to_cpu(src_port);
-+              entry->ipv6.dest_port = be16_to_cpu(dest_port);
-+              fallthrough;
-+      case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
-+              src = entry->ipv6.src_ip;
-+              dest = entry->ipv6.dest_ip;
-+              break;
-+      default:
-+              WARN_ON_ONCE(1);
-+              return -EINVAL;
-+      };
-+
-+      for (i = 0; i < 4; i++)
-+              src[i] = be32_to_cpu(src_addr[i]);
-+      for (i = 0; i < 4; i++)
-+              dest[i] = be32_to_cpu(dest_addr[i]);
-+
-+      return 0;
-+}
-+
-+int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port)
-+{
-+      struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
-+
-+      l2->etype = BIT(port);
-+
-+      if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER))
-+              entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
-+      else
-+              l2->etype |= BIT(8);
-+
-+      entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG;
-+
-+      return 0;
-+}
-+
-+int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid)
-+{
-+      struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
-+
-+      switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) {
-+      case 0:
-+              entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG |
-+                            FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
-+              l2->vlan1 = vid;
-+              return 0;
-+      case 1:
-+              if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) {
-+                      l2->vlan1 = vid;
-+                      l2->etype |= BIT(8);
-+              } else {
-+                      l2->vlan2 = vid;
-+                      entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
-+              }
-+              return 0;
-+      default:
-+              return -ENOSPC;
-+      }
-+}
-+
-+int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid)
-+{
-+      struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
-+
-+      if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) ||
-+          (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG))
-+              l2->etype = ETH_P_PPP_SES;
-+
-+      entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE;
-+      l2->pppoe_id = sid;
-+
-+      return 0;
-+}
-+
-+static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
-+{
-+      return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
-+             FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
-+}
-+
-+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
-+                       u16 timestamp)
-+{
-+      struct mtk_foe_entry *hwe;
-+      u32 hash;
-+
-+      timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
-+      entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
-+      entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
-+
-+      hash = mtk_ppe_hash_entry(entry);
-+      hwe = &ppe->foe_table[hash];
-+      if (!mtk_foe_entry_usable(hwe)) {
-+              hwe++;
-+              hash++;
-+
-+              if (!mtk_foe_entry_usable(hwe))
-+                      return -ENOSPC;
-+      }
-+
-+      memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
-+      wmb();
-+      hwe->ib1 = entry->ib1;
-+
-+      dma_wmb();
-+
-+      mtk_ppe_cache_clear(ppe);
-+
-+      return hash;
-+}
-+
-+int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
-+               int version)
-+{
-+      struct mtk_foe_entry *foe;
-+
-+      /* need to allocate a separate device, since it PPE DMA access is
-+       * not coherent.
-+       */
-+      ppe->base = base;
-+      ppe->dev = dev;
-+      ppe->version = version;
-+
-+      foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
-+                                &ppe->foe_phys, GFP_KERNEL);
-+      if (!foe)
-+              return -ENOMEM;
-+
-+      ppe->foe_table = foe;
-+
-+      mtk_ppe_debugfs_init(ppe);
-+
-+      return 0;
-+}
-+
-+static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
-+{
-+      static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 };
-+      int i, k;
-+
-+      memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(ppe->foe_table));
-+
-+      if (!IS_ENABLED(CONFIG_SOC_MT7621))
-+              return;
-+
-+      /* skip all entries that cross the 1024 byte boundary */
-+      for (i = 0; i < MTK_PPE_ENTRIES; i += 128)
-+              for (k = 0; k < ARRAY_SIZE(skip); k++)
-+                      ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC;
-+}
-+
-+int mtk_ppe_start(struct mtk_ppe *ppe)
-+{
-+      u32 val;
-+
-+      mtk_ppe_init_foe_table(ppe);
-+      ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
-+
-+      val = MTK_PPE_TB_CFG_ENTRY_80B |
-+            MTK_PPE_TB_CFG_AGE_NON_L4 |
-+            MTK_PPE_TB_CFG_AGE_UNBIND |
-+            MTK_PPE_TB_CFG_AGE_TCP |
-+            MTK_PPE_TB_CFG_AGE_UDP |
-+            MTK_PPE_TB_CFG_AGE_TCP_FIN |
-+            FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS,
-+                       MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) |
-+            FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE,
-+                       MTK_PPE_KEEPALIVE_DISABLE) |
-+            FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) |
-+            FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE,
-+                       MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) |
-+            FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
-+                       MTK_PPE_ENTRIES_SHIFT);
-+      ppe_w32(ppe, MTK_PPE_TB_CFG, val);
-+
-+      ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
-+              MTK_PPE_IP_PROTO_CHK_IPV4 | MTK_PPE_IP_PROTO_CHK_IPV6);
-+
-+      mtk_ppe_cache_enable(ppe, true);
-+
-+      val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
-+            MTK_PPE_FLOW_CFG_IP4_UDP_FRAG |
-+            MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
-+            MTK_PPE_FLOW_CFG_IP6_5T_ROUTE |
-+            MTK_PPE_FLOW_CFG_IP6_6RD |
-+            MTK_PPE_FLOW_CFG_IP4_NAT |
-+            MTK_PPE_FLOW_CFG_IP4_NAPT |
-+            MTK_PPE_FLOW_CFG_IP4_DSLITE |
-+            MTK_PPE_FLOW_CFG_L2_BRIDGE |
-+            MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
-+      ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
-+
-+      val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) |
-+            FIELD_PREP(MTK_PPE_UNBIND_AGE_DELTA, 3);
-+      ppe_w32(ppe, MTK_PPE_UNBIND_AGE, val);
-+
-+      val = FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_UDP, 12) |
-+            FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_NON_L4, 1);
-+      ppe_w32(ppe, MTK_PPE_BIND_AGE0, val);
-+
-+      val = FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
-+            FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP, 7);
-+      ppe_w32(ppe, MTK_PPE_BIND_AGE1, val);
-+
-+      val = MTK_PPE_BIND_LIMIT0_QUARTER | MTK_PPE_BIND_LIMIT0_HALF;
-+      ppe_w32(ppe, MTK_PPE_BIND_LIMIT0, val);
-+
-+      val = MTK_PPE_BIND_LIMIT1_FULL |
-+            FIELD_PREP(MTK_PPE_BIND_LIMIT1_NON_L4, 1);
-+      ppe_w32(ppe, MTK_PPE_BIND_LIMIT1, val);
-+
-+      val = FIELD_PREP(MTK_PPE_BIND_RATE_BIND, 30) |
-+            FIELD_PREP(MTK_PPE_BIND_RATE_PREBIND, 1);
-+      ppe_w32(ppe, MTK_PPE_BIND_RATE, val);
-+
-+      /* enable PPE */
-+      val = MTK_PPE_GLO_CFG_EN |
-+            MTK_PPE_GLO_CFG_IP4_L4_CS_DROP |
-+            MTK_PPE_GLO_CFG_IP4_CS_DROP |
-+            MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE;
-+      ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
-+
-+      ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
-+
-+      return 0;
-+}
-+
-+int mtk_ppe_stop(struct mtk_ppe *ppe)
-+{
-+      u32 val;
-+      int i;
-+
-+      for (i = 0; i < MTK_PPE_ENTRIES; i++)
-+              ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
-+                                                 MTK_FOE_STATE_INVALID);
-+
-+      mtk_ppe_cache_enable(ppe, false);
-+
-+      /* disable offload engine */
-+      ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN);
-+      ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0);
-+
-+      /* disable aging */
-+      val = MTK_PPE_TB_CFG_AGE_NON_L4 |
-+            MTK_PPE_TB_CFG_AGE_UNBIND |
-+            MTK_PPE_TB_CFG_AGE_TCP |
-+            MTK_PPE_TB_CFG_AGE_UDP |
-+            MTK_PPE_TB_CFG_AGE_TCP_FIN;
-+      ppe_clear(ppe, MTK_PPE_TB_CFG, val);
-+
-+      return mtk_ppe_wait_busy(ppe);
-+}
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -0,0 +1,287 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+
-+#ifndef __MTK_PPE_H
-+#define __MTK_PPE_H
-+
-+#include <linux/kernel.h>
-+#include <linux/bitfield.h>
-+
-+#define MTK_ETH_PPE_BASE              0xc00
-+
-+#define MTK_PPE_ENTRIES_SHIFT         3
-+#define MTK_PPE_ENTRIES                       (1024 << MTK_PPE_ENTRIES_SHIFT)
-+#define MTK_PPE_HASH_MASK             (MTK_PPE_ENTRIES - 1)
-+
-+#define MTK_FOE_IB1_UNBIND_TIMESTAMP  GENMASK(7, 0)
-+#define MTK_FOE_IB1_UNBIND_PACKETS    GENMASK(23, 8)
-+#define MTK_FOE_IB1_UNBIND_PREBIND    BIT(24)
-+
-+#define MTK_FOE_IB1_BIND_TIMESTAMP    GENMASK(14, 0)
-+#define MTK_FOE_IB1_BIND_KEEPALIVE    BIT(15)
-+#define MTK_FOE_IB1_BIND_VLAN_LAYER   GENMASK(18, 16)
-+#define MTK_FOE_IB1_BIND_PPPOE                BIT(19)
-+#define MTK_FOE_IB1_BIND_VLAN_TAG     BIT(20)
-+#define MTK_FOE_IB1_BIND_PKT_SAMPLE   BIT(21)
-+#define MTK_FOE_IB1_BIND_CACHE                BIT(22)
-+#define MTK_FOE_IB1_BIND_TUNNEL_DECAP BIT(23)
-+#define MTK_FOE_IB1_BIND_TTL          BIT(24)
-+
-+#define MTK_FOE_IB1_PACKET_TYPE               GENMASK(27, 25)
-+#define MTK_FOE_IB1_STATE             GENMASK(29, 28)
-+#define MTK_FOE_IB1_UDP                       BIT(30)
-+#define MTK_FOE_IB1_STATIC            BIT(31)
-+
-+enum {
-+      MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0,
-+      MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1,
-+      MTK_PPE_PKT_TYPE_BRIDGE = 2,
-+      MTK_PPE_PKT_TYPE_IPV4_DSLITE = 3,
-+      MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T = 4,
-+      MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T = 5,
-+      MTK_PPE_PKT_TYPE_IPV6_6RD = 7,
-+};
-+
-+#define MTK_FOE_IB2_QID                       GENMASK(3, 0)
-+#define MTK_FOE_IB2_PSE_QOS           BIT(4)
-+#define MTK_FOE_IB2_DEST_PORT         GENMASK(7, 5)
-+#define MTK_FOE_IB2_MULTICAST         BIT(8)
-+
-+#define MTK_FOE_IB2_WHNAT_QID2                GENMASK(13, 12)
-+#define MTK_FOE_IB2_WHNAT_DEVIDX      BIT(16)
-+#define MTK_FOE_IB2_WHNAT_NAT         BIT(17)
-+
-+#define MTK_FOE_IB2_PORT_MG           GENMASK(17, 12)
-+
-+#define MTK_FOE_IB2_PORT_AG           GENMASK(23, 18)
-+
-+#define MTK_FOE_IB2_DSCP              GENMASK(31, 24)
-+
-+#define MTK_FOE_VLAN2_WHNAT_BSS               GEMMASK(5, 0)
-+#define MTK_FOE_VLAN2_WHNAT_WCID      GENMASK(13, 6)
-+#define MTK_FOE_VLAN2_WHNAT_RING      GENMASK(15, 14)
-+
-+enum {
-+      MTK_FOE_STATE_INVALID,
-+      MTK_FOE_STATE_UNBIND,
-+      MTK_FOE_STATE_BIND,
-+      MTK_FOE_STATE_FIN
-+};
-+
-+struct mtk_foe_mac_info {
-+      u16 vlan1;
-+      u16 etype;
-+
-+      u32 dest_mac_hi;
-+
-+      u16 vlan2;
-+      u16 dest_mac_lo;
-+
-+      u32 src_mac_hi;
-+
-+      u16 pppoe_id;
-+      u16 src_mac_lo;
-+};
-+
-+struct mtk_foe_bridge {
-+      u32 dest_mac_hi;
-+
-+      u16 src_mac_lo;
-+      u16 dest_mac_lo;
-+
-+      u32 src_mac_hi;
-+
-+      u32 ib2;
-+
-+      u32 _rsv[5];
-+
-+      u32 udf_tsid;
-+      struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_ipv4_tuple {
-+      u32 src_ip;
-+      u32 dest_ip;
-+      union {
-+              struct {
-+                      u16 dest_port;
-+                      u16 src_port;
-+              };
-+              struct {
-+                      u8 protocol;
-+                      u8 _pad[3]; /* fill with 0xa5a5a5 */
-+              };
-+              u32 ports;
-+      };
-+};
-+
-+struct mtk_foe_ipv4 {
-+      struct mtk_ipv4_tuple orig;
-+
-+      u32 ib2;
-+
-+      struct mtk_ipv4_tuple new;
-+
-+      u16 timestamp;
-+      u16 _rsv0[3];
-+
-+      u32 udf_tsid;
-+
-+      struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_foe_ipv4_dslite {
-+      struct mtk_ipv4_tuple ip4;
-+
-+      u32 tunnel_src_ip[4];
-+      u32 tunnel_dest_ip[4];
-+
-+      u8 flow_label[3];
-+      u8 priority;
-+
-+      u32 udf_tsid;
-+
-+      u32 ib2;
-+
-+      struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_foe_ipv6 {
-+      u32 src_ip[4];
-+      u32 dest_ip[4];
-+
-+      union {
-+              struct {
-+                      u8 protocol;
-+                      u8 _pad[3]; /* fill with 0xa5a5a5 */
-+              }; /* 3-tuple */
-+              struct {
-+                      u16 dest_port;
-+                      u16 src_port;
-+              }; /* 5-tuple */
-+              u32 ports;
-+      };
-+
-+      u32 _rsv[3];
-+
-+      u32 udf;
-+
-+      u32 ib2;
-+      struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_foe_ipv6_6rd {
-+      u32 src_ip[4];
-+      u32 dest_ip[4];
-+      u16 dest_port;
-+      u16 src_port;
-+
-+      u32 tunnel_src_ip;
-+      u32 tunnel_dest_ip;
-+
-+      u16 hdr_csum;
-+      u8 dscp;
-+      u8 ttl;
-+
-+      u8 flag;
-+      u8 pad;
-+      u8 per_flow_6rd_id;
-+      u8 pad2;
-+
-+      u32 ib2;
-+      struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_foe_entry {
-+      u32 ib1;
-+
-+      union {
-+              struct mtk_foe_bridge bridge;
-+              struct mtk_foe_ipv4 ipv4;
-+              struct mtk_foe_ipv4_dslite dslite;
-+              struct mtk_foe_ipv6 ipv6;
-+              struct mtk_foe_ipv6_6rd ipv6_6rd;
-+              u32 data[19];
-+      };
-+};
-+
-+enum {
-+      MTK_PPE_CPU_REASON_TTL_EXCEEDED                 = 0x02,
-+      MTK_PPE_CPU_REASON_OPTION_HEADER                = 0x03,
-+      MTK_PPE_CPU_REASON_NO_FLOW                      = 0x07,
-+      MTK_PPE_CPU_REASON_IPV4_FRAG                    = 0x08,
-+      MTK_PPE_CPU_REASON_IPV4_DSLITE_FRAG             = 0x09,
-+      MTK_PPE_CPU_REASON_IPV4_DSLITE_NO_TCP_UDP       = 0x0a,
-+      MTK_PPE_CPU_REASON_IPV6_6RD_NO_TCP_UDP          = 0x0b,
-+      MTK_PPE_CPU_REASON_TCP_FIN_SYN_RST              = 0x0c,
-+      MTK_PPE_CPU_REASON_UN_HIT                       = 0x0d,
-+      MTK_PPE_CPU_REASON_HIT_UNBIND                   = 0x0e,
-+      MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED      = 0x0f,
-+      MTK_PPE_CPU_REASON_HIT_BIND_TCP_FIN             = 0x10,
-+      MTK_PPE_CPU_REASON_HIT_TTL_1                    = 0x11,
-+      MTK_PPE_CPU_REASON_HIT_BIND_VLAN_VIOLATION      = 0x12,
-+      MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR         = 0x13,
-+      MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR         = 0x14,
-+      MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR        = 0x15,
-+      MTK_PPE_CPU_REASON_HIT_BIND_FORCE_CPU           = 0x16,
-+      MTK_PPE_CPU_REASON_TUNNEL_OPTION_HEADER         = 0x17,
-+      MTK_PPE_CPU_REASON_MULTICAST_TO_CPU             = 0x18,
-+      MTK_PPE_CPU_REASON_MULTICAST_TO_GMAC1_CPU       = 0x19,
-+      MTK_PPE_CPU_REASON_HIT_PRE_BIND                 = 0x1a,
-+      MTK_PPE_CPU_REASON_PACKET_SAMPLING              = 0x1b,
-+      MTK_PPE_CPU_REASON_EXCEED_MTU                   = 0x1c,
-+      MTK_PPE_CPU_REASON_PPE_BYPASS                   = 0x1e,
-+      MTK_PPE_CPU_REASON_INVALID                      = 0x1f,
-+};
-+
-+struct mtk_ppe {
-+      struct device *dev;
-+      void __iomem *base;
-+      int version;
-+
-+      struct mtk_foe_entry *foe_table;
-+      dma_addr_t foe_phys;
-+
-+      void *acct_table;
-+};
-+
-+int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
-+               int version);
-+int mtk_ppe_start(struct mtk_ppe *ppe);
-+int mtk_ppe_stop(struct mtk_ppe *ppe);
-+
-+static inline void
-+mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash)
-+{
-+      ppe->foe_table[hash].ib1 = 0;
-+      dma_wmb();
-+}
-+
-+static inline int
-+mtk_foe_entry_timestamp(struct mtk_ppe *ppe, u16 hash)
-+{
-+      u32 ib1 = READ_ONCE(ppe->foe_table[hash].ib1);
-+
-+      if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND)
-+              return -1;
-+
-+      return FIELD_GET(MTK_FOE_IB1_BIND_TIMESTAMP, ib1);
-+}
-+
-+int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
-+                        u8 pse_port, u8 *src_mac, u8 *dest_mac);
-+int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port);
-+int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig,
-+                               __be32 src_addr, __be16 src_port,
-+                               __be32 dest_addr, __be16 dest_port);
-+int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
-+                               __be32 *src_addr, __be16 src_port,
-+                               __be32 *dest_addr, __be16 dest_port);
-+int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
-+int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
-+int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
-+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
-+                       u16 timestamp);
-+int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
-+
-+#endif
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-@@ -0,0 +1,217 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+
-+#include <linux/kernel.h>
-+#include <linux/debugfs.h>
-+#include "mtk_eth_soc.h"
-+
-+struct mtk_flow_addr_info
-+{
-+      void *src, *dest;
-+      u16 *src_port, *dest_port;
-+      bool ipv6;
-+};
-+
-+static const char *mtk_foe_entry_state_str(int state)
-+{
-+      static const char * const state_str[] = {
-+              [MTK_FOE_STATE_INVALID] = "INV",
-+              [MTK_FOE_STATE_UNBIND] = "UNB",
-+              [MTK_FOE_STATE_BIND] = "BND",
-+              [MTK_FOE_STATE_FIN] = "FIN",
-+      };
-+
-+      if (state >= ARRAY_SIZE(state_str) || !state_str[state])
-+              return "UNK";
-+
-+      return state_str[state];
-+}
-+
-+static const char *mtk_foe_pkt_type_str(int type)
-+{
-+      static const char * const type_str[] = {
-+              [MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
-+              [MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
-+              [MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
-+              [MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
-+              [MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
-+              [MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
-+              [MTK_PPE_PKT_TYPE_IPV6_6RD] = "6RD",
-+      };
-+
-+      if (type >= ARRAY_SIZE(type_str) || !type_str[type])
-+              return "UNKNOWN";
-+
-+      return type_str[type];
-+}
-+
-+static void
-+mtk_print_addr(struct seq_file *m, u32 *addr, bool ipv6)
-+{
-+      u32 n_addr[4];
-+      int i;
-+
-+      if (!ipv6) {
-+              seq_printf(m, "%pI4h", addr);
-+              return;
-+      }
-+
-+      for (i = 0; i < ARRAY_SIZE(n_addr); i++)
-+              n_addr[i] = htonl(addr[i]);
-+      seq_printf(m, "%pI6", n_addr);
-+}
-+
-+static void
-+mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
-+{
-+      mtk_print_addr(m, ai->src, ai->ipv6);
-+      if (ai->src_port)
-+              seq_printf(m, ":%d", *ai->src_port);
-+      seq_printf(m, "->");
-+      mtk_print_addr(m, ai->dest, ai->ipv6);
-+      if (ai->dest_port)
-+              seq_printf(m, ":%d", *ai->dest_port);
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
-+{
-+      struct mtk_ppe *ppe = m->private;
-+      int i, count;
-+
-+      for (i = 0, count = 0; i < MTK_PPE_ENTRIES; i++) {
-+              struct mtk_foe_entry *entry = &ppe->foe_table[i];
-+              struct mtk_foe_mac_info *l2;
-+              struct mtk_flow_addr_info ai = {};
-+              unsigned char h_source[ETH_ALEN];
-+              unsigned char h_dest[ETH_ALEN];
-+              int type, state;
-+              u32 ib2;
-+
-+
-+              state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
-+              if (!state)
-+                      continue;
-+
-+              if (bind && state != MTK_FOE_STATE_BIND)
-+                      continue;
-+
-+              type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+              seq_printf(m, "%05x %s %7s", i,
-+                         mtk_foe_entry_state_str(state),
-+                         mtk_foe_pkt_type_str(type));
-+
-+              switch (type) {
-+              case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
-+              case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+                      ai.src_port = &entry->ipv4.orig.src_port;
-+                      ai.dest_port = &entry->ipv4.orig.dest_port;
-+                      fallthrough;
-+              case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
-+                      ai.src = &entry->ipv4.orig.src_ip;
-+                      ai.dest = &entry->ipv4.orig.dest_ip;
-+                      break;
-+              case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
-+                      ai.src_port = &entry->ipv6.src_port;
-+                      ai.dest_port = &entry->ipv6.dest_port;
-+                      fallthrough;
-+              case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
-+              case MTK_PPE_PKT_TYPE_IPV6_6RD:
-+                      ai.src = &entry->ipv6.src_ip;
-+                      ai.dest = &entry->ipv6.dest_ip;
-+                      ai.ipv6 = true;
-+                      break;
-+              }
-+
-+              seq_printf(m, " orig=");
-+              mtk_print_addr_info(m, &ai);
-+
-+              switch (type) {
-+              case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
-+              case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+                      ai.src_port = &entry->ipv4.new.src_port;
-+                      ai.dest_port = &entry->ipv4.new.dest_port;
-+                      fallthrough;
-+              case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
-+                      ai.src = &entry->ipv4.new.src_ip;
-+                      ai.dest = &entry->ipv4.new.dest_ip;
-+                      seq_printf(m, " new=");
-+                      mtk_print_addr_info(m, &ai);
-+                      break;
-+              }
-+
-+              if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
-+                      l2 = &entry->ipv6.l2;
-+                      ib2 = entry->ipv6.ib2;
-+              } else {
-+                      l2 = &entry->ipv4.l2;
-+                      ib2 = entry->ipv4.ib2;
-+              }
-+
-+              *((__be32 *)h_source) = htonl(l2->src_mac_hi);
-+              *((__be16 *)&h_source[4]) = htons(l2->src_mac_lo);
-+              *((__be32 *)h_dest) = htonl(l2->dest_mac_hi);
-+              *((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
-+
-+              seq_printf(m, " eth=%pM->%pM etype=%04x"
-+                            " vlan=%d,%d ib1=%08x ib2=%08x\n",
-+                         h_source, h_dest, ntohs(l2->etype),
-+                         l2->vlan1, l2->vlan2, entry->ib1, ib2);
-+      }
-+
-+      return 0;
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private)
-+{
-+      return mtk_ppe_debugfs_foe_show(m, private, false);
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private)
-+{
-+      return mtk_ppe_debugfs_foe_show(m, private, true);
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_open_all(struct inode *inode, struct file *file)
-+{
-+      return single_open(file, mtk_ppe_debugfs_foe_show_all,
-+                         inode->i_private);
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file)
-+{
-+      return single_open(file, mtk_ppe_debugfs_foe_show_bind,
-+                         inode->i_private);
-+}
-+
-+int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
-+{
-+      static const struct file_operations fops_all = {
-+              .open = mtk_ppe_debugfs_foe_open_all,
-+              .read = seq_read,
-+              .llseek = seq_lseek,
-+              .release = single_release,
-+      };
-+
-+      static const struct file_operations fops_bind = {
-+              .open = mtk_ppe_debugfs_foe_open_bind,
-+              .read = seq_read,
-+              .llseek = seq_lseek,
-+              .release = single_release,
-+      };
-+
-+      struct dentry *root;
-+
-+      root = debugfs_create_dir("mtk_ppe", NULL);
-+      if (!root)
-+              return -ENOMEM;
-+
-+      debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all);
-+      debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind);
-+
-+      return 0;
-+}
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-@@ -0,0 +1,144 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+
-+#ifndef __MTK_PPE_REGS_H
-+#define __MTK_PPE_REGS_H
-+
-+#define MTK_PPE_GLO_CFG                               0x200
-+#define MTK_PPE_GLO_CFG_EN                    BIT(0)
-+#define MTK_PPE_GLO_CFG_TSID_EN                       BIT(1)
-+#define MTK_PPE_GLO_CFG_IP4_L4_CS_DROP                BIT(2)
-+#define MTK_PPE_GLO_CFG_IP4_CS_DROP           BIT(3)
-+#define MTK_PPE_GLO_CFG_TTL0_DROP             BIT(4)
-+#define MTK_PPE_GLO_CFG_PPE_BSWAP             BIT(5)
-+#define MTK_PPE_GLO_CFG_PSE_HASH_OFS          BIT(6)
-+#define MTK_PPE_GLO_CFG_MCAST_TB_EN           BIT(7)
-+#define MTK_PPE_GLO_CFG_FLOW_DROP_KA          BIT(8)
-+#define MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE      BIT(9)
-+#define MTK_PPE_GLO_CFG_UDP_LITE_EN           BIT(10)
-+#define MTK_PPE_GLO_CFG_UDP_LEN_DROP          BIT(11)
-+#define MTK_PPE_GLO_CFG_MCAST_ENTRIES         GNEMASK(13, 12)
-+#define MTK_PPE_GLO_CFG_BUSY                  BIT(31)
-+
-+#define MTK_PPE_FLOW_CFG                      0x204
-+#define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG         BIT(6)
-+#define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG         BIT(7)
-+#define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE         BIT(8)
-+#define MTK_PPE_FLOW_CFG_IP6_5T_ROUTE         BIT(9)
-+#define MTK_PPE_FLOW_CFG_IP6_6RD              BIT(10)
-+#define MTK_PPE_FLOW_CFG_IP4_NAT              BIT(12)
-+#define MTK_PPE_FLOW_CFG_IP4_NAPT             BIT(13)
-+#define MTK_PPE_FLOW_CFG_IP4_DSLITE           BIT(14)
-+#define MTK_PPE_FLOW_CFG_L2_BRIDGE            BIT(15)
-+#define MTK_PPE_FLOW_CFG_IP_PROTO_BLACKLIST   BIT(16)
-+#define MTK_PPE_FLOW_CFG_IP4_NAT_FRAG         BIT(17)
-+#define MTK_PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL  BIT(18)
-+#define MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY     BIT(19)
-+#define MTK_PPE_FLOW_CFG_IP6_HASH_GRE_KEY     BIT(20)
-+
-+#define MTK_PPE_IP_PROTO_CHK                  0x208
-+#define MTK_PPE_IP_PROTO_CHK_IPV4             GENMASK(15, 0)
-+#define MTK_PPE_IP_PROTO_CHK_IPV6             GENMASK(31, 16)
-+
-+#define MTK_PPE_TB_CFG                                0x21c
-+#define MTK_PPE_TB_CFG_ENTRY_NUM              GENMASK(2, 0)
-+#define MTK_PPE_TB_CFG_ENTRY_80B              BIT(3)
-+#define MTK_PPE_TB_CFG_SEARCH_MISS            GENMASK(5, 4)
-+#define MTK_PPE_TB_CFG_AGE_PREBIND            BIT(6)
-+#define MTK_PPE_TB_CFG_AGE_NON_L4             BIT(7)
-+#define MTK_PPE_TB_CFG_AGE_UNBIND             BIT(8)
-+#define MTK_PPE_TB_CFG_AGE_TCP                        BIT(9)
-+#define MTK_PPE_TB_CFG_AGE_UDP                        BIT(10)
-+#define MTK_PPE_TB_CFG_AGE_TCP_FIN            BIT(11)
-+#define MTK_PPE_TB_CFG_KEEPALIVE              GENMASK(13, 12)
-+#define MTK_PPE_TB_CFG_HASH_MODE              GENMASK(15, 14)
-+#define MTK_PPE_TB_CFG_SCAN_MODE              GENMASK(17, 16)
-+#define MTK_PPE_TB_CFG_HASH_DEBUG             GENMASK(19, 18)
-+
-+enum {
-+      MTK_PPE_SCAN_MODE_DISABLED,
-+      MTK_PPE_SCAN_MODE_CHECK_AGE,
-+      MTK_PPE_SCAN_MODE_KEEPALIVE_AGE,
-+};
-+
-+enum {
-+      MTK_PPE_KEEPALIVE_DISABLE,
-+      MTK_PPE_KEEPALIVE_UNICAST_CPU,
-+      MTK_PPE_KEEPALIVE_DUP_CPU = 3,
-+};
-+
-+enum {
-+      MTK_PPE_SEARCH_MISS_ACTION_DROP,
-+      MTK_PPE_SEARCH_MISS_ACTION_FORWARD = 2,
-+      MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD = 3,
-+};
-+
-+#define MTK_PPE_TB_BASE                               0x220
-+
-+#define MTK_PPE_TB_USED                               0x224
-+#define MTK_PPE_TB_USED_NUM                   GENMASK(13, 0)
-+
-+#define MTK_PPE_BIND_RATE                     0x228
-+#define MTK_PPE_BIND_RATE_BIND                        GENMASK(15, 0)
-+#define MTK_PPE_BIND_RATE_PREBIND             GENMASK(31, 16)
-+
-+#define MTK_PPE_BIND_LIMIT0                   0x22c
-+#define MTK_PPE_BIND_LIMIT0_QUARTER           GENMASK(13, 0)
-+#define MTK_PPE_BIND_LIMIT0_HALF              GENMASK(29, 16)
-+
-+#define MTK_PPE_BIND_LIMIT1                   0x230
-+#define MTK_PPE_BIND_LIMIT1_FULL              GENMASK(13, 0)
-+#define MTK_PPE_BIND_LIMIT1_NON_L4            GENMASK(23, 16)
-+
-+#define MTK_PPE_KEEPALIVE                     0x234
-+#define MTK_PPE_KEEPALIVE_TIME                        GENMASK(15, 0)
-+#define MTK_PPE_KEEPALIVE_TIME_TCP            GENMASK(23, 16)
-+#define MTK_PPE_KEEPALIVE_TIME_UDP            GENMASK(31, 24)
-+
-+#define MTK_PPE_UNBIND_AGE                    0x238
-+#define MTK_PPE_UNBIND_AGE_MIN_PACKETS                GENMASK(31, 16)
-+#define MTK_PPE_UNBIND_AGE_DELTA              GENMASK(7, 0)
-+
-+#define MTK_PPE_BIND_AGE0                     0x23c
-+#define MTK_PPE_BIND_AGE0_DELTA_NON_L4                GENMASK(30, 16)
-+#define MTK_PPE_BIND_AGE0_DELTA_UDP           GENMASK(14, 0)
-+
-+#define MTK_PPE_BIND_AGE1                     0x240
-+#define MTK_PPE_BIND_AGE1_DELTA_TCP_FIN               GENMASK(30, 16)
-+#define MTK_PPE_BIND_AGE1_DELTA_TCP           GENMASK(14, 0)
-+
-+#define MTK_PPE_HASH_SEED                     0x244
-+
-+#define MTK_PPE_DEFAULT_CPU_PORT              0x248
-+#define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n)     (GENMASK(2, 0) << ((_n) * 4))
-+
-+#define MTK_PPE_MTU_DROP                      0x308
-+
-+#define MTK_PPE_VLAN_MTU0                     0x30c
-+#define MTK_PPE_VLAN_MTU0_NONE                        GENMASK(13, 0)
-+#define MTK_PPE_VLAN_MTU0_1TAG                        GENMASK(29, 16)
-+
-+#define MTK_PPE_VLAN_MTU1                     0x310
-+#define MTK_PPE_VLAN_MTU1_2TAG                        GENMASK(13, 0)
-+#define MTK_PPE_VLAN_MTU1_3TAG                        GENMASK(29, 16)
-+
-+#define MTK_PPE_VPM_TPID                      0x318
-+
-+#define MTK_PPE_CACHE_CTL                     0x320
-+#define MTK_PPE_CACHE_CTL_EN                  BIT(0)
-+#define MTK_PPE_CACHE_CTL_LOCK_CLR            BIT(4)
-+#define MTK_PPE_CACHE_CTL_REQ                 BIT(8)
-+#define MTK_PPE_CACHE_CTL_CLEAR                       BIT(9)
-+#define MTK_PPE_CACHE_CTL_CMD                 GENMASK(13, 12)
-+
-+#define MTK_PPE_MIB_CFG                               0x334
-+#define MTK_PPE_MIB_CFG_EN                    BIT(0)
-+#define MTK_PPE_MIB_CFG_RD_CLR                        BIT(1)
-+
-+#define MTK_PPE_MIB_TB_BASE                   0x338
-+
-+#define MTK_PPE_MIB_CACHE_CTL                 0x350
-+#define MTK_PPE_MIB_CACHE_CTL_EN              BIT(0)
-+#define MTK_PPE_MIB_CACHE_CTL_FLUSH           BIT(2)
-+
-+#endif
diff --git a/target/linux/generic/pending-5.10/770-16-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch b/target/linux/generic/pending-5.10/770-16-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch
deleted file mode 100644 (file)
index 2f1ae6c..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 10 Dec 2020 12:19:18 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: add flow offloading support
-
-This adds support for offloading IPv4 routed flows, including SNAT/DNAT,
-one VLAN, and DSA.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -4,5 +4,5 @@
- #
- obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
--mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o
-+mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
- obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -2896,6 +2896,7 @@ static const struct net_device_ops mtk_n
- #ifdef CONFIG_NET_POLL_CONTROLLER
-       .ndo_poll_controller    = mtk_poll_controller,
- #endif
-+      .ndo_setup_tc           = mtk_eth_setup_tc,
- };
- static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
-@@ -3161,6 +3162,10 @@ static int mtk_probe(struct platform_dev
-                                  eth->base + MTK_ETH_PPE_BASE, 2);
-               if (err)
-                       goto err_free_dev;
-+
-+              err = mtk_eth_offload_init(eth);
-+              if (err)
-+                      goto err_free_dev;
-       }
-       for (i = 0; i < MTK_MAX_DEVS; i++) {
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -16,6 +16,7 @@
- #include <linux/refcount.h>
- #include <linux/phylink.h>
- #include <linux/dim.h>
-+#include <linux/rhashtable.h>
- #include "mtk_ppe.h"
- #define MTK_QDMA_PAGE_SIZE    2048
-@@ -41,7 +42,8 @@
-                                NETIF_F_HW_VLAN_CTAG_RX | \
-                                NETIF_F_SG | NETIF_F_TSO | \
-                                NETIF_F_TSO6 | \
--                               NETIF_F_IPV6_CSUM)
-+                               NETIF_F_IPV6_CSUM |\
-+                               NETIF_F_HW_TC)
- #define MTK_HW_FEATURES_MT7628        (NETIF_F_SG | NETIF_F_RXCSUM)
- #define NEXT_DESP_IDX(X, Y)   (((X) + 1) & ((Y) - 1))
-@@ -929,6 +931,7 @@ struct mtk_eth {
-       int                             ip_align;
-       struct mtk_ppe                  ppe;
-+      struct rhashtable               flow_table;
- };
- /* struct mtk_mac -   the structure that holds the info about the MACs of the
-@@ -973,4 +976,9 @@ int mtk_gmac_sgmii_path_setup(struct mtk
- int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
- int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
-+int mtk_eth_offload_init(struct mtk_eth *eth);
-+int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
-+                   void *type_data);
-+
-+
- #endif /* MTK_ETH_H */
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -0,0 +1,491 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ *  Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
-+ */
-+
-+#include <linux/if_ether.h>
-+#include <linux/rhashtable.h>
-+#include <linux/if_ether.h>
-+#include <linux/ip.h>
-+#include <net/flow_offload.h>
-+#include <net/pkt_cls.h>
-+#include <net/dsa.h>
-+#include "mtk_eth_soc.h"
-+
-+struct mtk_flow_data {
-+      struct ethhdr eth;
-+
-+      union {
-+              struct {
-+                      __be32 src_addr;
-+                      __be32 dst_addr;
-+              } v4;
-+      };
-+
-+      __be16 src_port;
-+      __be16 dst_port;
-+
-+      struct {
-+              u16 id;
-+              __be16 proto;
-+              u8 num;
-+      } vlan;
-+      struct {
-+              u16 sid;
-+              u8 num;
-+      } pppoe;
-+};
-+
-+struct mtk_flow_entry {
-+      struct rhash_head node;
-+      unsigned long cookie;
-+      u16 hash;
-+};
-+
-+static const struct rhashtable_params mtk_flow_ht_params = {
-+      .head_offset = offsetof(struct mtk_flow_entry, node),
-+      .head_offset = offsetof(struct mtk_flow_entry, cookie),
-+      .key_len = sizeof(unsigned long),
-+      .automatic_shrinking = true,
-+};
-+
-+static u32
-+mtk_eth_timestamp(struct mtk_eth *eth)
-+{
-+      return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
-+}
-+
-+static int
-+mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
-+                     bool egress)
-+{
-+      return mtk_foe_entry_set_ipv4_tuple(foe, egress,
-+                                          data->v4.src_addr, data->src_port,
-+                                          data->v4.dst_addr, data->dst_port);
-+}
-+
-+static void
-+mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
-+{
-+      void *dest = eth + act->mangle.offset;
-+      const void *src = &act->mangle.val;
-+
-+      if (act->mangle.offset > 8)
-+              return;
-+
-+      if (act->mangle.mask == 0xffff) {
-+              src += 2;
-+              dest += 2;
-+      }
-+
-+      memcpy(dest, src, act->mangle.mask ? 2 : 4);
-+}
-+
-+
-+static int
-+mtk_flow_mangle_ports(const struct flow_action_entry *act,
-+                    struct mtk_flow_data *data)
-+{
-+      u32 val = ntohl(act->mangle.val);
-+
-+      switch (act->mangle.offset) {
-+      case 0:
-+              if (act->mangle.mask == ~htonl(0xffff))
-+                      data->dst_port = cpu_to_be16(val);
-+              else
-+                      data->src_port = cpu_to_be16(val >> 16);
-+              break;
-+      case 2:
-+              data->dst_port = cpu_to_be16(val);
-+              break;
-+      default:
-+              return -EINVAL;
-+      }
-+
-+      return 0;
-+}
-+
-+static int
-+mtk_flow_mangle_ipv4(const struct flow_action_entry *act,
-+                   struct mtk_flow_data *data)
-+{
-+      __be32 *dest;
-+
-+      switch (act->mangle.offset) {
-+      case offsetof(struct iphdr, saddr):
-+              dest = &data->v4.src_addr;
-+              break;
-+      case offsetof(struct iphdr, daddr):
-+              dest = &data->v4.dst_addr;
-+              break;
-+      default:
-+              return -EINVAL;
-+      }
-+
-+      memcpy(dest, &act->mangle.val, sizeof(u32));
-+
-+      return 0;
-+}
-+
-+static int
-+mtk_flow_get_dsa_port(struct net_device **dev)
-+{
-+#if IS_ENABLED(CONFIG_NET_DSA)
-+      struct dsa_port *dp;
-+
-+      dp = dsa_port_from_netdev(*dev);
-+      if (IS_ERR(dp))
-+              return -ENODEV;
-+
-+      if (!dp->cpu_dp)
-+              return -ENODEV;
-+
-+      if (!dp->cpu_dp->tag_ops)
-+              return -ENODEV;
-+
-+      if (dp->cpu_dp->tag_ops->proto != DSA_TAG_PROTO_MTK)
-+              return -ENODEV;
-+
-+      *dev = dp->cpu_dp->master;
-+
-+      return dp->index;
-+#else
-+      return -ENODEV;
-+#endif
-+}
-+
-+static int
-+mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
-+                         struct net_device *dev)
-+{
-+      int pse_port, dsa_port;
-+
-+      dsa_port = mtk_flow_get_dsa_port(&dev);
-+      if (dsa_port >= 0)
-+              mtk_foe_entry_set_dsa(foe, dsa_port);
-+
-+      if (dev == eth->netdev[0])
-+              pse_port = 1;
-+      else if (dev == eth->netdev[1])
-+              pse_port = 2;
-+      else
-+              return -EOPNOTSUPP;
-+
-+      mtk_foe_entry_set_pse_port(foe, pse_port);
-+
-+      return 0;
-+}
-+
-+static int
-+mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
-+{
-+      struct flow_rule *rule = flow_cls_offload_flow_rule(f);
-+      struct flow_action_entry *act;
-+      struct mtk_flow_data data = {};
-+      struct mtk_foe_entry foe;
-+      struct net_device *odev = NULL;
-+      struct mtk_flow_entry *entry;
-+      int offload_type = 0;
-+      u16 addr_type = 0;
-+      u32 timestamp;
-+      u8 l4proto = 0;
-+      int err = 0;
-+      int hash;
-+      int i;
-+
-+      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
-+              struct flow_match_meta match;
-+
-+              flow_rule_match_meta(rule, &match);
-+      } else {
-+              return -EOPNOTSUPP;
-+      }
-+
-+      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
-+              struct flow_match_control match;
-+
-+              flow_rule_match_control(rule, &match);
-+              addr_type = match.key->addr_type;
-+      } else {
-+              return -EOPNOTSUPP;
-+      }
-+
-+      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
-+              struct flow_match_basic match;
-+
-+              flow_rule_match_basic(rule, &match);
-+              l4proto = match.key->ip_proto;
-+      } else {
-+              return -EOPNOTSUPP;
-+      }
-+
-+      flow_action_for_each(i, act, &rule->action) {
-+              switch (act->id) {
-+              case FLOW_ACTION_MANGLE:
-+                      if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
-+                              mtk_flow_offload_mangle_eth(act, &data.eth);
-+                      break;
-+              case FLOW_ACTION_REDIRECT:
-+                      odev = act->dev;
-+                      break;
-+              case FLOW_ACTION_CSUM:
-+                      break;
-+              case FLOW_ACTION_VLAN_PUSH:
-+                      if (data.vlan.num == 1 ||
-+                          act->vlan.proto != htons(ETH_P_8021Q))
-+                              return -EOPNOTSUPP;
-+
-+                      data.vlan.id = act->vlan.vid;
-+                      data.vlan.proto = act->vlan.proto;
-+                      data.vlan.num++;
-+                      break;
-+              case FLOW_ACTION_PPPOE_PUSH:
-+                      if (data.pppoe.num == 1)
-+                              return -EOPNOTSUPP;
-+
-+                      data.pppoe.sid = act->pppoe.sid;
-+                      data.pppoe.num++;
-+                      break;
-+              default:
-+                      return -EOPNOTSUPP;
-+              }
-+      }
-+
-+      switch (addr_type) {
-+      case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
-+              offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
-+              break;
-+      default:
-+              return -EOPNOTSUPP;
-+      }
-+
-+      if (!is_valid_ether_addr(data.eth.h_source) ||
-+          !is_valid_ether_addr(data.eth.h_dest))
-+              return -EINVAL;
-+
-+      err = mtk_foe_entry_prepare(&foe, offload_type, l4proto, 0,
-+                                  data.eth.h_source,
-+                                  data.eth.h_dest);
-+      if (err)
-+              return err;
-+
-+      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
-+              struct flow_match_ports ports;
-+
-+              flow_rule_match_ports(rule, &ports);
-+              data.src_port = ports.key->src;
-+              data.dst_port = ports.key->dst;
-+      } else {
-+              return -EOPNOTSUPP;
-+      }
-+
-+      if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-+              struct flow_match_ipv4_addrs addrs;
-+
-+              flow_rule_match_ipv4_addrs(rule, &addrs);
-+
-+              data.v4.src_addr = addrs.key->src;
-+              data.v4.dst_addr = addrs.key->dst;
-+
-+              mtk_flow_set_ipv4_addr(&foe, &data, false);
-+      }
-+
-+      flow_action_for_each(i, act, &rule->action) {
-+              if (act->id != FLOW_ACTION_MANGLE)
-+                      continue;
-+
-+              switch (act->mangle.htype) {
-+              case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
-+              case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
-+                      err = mtk_flow_mangle_ports(act, &data);
-+                      break;
-+              case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
-+                      err = mtk_flow_mangle_ipv4(act, &data);
-+                      break;
-+              case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
-+                      /* handled earlier */
-+                      break;
-+              default:
-+                      return -EOPNOTSUPP;
-+              }
-+
-+              if (err)
-+                      return err;
-+      }
-+
-+      if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-+              err = mtk_flow_set_ipv4_addr(&foe, &data, true);
-+              if (err)
-+                      return err;
-+      }
-+
-+      if (data.vlan.num == 1) {
-+              if (data.vlan.proto != htons(ETH_P_8021Q))
-+                      return -EOPNOTSUPP;
-+
-+              mtk_foe_entry_set_vlan(&foe, data.vlan.id);
-+      }
-+      if (data.pppoe.num == 1)
-+              mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
-+
-+      err = mtk_flow_set_output_device(eth, &foe, odev);
-+      if (err)
-+              return err;
-+
-+      entry = kzalloc(sizeof(*entry), GFP_KERNEL);
-+      if (!entry)
-+              return -ENOMEM;
-+
-+      entry->cookie = f->cookie;
-+      timestamp = mtk_eth_timestamp(eth);
-+      hash = mtk_foe_entry_commit(&eth->ppe, &foe, timestamp);
-+      if (hash < 0) {
-+              err = hash;
-+              goto free;
-+      }
-+
-+      entry->hash = hash;
-+      err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
-+                                   mtk_flow_ht_params);
-+      if (err < 0)
-+              goto clear_flow;
-+
-+      return 0;
-+clear_flow:
-+      mtk_foe_entry_clear(&eth->ppe, hash);
-+free:
-+      kfree(entry);
-+      return err;
-+}
-+
-+static int
-+mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
-+{
-+      struct mtk_flow_entry *entry;
-+
-+      entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
-+                                mtk_flow_ht_params);
-+      if (!entry)
-+              return -ENOENT;
-+
-+      mtk_foe_entry_clear(&eth->ppe, entry->hash);
-+      rhashtable_remove_fast(&eth->flow_table, &entry->node,
-+                             mtk_flow_ht_params);
-+      kfree(entry);
-+
-+      return 0;
-+}
-+
-+static int
-+mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
-+{
-+      struct mtk_flow_entry *entry;
-+      int timestamp;
-+      u32 idle;
-+
-+      entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
-+                                mtk_flow_ht_params);
-+      if (!entry)
-+              return -ENOENT;
-+
-+      timestamp = mtk_foe_entry_timestamp(&eth->ppe, entry->hash);
-+      if (timestamp < 0)
-+              return -ETIMEDOUT;
-+
-+      idle = mtk_eth_timestamp(eth) - timestamp;
-+      f->stats.lastused = jiffies - idle * HZ;
-+
-+      return 0;
-+}
-+
-+static int
-+mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
-+{
-+      struct flow_cls_offload *cls = type_data;
-+      struct net_device *dev = cb_priv;
-+      struct mtk_mac *mac = netdev_priv(dev);
-+      struct mtk_eth *eth = mac->hw;
-+
-+      if (!tc_can_offload(dev))
-+              return -EOPNOTSUPP;
-+
-+      if (type != TC_SETUP_CLSFLOWER)
-+              return -EOPNOTSUPP;
-+
-+      switch (cls->command) {
-+      case FLOW_CLS_REPLACE:
-+              return mtk_flow_offload_replace(eth, cls);
-+      case FLOW_CLS_DESTROY:
-+              return mtk_flow_offload_destroy(eth, cls);
-+      case FLOW_CLS_STATS:
-+              return mtk_flow_offload_stats(eth, cls);
-+      default:
-+              return -EOPNOTSUPP;
-+      }
-+
-+      return 0;
-+}
-+
-+static int
-+mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
-+{
-+      struct mtk_mac *mac = netdev_priv(dev);
-+      struct mtk_eth *eth = mac->hw;
-+      static LIST_HEAD(block_cb_list);
-+      struct flow_block_cb *block_cb;
-+      flow_setup_cb_t *cb;
-+
-+      if (!eth->ppe.foe_table)
-+              return -EOPNOTSUPP;
-+
-+      if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
-+              return -EOPNOTSUPP;
-+
-+      cb = mtk_eth_setup_tc_block_cb;
-+      f->driver_block_list = &block_cb_list;
-+
-+      switch (f->command) {
-+      case FLOW_BLOCK_BIND:
-+              block_cb = flow_block_cb_lookup(f->block, cb, dev);
-+              if (block_cb) {
-+                      flow_block_cb_incref(block_cb);
-+                      return 0;
-+              }
-+              block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
-+              if (IS_ERR(block_cb))
-+                      return PTR_ERR(block_cb);
-+
-+              flow_block_cb_add(block_cb, f);
-+              list_add_tail(&block_cb->driver_list, &block_cb_list);
-+              return 0;
-+      case FLOW_BLOCK_UNBIND:
-+              block_cb = flow_block_cb_lookup(f->block, cb, dev);
-+              if (!block_cb)
-+                      return -ENOENT;
-+
-+              if (flow_block_cb_decref(block_cb)) {
-+                      flow_block_cb_remove(block_cb, f);
-+                      list_del(&block_cb->driver_list);
-+              }
-+              return 0;
-+      default:
-+              return -EOPNOTSUPP;
-+      }
-+}
-+
-+int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
-+                   void *type_data)
-+{
-+      if (type == TC_SETUP_FT)
-+              return mtk_eth_setup_tc_block(dev, type_data);
-+
-+      return -EOPNOTSUPP;
-+}
-+
-+int mtk_eth_offload_init(struct mtk_eth *eth)
-+{
-+      if (!eth->ppe.foe_table)
-+              return 0;
-+
-+      return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
-+}