kernel: update 5.10 flow offload patches
authorFelix Fietkau <nbd@nbd.name>
Mon, 8 Mar 2021 16:20:20 +0000 (17:20 +0100)
committerFelix Fietkau <nbd@nbd.name>
Fri, 12 Mar 2021 19:23:52 +0000 (20:23 +0100)
Includes PPPoE support and VLAN related fixes

Signed-off-by: Felix Fietkau <nbd@nbd.name>
31 files changed:
target/linux/generic/hack-5.10/650-netfilter-add-xt_FLOWOFFLOAD-target.patch [new file with mode: 0644]
target/linux/generic/hack-5.10/650-netfilter-add-xt_OFFLOAD-target.patch [deleted file]
target/linux/generic/pending-5.10/640-02-net-resolve-forwarding-path-from-virtual-netdevice-a.patch
target/linux/generic/pending-5.10/640-03-net-8021q-resolve-forwarding-path-for-vlan-devices.patch
target/linux/generic/pending-5.10/640-05-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch
target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch
target/linux/generic/pending-5.10/640-07-netfilter-flowtable-add-vlan-support.patch
target/linux/generic/pending-5.10/640-09-net-bridge-resolve-VLAN-tag-actions-in-forwarding-pa.patch
target/linux/generic/pending-5.10/640-10-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch [deleted file]
target/linux/generic/pending-5.10/640-10-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-11-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-11-netfilter-flowtable-add-offload-support-for-xmit-pat.patch [deleted file]
target/linux/generic/pending-5.10/640-12-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-12-netfilter-nft_flow_offload-add-dsa-support.patch [deleted file]
target/linux/generic/pending-5.10/640-13-dsa-slave-add-support-for-TC_SETUP_FT.patch [deleted file]
target/linux/generic/pending-5.10/640-13-netfilter-flowtable-add-pppoe-support.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch [deleted file]
target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-dsa-support.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-15-netfilter-flowtable-add-offload-support-for-xmit-pat.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-15-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch [deleted file]
target/linux/generic/pending-5.10/640-16-dsa-slave-add-support-for-TC_SETUP_FT.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-16-netfilter-nft_flow_offload-fix-bridge-vlan-tag-handl.patch [deleted file]
target/linux/generic/pending-5.10/640-17-netfilter-flowtable-rework-ingress-vlan-matching.patch [deleted file]
target/linux/generic/pending-5.10/640-17-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-18-netfilter-flowtable-handle-bridge-vlan-filter-offloa.patch [deleted file]
target/linux/generic/pending-5.10/640-18-netfilter-nf_flow_table-fix-untagging-with-hardware-.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-19-net-flow_offload-add-FLOW_ACTION_PPPOE_PUSH.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/640-20-netfilter-flowtable-support-for-FLOW_ACTION_PPPOE_PU.patch [new file with mode: 0644]
target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
target/linux/generic/pending-5.10/690-net-add-support-for-threaded-NAPI-polling.patch
target/linux/generic/pending-5.10/770-16-net-ethernet-mtk_eth_soc-add-flow-offloading-support.patch

diff --git a/target/linux/generic/hack-5.10/650-netfilter-add-xt_FLOWOFFLOAD-target.patch b/target/linux/generic/hack-5.10/650-netfilter-add-xt_FLOWOFFLOAD-target.patch
new file mode 100644 (file)
index 0000000..97aa7a6
--- /dev/null
@@ -0,0 +1,820 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 20 Feb 2018 15:56:02 +0100
+Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ create mode 100644 net/netfilter/xt_OFFLOAD.c
+
+--- a/net/ipv4/netfilter/Kconfig
++++ b/net/ipv4/netfilter/Kconfig
+@@ -56,8 +56,6 @@ config NF_TABLES_ARP
+       help
+         This option enables the ARP support for nf_tables.
+-endif # NF_TABLES
+-
+ config NF_FLOW_TABLE_IPV4
+       tristate "Netfilter flow table IPv4 module"
+       depends on NF_FLOW_TABLE
+@@ -66,6 +64,8 @@ config NF_FLOW_TABLE_IPV4
+         To compile it as a module, choose M here.
++endif # NF_TABLES
++
+ config NF_DUP_IPV4
+       tristate "Netfilter IPv4 packet duplication to alternate destination"
+       depends on !NF_CONNTRACK || NF_CONNTRACK
+--- a/net/ipv6/netfilter/Kconfig
++++ b/net/ipv6/netfilter/Kconfig
+@@ -45,7 +45,6 @@ config NFT_FIB_IPV6
+         multicast or blackhole.
+ endif # NF_TABLES_IPV6
+-endif # NF_TABLES
+ config NF_FLOW_TABLE_IPV6
+       tristate "Netfilter flow table IPv6 module"
+@@ -55,6 +54,8 @@ config NF_FLOW_TABLE_IPV6
+         To compile it as a module, choose M here.
++endif # NF_TABLES
++
+ config NF_DUP_IPV6
+       tristate "Netfilter IPv6 packet duplication to alternate destination"
+       depends on !NF_CONNTRACK || NF_CONNTRACK
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -683,8 +683,6 @@ config NFT_FIB_NETDEV
+ endif # NF_TABLES_NETDEV
+-endif # NF_TABLES
+-
+ config NF_FLOW_TABLE_INET
+       tristate "Netfilter flow table mixed IPv4/IPv6 module"
+       depends on NF_FLOW_TABLE
+@@ -693,11 +691,12 @@ config NF_FLOW_TABLE_INET
+         To compile it as a module, choose M here.
++endif # NF_TABLES
++
+ config NF_FLOW_TABLE
+       tristate "Netfilter flow table module"
+       depends on NETFILTER_INGRESS
+       depends on NF_CONNTRACK
+-      depends on NF_TABLES
+       help
+         This option adds the flow table core infrastructure.
+@@ -977,6 +976,15 @@ config NETFILTER_XT_TARGET_NOTRACK
+       depends on NETFILTER_ADVANCED
+       select NETFILTER_XT_TARGET_CT
++config NETFILTER_XT_TARGET_FLOWOFFLOAD
++      tristate '"FLOWOFFLOAD" target support'
++      depends on NF_FLOW_TABLE
++      depends on NETFILTER_INGRESS
++      help
++        This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload
++        module to speed up processing of packets by bypassing the usual
++        netfilter chains
++
+ config NETFILTER_XT_TARGET_RATEEST
+       tristate '"RATEEST" target support'
+       depends on NETFILTER_ADVANCED
+--- a/net/netfilter/Makefile
++++ b/net/netfilter/Makefile
+@@ -145,6 +145,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF
+ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
++obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
+--- /dev/null
++++ b/net/netfilter/xt_FLOWOFFLOAD.c
+@@ -0,0 +1,658 @@
++/*
++ * Copyright (C) 2018-2021 Felix Fietkau <nbd@nbd.name>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/netfilter.h>
++#include <linux/netfilter/xt_FLOWOFFLOAD.h>
++#include <net/ip.h>
++#include <net/netfilter/nf_conntrack.h>
++#include <net/netfilter/nf_conntrack_extend.h>
++#include <net/netfilter/nf_conntrack_helper.h>
++#include <net/netfilter/nf_flow_table.h>
++
++struct xt_flowoffload_hook {
++      struct hlist_node list;
++      struct nf_hook_ops ops;
++      struct net *net;
++      bool registered;
++      bool used;
++};
++
++struct xt_flowoffload_table {
++      struct nf_flowtable ft;
++      struct hlist_head hooks;
++      struct delayed_work work;
++};
++
++static DEFINE_SPINLOCK(hooks_lock);
++
++struct xt_flowoffload_table flowtable[2];
++
++static unsigned int
++xt_flowoffload_net_hook(void *priv, struct sk_buff *skb,
++                      const struct nf_hook_state *state)
++{
++      struct nf_flowtable *ft = priv;
++
++      if (!atomic_read(&ft->rhashtable.nelems))
++              return NF_ACCEPT;
++
++      switch (skb->protocol) {
++      case htons(ETH_P_IP):
++              return nf_flow_offload_ip_hook(priv, skb, state);
++      case htons(ETH_P_IPV6):
++              return nf_flow_offload_ipv6_hook(priv, skb, state);
++      }
++
++      return NF_ACCEPT;
++}
++
++static int
++xt_flowoffload_create_hook(struct xt_flowoffload_table *table,
++                         struct net_device *dev)
++{
++      struct xt_flowoffload_hook *hook;
++      struct nf_hook_ops *ops;
++
++      hook = kzalloc(sizeof(*hook), GFP_ATOMIC);
++      if (!hook)
++              return -ENOMEM;
++
++      ops = &hook->ops;
++      ops->pf = NFPROTO_NETDEV;
++      ops->hooknum = NF_NETDEV_INGRESS;
++      ops->priority = 10;
++      ops->priv = &table->ft;
++      ops->hook = xt_flowoffload_net_hook;
++      ops->dev = dev;
++
++      hlist_add_head(&hook->list, &table->hooks);
++      mod_delayed_work(system_power_efficient_wq, &table->work, 0);
++
++      return 0;
++}
++
++static struct xt_flowoffload_hook *
++flow_offload_lookup_hook(struct xt_flowoffload_table *table,
++                       struct net_device *dev)
++{
++      struct xt_flowoffload_hook *hook;
++
++      hlist_for_each_entry(hook, &table->hooks, list) {
++              if (hook->ops.dev == dev)
++                      return hook;
++      }
++
++      return NULL;
++}
++
++static void
++xt_flowoffload_check_device(struct xt_flowoffload_table *table,
++                          struct net_device *dev)
++{
++      struct xt_flowoffload_hook *hook;
++
++      if (!dev)
++              return;
++
++      spin_lock_bh(&hooks_lock);
++      hook = flow_offload_lookup_hook(table, dev);
++      if (hook)
++              hook->used = true;
++      else
++              xt_flowoffload_create_hook(table, dev);
++      spin_unlock_bh(&hooks_lock);
++}
++
++static void
++xt_flowoffload_register_hooks(struct xt_flowoffload_table *table)
++{
++      struct xt_flowoffload_hook *hook;
++
++restart:
++      hlist_for_each_entry(hook, &table->hooks, list) {
++              if (hook->registered)
++                      continue;
++
++              hook->registered = true;
++              hook->net = dev_net(hook->ops.dev);
++              spin_unlock_bh(&hooks_lock);
++              nf_register_net_hook(hook->net, &hook->ops);
++              if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
++                      table->ft.type->setup(&table->ft, hook->ops.dev,
++                                            FLOW_BLOCK_BIND);
++              spin_lock_bh(&hooks_lock);
++              goto restart;
++      }
++
++}
++
++static bool
++xt_flowoffload_cleanup_hooks(struct xt_flowoffload_table *table)
++{
++      struct xt_flowoffload_hook *hook;
++      bool active = false;
++
++restart:
++      spin_lock_bh(&hooks_lock);
++      hlist_for_each_entry(hook, &table->hooks, list) {
++              if (hook->used || !hook->registered) {
++                      active = true;
++                      continue;
++              }
++
++              hlist_del(&hook->list);
++              spin_unlock_bh(&hooks_lock);
++              if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
++                      table->ft.type->setup(&table->ft, hook->ops.dev,
++                                            FLOW_BLOCK_UNBIND);
++              nf_unregister_net_hook(hook->net, &hook->ops);
++              kfree(hook);
++              goto restart;
++      }
++      spin_unlock_bh(&hooks_lock);
++
++      return active;
++}
++
++static void
++xt_flowoffload_check_hook(struct flow_offload *flow, void *data)
++{
++      struct xt_flowoffload_table *table = data;
++      struct flow_offload_tuple *tuple0 = &flow->tuplehash[0].tuple;
++      struct flow_offload_tuple *tuple1 = &flow->tuplehash[1].tuple;
++      struct xt_flowoffload_hook *hook;
++
++      spin_lock_bh(&hooks_lock);
++      hlist_for_each_entry(hook, &table->hooks, list) {
++              if (hook->ops.dev->ifindex != tuple0->iifidx &&
++                  hook->ops.dev->ifindex != tuple1->iifidx)
++                      continue;
++
++              hook->used = true;
++      }
++      spin_unlock_bh(&hooks_lock);
++
++      cond_resched();
++}
++
++static void
++xt_flowoffload_hook_work(struct work_struct *work)
++{
++      struct xt_flowoffload_table *table;
++      struct xt_flowoffload_hook *hook;
++      int err;
++
++      table = container_of(work, struct xt_flowoffload_table, work.work);
++
++      spin_lock_bh(&hooks_lock);
++      xt_flowoffload_register_hooks(table);
++      hlist_for_each_entry(hook, &table->hooks, list)
++              hook->used = false;
++      spin_unlock_bh(&hooks_lock);
++
++      err = nf_flow_table_iterate(&table->ft, xt_flowoffload_check_hook,
++                                  table);
++      if (err && err != -EAGAIN)
++              goto out;
++
++      if (!xt_flowoffload_cleanup_hooks(table))
++              return;
++
++out:
++      queue_delayed_work(system_power_efficient_wq, &table->work, HZ);
++}
++
++static bool
++xt_flowoffload_skip(struct sk_buff *skb, int family)
++{
++      if (skb_sec_path(skb))
++              return true;
++
++      if (family == NFPROTO_IPV4) {
++              const struct ip_options *opt = &(IPCB(skb)->opt);
++
++              if (unlikely(opt->optlen))
++                      return true;
++      }
++
++      return false;
++}
++
++static bool flow_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
++xt_flowoffload_route_check_path(struct nf_flow_route *route,
++                              const struct nf_conn *ct,
++                              enum ip_conntrack_dir dir,
++                              struct net_device **out_dev)
++{
++      const struct dst_entry *dst = route->tuple[dir].dst;
++      const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
++      struct net_device_path_stack stack;
++      enum net_device_path_type prev_type;
++      struct net_device *dev = dst->dev;
++      struct neighbour *n;
++      bool last = false;
++      u8 nud_state;
++      int i;
++
++      route->tuple[!dir].in.ifindex = dev->ifindex;
++      route->tuple[dir].out.ifindex = dev->ifindex;
++
++      if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
++              return;
++
++      if ((dev->flags & IFF_LOOPBACK) ||
++          dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||
++          !is_valid_ether_addr(dev->dev_addr))
++              return;
++
++      n = dst_neigh_lookup(dst, daddr);
++      if (!n)
++              return;
++
++      read_lock_bh(&n->lock);
++      nud_state = n->nud_state;
++      memcpy(route->tuple[dir].out.h_dest, n->ha, ETH_ALEN);
++      read_unlock_bh(&n->lock);
++      neigh_release(n);
++
++      if (!(nud_state & NUD_VALID))
++              return;
++
++      if (dev_fill_forward_path(dev, route->tuple[dir].out.h_dest, &stack) ||
++          !stack.num_paths)
++              return;
++
++      prev_type = DEV_PATH_ETHERNET;
++      for (i = 0; i <= stack.num_paths; i++) {
++              const struct net_device_path *path = &stack.path[i];
++              int n_encaps = route->tuple[!dir].in.num_encaps;
++
++              dev = (struct net_device *)path->dev;
++              if (flow_is_valid_ether_device(dev)) {
++                      if (route->tuple[dir].xmit_type != FLOW_OFFLOAD_XMIT_DIRECT) {
++                              memcpy(route->tuple[dir].out.h_source,
++                                     dev->dev_addr, ETH_ALEN);
++                              route->tuple[dir].out.ifindex = dev->ifindex;
++                      }
++                      route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
++              }
++
++              switch (path->type) {
++              case DEV_PATH_PPPOE:
++              case DEV_PATH_VLAN:
++                      if (n_encaps >= NF_FLOW_TABLE_ENCAP_MAX ||
++                          i == stack.num_paths) {
++                              last = true;
++                              break;
++                      }
++
++                      route->tuple[!dir].in.num_encaps++;
++                      route->tuple[!dir].in.encap[n_encaps].id = path->encap.id;
++                      route->tuple[!dir].in.encap[n_encaps].proto = path->encap.proto;
++                      if (path->type == DEV_PATH_PPPOE)
++                              memcpy(route->tuple[dir].out.h_dest,
++                                     path->encap.h_dest, ETH_ALEN);
++                      break;
++              case DEV_PATH_BRIDGE:
++                      switch (path->bridge.vlan_mode) {
++                      case DEV_PATH_BR_VLAN_TAG:
++                              if (n_encaps >= NF_FLOW_TABLE_ENCAP_MAX ||
++                                  i == stack.num_paths) {
++                                      last = true;
++                                      break;
++                              }
++
++                              route->tuple[!dir].in.num_encaps++;
++                              route->tuple[!dir].in.encap[n_encaps].id =
++                                      path->bridge.vlan_id;
++                              route->tuple[!dir].in.encap[n_encaps].proto =
++                                      path->bridge.vlan_proto;
++                              break;
++                      case DEV_PATH_BR_VLAN_UNTAG:
++                              route->tuple[!dir].in.num_encaps--;
++                              break;
++                      case DEV_PATH_BR_VLAN_UNTAG_HW:
++                              route->tuple[!dir].in.ingress_vlans |= BIT(n_encaps - 1);
++                              break;
++                      case DEV_PATH_BR_VLAN_KEEP:
++                              break;
++                      }
++                      break;
++              default:
++                      last = true;
++                      break;
++              }
++
++              if (last)
++                      break;
++      }
++
++      *out_dev = dev;
++      route->tuple[dir].out.hw_ifindex = dev->ifindex;
++      route->tuple[!dir].in.ifindex = dev->ifindex;
++}
++
++static int
++xt_flowoffload_route_dir(struct nf_flow_route *route, const struct nf_conn *ct,
++                       enum ip_conntrack_dir dir,
++                       const struct xt_action_param *par, int ifindex)
++{
++      struct dst_entry *dst = NULL;
++      struct flowi fl;
++
++      memset(&fl, 0, sizeof(fl));
++      switch (xt_family(par)) {
++      case NFPROTO_IPV4:
++              fl.u.ip4.daddr = ct->tuplehash[!dir].tuple.src.u3.ip;
++              fl.u.ip4.flowi4_oif = ifindex;
++              break;
++      case NFPROTO_IPV6:
++              fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.dst.u3.in6;
++              fl.u.ip6.daddr = ct->tuplehash[!dir].tuple.src.u3.in6;
++              fl.u.ip6.flowi6_oif = ifindex;
++              break;
++      }
++
++      nf_route(xt_net(par), &dst, &fl, false, xt_family(par));
++      if (!dst)
++              return -ENOENT;
++
++      route->tuple[dir].dst = dst;
++      if (dst_xfrm(dst))
++              route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_XFRM;
++      else
++              route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_NEIGH;
++
++      return 0;
++}
++
++static int
++xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct,
++                   const struct xt_action_param *par,
++                   struct nf_flow_route *route, enum ip_conntrack_dir dir,
++                   struct net_device **dev)
++{
++      int ret;
++
++      ret = xt_flowoffload_route_dir(route, ct, dir, par,
++                                     dev[dir]->ifindex);
++      if (ret)
++              return ret;
++
++      ret = xt_flowoffload_route_dir(route, ct, !dir, par,
++                                     dev[!dir]->ifindex);
++      if (ret)
++              return ret;
++
++      xt_flowoffload_route_check_path(route, ct, dir, &dev[!dir]);
++      xt_flowoffload_route_check_path(route, ct, !dir, &dev[dir]);
++
++      return 0;
++}
++
++static unsigned int
++flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
++{
++      struct xt_flowoffload_table *table;
++      const struct xt_flowoffload_target_info *info = par->targinfo;
++      struct tcphdr _tcph, *tcph = NULL;
++      enum ip_conntrack_info ctinfo;
++      enum ip_conntrack_dir dir;
++      struct nf_flow_route route = {};
++      struct flow_offload *flow = NULL;
++      struct net_device *devs[2] = {};
++      struct nf_conn *ct;
++      struct net *net;
++
++      if (xt_flowoffload_skip(skb, xt_family(par)))
++              return XT_CONTINUE;
++
++      ct = nf_ct_get(skb, &ctinfo);
++      if (ct == NULL)
++              return XT_CONTINUE;
++
++      switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
++      case IPPROTO_TCP:
++              if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
++                      return XT_CONTINUE;
++
++              tcph = skb_header_pointer(skb, par->thoff,
++                                        sizeof(_tcph), &_tcph);
++              if (unlikely(!tcph || tcph->fin || tcph->rst))
++                      return XT_CONTINUE;
++              break;
++      case IPPROTO_UDP:
++              break;
++      default:
++              return XT_CONTINUE;
++      }
++
++      if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) ||
++          ct->status & IPS_SEQ_ADJUST)
++              return XT_CONTINUE;
++
++      if (!nf_ct_is_confirmed(ct))
++              return XT_CONTINUE;
++
++      devs[dir] = xt_out(par);
++      devs[!dir] = xt_in(par);
++
++      if (!devs[dir] || !devs[!dir])
++              return XT_CONTINUE;
++
++      if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
++              return XT_CONTINUE;
++
++      dir = CTINFO2DIR(ctinfo);
++
++      if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0)
++              goto err_flow_route;
++
++      flow = flow_offload_alloc(ct);
++      if (!flow)
++              goto err_flow_alloc;
++
++      if (flow_offload_route_init(flow, &route) < 0)
++              goto err_flow_add;
++
++      if (tcph) {
++              ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
++              ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
++      }
++
++      table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)];
++      if (flow_offload_add(&table->ft, flow) < 0)
++              goto err_flow_add;
++
++      xt_flowoffload_check_device(table, devs[0]);
++      xt_flowoffload_check_device(table, devs[1]);
++
++      net = read_pnet(&table->ft.net);
++      if (!net)
++              write_pnet(&table->ft.net, xt_net(par));
++
++      dst_release(route.tuple[dir].dst);
++      dst_release(route.tuple[!dir].dst);
++
++      return XT_CONTINUE;
++
++err_flow_add:
++      flow_offload_free(flow);
++err_flow_alloc:
++      dst_release(route.tuple[dir].dst);
++      dst_release(route.tuple[!dir].dst);
++err_flow_route:
++      clear_bit(IPS_OFFLOAD_BIT, &ct->status);
++
++      return XT_CONTINUE;
++}
++
++static int flowoffload_chk(const struct xt_tgchk_param *par)
++{
++      struct xt_flowoffload_target_info *info = par->targinfo;
++
++      if (info->flags & ~XT_FLOWOFFLOAD_MASK)
++              return -EINVAL;
++
++      return 0;
++}
++
++static struct xt_target offload_tg_reg __read_mostly = {
++      .family         = NFPROTO_UNSPEC,
++      .name           = "FLOWOFFLOAD",
++      .revision       = 0,
++      .targetsize     = sizeof(struct xt_flowoffload_target_info),
++      .usersize       = sizeof(struct xt_flowoffload_target_info),
++      .checkentry     = flowoffload_chk,
++      .target         = flowoffload_tg,
++      .me             = THIS_MODULE,
++};
++
++static int flow_offload_netdev_event(struct notifier_block *this,
++                                   unsigned long event, void *ptr)
++{
++      struct xt_flowoffload_hook *hook0, *hook1;
++      struct net_device *dev = netdev_notifier_info_to_dev(ptr);
++
++      if (event != NETDEV_UNREGISTER)
++              return NOTIFY_DONE;
++
++      spin_lock_bh(&hooks_lock);
++      hook0 = flow_offload_lookup_hook(&flowtable[0], dev);
++      if (hook0)
++              hlist_del(&hook0->list);
++
++      hook1 = flow_offload_lookup_hook(&flowtable[1], dev);
++      if (hook1)
++              hlist_del(&hook1->list);
++      spin_unlock_bh(&hooks_lock);
++
++      if (hook0) {
++              nf_unregister_net_hook(hook0->net, &hook0->ops);
++              kfree(hook0);
++      }
++
++      if (hook1) {
++              nf_unregister_net_hook(hook1->net, &hook1->ops);
++              kfree(hook1);
++      }
++
++      nf_flow_table_cleanup(dev);
++
++      return NOTIFY_DONE;
++}
++
++static struct notifier_block flow_offload_netdev_notifier = {
++      .notifier_call  = flow_offload_netdev_event,
++};
++
++static unsigned int
++nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb,
++                        const struct nf_hook_state *state)
++{
++      switch (skb->protocol) {
++      case htons(ETH_P_IP):
++              return nf_flow_offload_ip_hook(priv, skb, state);
++      case htons(ETH_P_IPV6):
++              return nf_flow_offload_ipv6_hook(priv, skb, state);
++      }
++
++      return NF_ACCEPT;
++}
++
++static int nf_flow_rule_route_inet(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 *flow_tuple = &flow->tuplehash[dir].tuple;
++      int err;
++
++      switch (flow_tuple->l3proto) {
++      case NFPROTO_IPV4:
++              err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
++              break;
++      case NFPROTO_IPV6:
++              err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
++              break;
++      default:
++              err = -1;
++              break;
++      }
++
++      return err;
++}
++
++static struct nf_flowtable_type flowtable_inet = {
++      .family         = NFPROTO_INET,
++      .init           = nf_flow_table_init,
++      .setup          = nf_flow_table_offload_setup,
++      .action         = nf_flow_rule_route_inet,
++      .free           = nf_flow_table_free,
++      .hook           = nf_flow_offload_inet_hook,
++      .owner          = THIS_MODULE,
++};
++
++static int init_flowtable(struct xt_flowoffload_table *tbl)
++{
++      INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work);
++      tbl->ft.type = &flowtable_inet;
++
++      return nf_flow_table_init(&tbl->ft);
++}
++
++static int __init xt_flowoffload_tg_init(void)
++{
++      int ret;
++
++      register_netdevice_notifier(&flow_offload_netdev_notifier);
++
++      ret = init_flowtable(&flowtable[0]);
++      if (ret)
++              return ret;
++
++      ret = init_flowtable(&flowtable[1]);
++      if (ret)
++              goto cleanup;
++
++      flowtable[1].ft.flags = NF_FLOWTABLE_HW_OFFLOAD;
++
++      ret = xt_register_target(&offload_tg_reg);
++      if (ret)
++              goto cleanup2;
++
++      return 0;
++
++cleanup2:
++      nf_flow_table_free(&flowtable[1].ft);
++cleanup:
++      nf_flow_table_free(&flowtable[0].ft);
++      return ret;
++}
++
++static void __exit xt_flowoffload_tg_exit(void)
++{
++      xt_unregister_target(&offload_tg_reg);
++      unregister_netdevice_notifier(&flow_offload_netdev_notifier);
++      nf_flow_table_free(&flowtable[0].ft);
++      nf_flow_table_free(&flowtable[1].ft);
++}
++
++MODULE_LICENSE("GPL");
++module_init(xt_flowoffload_tg_init);
++module_exit(xt_flowoffload_tg_exit);
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -7,7 +7,6 @@
+ #include <linux/netdevice.h>
+ #include <net/ip.h>
+ #include <net/ip6_route.h>
+-#include <net/netfilter/nf_tables.h>
+ #include <net/netfilter/nf_flow_table.h>
+ #include <net/netfilter/nf_conntrack.h>
+ #include <net/netfilter/nf_conntrack_core.h>
+@@ -356,8 +355,7 @@ flow_offload_lookup(struct nf_flowtable
+ }
+ EXPORT_SYMBOL_GPL(flow_offload_lookup);
+-static int
+-nf_flow_table_iterate(struct nf_flowtable *flow_table,
++int nf_flow_table_iterate(struct nf_flowtable *flow_table,
+                     void (*iter)(struct flow_offload *flow, void *data),
+                     void *data)
+ {
+@@ -389,6 +387,7 @@ nf_flow_table_iterate(struct nf_flowtabl
+       return err;
+ }
++EXPORT_SYMBOL_GPL(nf_flow_table_iterate);
+ static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
+ {
+--- /dev/null
++++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
++#ifndef _XT_FLOWOFFLOAD_H
++#define _XT_FLOWOFFLOAD_H
++
++#include <linux/types.h>
++
++enum {
++      XT_FLOWOFFLOAD_HW       = 1 << 0,
++
++      XT_FLOWOFFLOAD_MASK     = XT_FLOWOFFLOAD_HW
++};
++
++struct xt_flowoffload_target_info {
++      __u32 flags;
++};
++
++#endif /* _XT_FLOWOFFLOAD_H */
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -266,6 +266,10 @@ void nf_flow_table_free(struct nf_flowta
+ 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);
++
+ 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);
diff --git a/target/linux/generic/hack-5.10/650-netfilter-add-xt_OFFLOAD-target.patch b/target/linux/generic/hack-5.10/650-netfilter-add-xt_OFFLOAD-target.patch
deleted file mode 100644 (file)
index eb540ac..0000000
+++ /dev/null
@@ -1,822 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 20 Feb 2018 15:56:02 +0100
-Subject: [PATCH] netfilter: add xt_OFFLOAD target
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
- create mode 100644 net/netfilter/xt_OFFLOAD.c
-
---- a/net/ipv4/netfilter/Kconfig
-+++ b/net/ipv4/netfilter/Kconfig
-@@ -56,8 +56,6 @@ config NF_TABLES_ARP
-       help
-         This option enables the ARP support for nf_tables.
--endif # NF_TABLES
--
- config NF_FLOW_TABLE_IPV4
-       tristate "Netfilter flow table IPv4 module"
-       depends on NF_FLOW_TABLE
-@@ -66,6 +64,8 @@ config NF_FLOW_TABLE_IPV4
-         To compile it as a module, choose M here.
-+endif # NF_TABLES
-+
- config NF_DUP_IPV4
-       tristate "Netfilter IPv4 packet duplication to alternate destination"
-       depends on !NF_CONNTRACK || NF_CONNTRACK
---- a/net/ipv6/netfilter/Kconfig
-+++ b/net/ipv6/netfilter/Kconfig
-@@ -45,7 +45,6 @@ config NFT_FIB_IPV6
-         multicast or blackhole.
- endif # NF_TABLES_IPV6
--endif # NF_TABLES
- config NF_FLOW_TABLE_IPV6
-       tristate "Netfilter flow table IPv6 module"
-@@ -55,6 +54,8 @@ config NF_FLOW_TABLE_IPV6
-         To compile it as a module, choose M here.
-+endif # NF_TABLES
-+
- config NF_DUP_IPV6
-       tristate "Netfilter IPv6 packet duplication to alternate destination"
-       depends on !NF_CONNTRACK || NF_CONNTRACK
---- a/net/netfilter/Kconfig
-+++ b/net/netfilter/Kconfig
-@@ -683,8 +683,6 @@ config NFT_FIB_NETDEV
- endif # NF_TABLES_NETDEV
--endif # NF_TABLES
--
- config NF_FLOW_TABLE_INET
-       tristate "Netfilter flow table mixed IPv4/IPv6 module"
-       depends on NF_FLOW_TABLE
-@@ -693,11 +691,12 @@ config NF_FLOW_TABLE_INET
-         To compile it as a module, choose M here.
-+endif # NF_TABLES
-+
- config NF_FLOW_TABLE
-       tristate "Netfilter flow table module"
-       depends on NETFILTER_INGRESS
-       depends on NF_CONNTRACK
--      depends on NF_TABLES
-       help
-         This option adds the flow table core infrastructure.
-@@ -977,6 +976,15 @@ config NETFILTER_XT_TARGET_NOTRACK
-       depends on NETFILTER_ADVANCED
-       select NETFILTER_XT_TARGET_CT
-+config NETFILTER_XT_TARGET_FLOWOFFLOAD
-+      tristate '"FLOWOFFLOAD" target support'
-+      depends on NF_FLOW_TABLE
-+      depends on NETFILTER_INGRESS
-+      help
-+        This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload
-+        module to speed up processing of packets by bypassing the usual
-+        netfilter chains
-+
- config NETFILTER_XT_TARGET_RATEEST
-       tristate '"RATEEST" target support'
-       depends on NETFILTER_ADVANCED
---- a/net/netfilter/Makefile
-+++ b/net/netfilter/Makefile
-@@ -145,6 +145,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF
- obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
-+obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
---- /dev/null
-+++ b/net/netfilter/xt_FLOWOFFLOAD.c
-@@ -0,0 +1,660 @@
-+/*
-+ * Copyright (C) 2018-2021 Felix Fietkau <nbd@nbd.name>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ */
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/netfilter.h>
-+#include <linux/netfilter/xt_FLOWOFFLOAD.h>
-+#include <net/ip.h>
-+#include <net/netfilter/nf_conntrack.h>
-+#include <net/netfilter/nf_conntrack_extend.h>
-+#include <net/netfilter/nf_conntrack_helper.h>
-+#include <net/netfilter/nf_flow_table.h>
-+
-+struct xt_flowoffload_hook {
-+      struct hlist_node list;
-+      struct nf_hook_ops ops;
-+      struct net *net;
-+      bool registered;
-+      bool used;
-+};
-+
-+struct xt_flowoffload_table {
-+      struct nf_flowtable ft;
-+      struct hlist_head hooks;
-+      struct delayed_work work;
-+};
-+
-+static DEFINE_SPINLOCK(hooks_lock);
-+
-+struct xt_flowoffload_table flowtable[2];
-+
-+static unsigned int
-+xt_flowoffload_net_hook(void *priv, struct sk_buff *skb,
-+                      const struct nf_hook_state *state)
-+{
-+      struct nf_flowtable *ft = priv;
-+
-+      if (!atomic_read(&ft->rhashtable.nelems))
-+              return NF_ACCEPT;
-+
-+      switch (skb->protocol) {
-+      case htons(ETH_P_IP):
-+              return nf_flow_offload_ip_hook(priv, skb, state);
-+      case htons(ETH_P_IPV6):
-+              return nf_flow_offload_ipv6_hook(priv, skb, state);
-+      }
-+
-+      return NF_ACCEPT;
-+}
-+
-+static int
-+xt_flowoffload_create_hook(struct xt_flowoffload_table *table,
-+                         struct net_device *dev)
-+{
-+      struct xt_flowoffload_hook *hook;
-+      struct nf_hook_ops *ops;
-+
-+      hook = kzalloc(sizeof(*hook), GFP_ATOMIC);
-+      if (!hook)
-+              return -ENOMEM;
-+
-+      ops = &hook->ops;
-+      ops->pf = NFPROTO_NETDEV;
-+      ops->hooknum = NF_NETDEV_INGRESS;
-+      ops->priority = 10;
-+      ops->priv = &table->ft;
-+      ops->hook = xt_flowoffload_net_hook;
-+      ops->dev = dev;
-+
-+      hlist_add_head(&hook->list, &table->hooks);
-+      mod_delayed_work(system_power_efficient_wq, &table->work, 0);
-+
-+      return 0;
-+}
-+
-+static struct xt_flowoffload_hook *
-+flow_offload_lookup_hook(struct xt_flowoffload_table *table,
-+                       struct net_device *dev)
-+{
-+      struct xt_flowoffload_hook *hook;
-+
-+      hlist_for_each_entry(hook, &table->hooks, list) {
-+              if (hook->ops.dev == dev)
-+                      return hook;
-+      }
-+
-+      return NULL;
-+}
-+
-+static void
-+xt_flowoffload_check_device(struct xt_flowoffload_table *table,
-+                          struct net_device *dev)
-+{
-+      struct xt_flowoffload_hook *hook;
-+
-+      if (!dev)
-+              return;
-+
-+      spin_lock_bh(&hooks_lock);
-+      hook = flow_offload_lookup_hook(table, dev);
-+      if (hook)
-+              hook->used = true;
-+      else
-+              xt_flowoffload_create_hook(table, dev);
-+      spin_unlock_bh(&hooks_lock);
-+}
-+
-+static void
-+xt_flowoffload_register_hooks(struct xt_flowoffload_table *table)
-+{
-+      struct xt_flowoffload_hook *hook;
-+
-+restart:
-+      hlist_for_each_entry(hook, &table->hooks, list) {
-+              if (hook->registered)
-+                      continue;
-+
-+              hook->registered = true;
-+              hook->net = dev_net(hook->ops.dev);
-+              spin_unlock_bh(&hooks_lock);
-+              nf_register_net_hook(hook->net, &hook->ops);
-+              if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
-+                      table->ft.type->setup(&table->ft, hook->ops.dev,
-+                                            FLOW_BLOCK_BIND);
-+              spin_lock_bh(&hooks_lock);
-+              goto restart;
-+      }
-+
-+}
-+
-+static bool
-+xt_flowoffload_cleanup_hooks(struct xt_flowoffload_table *table)
-+{
-+      struct xt_flowoffload_hook *hook;
-+      bool active = false;
-+
-+restart:
-+      spin_lock_bh(&hooks_lock);
-+      hlist_for_each_entry(hook, &table->hooks, list) {
-+              if (hook->used || !hook->registered) {
-+                      active = true;
-+                      continue;
-+              }
-+
-+              hlist_del(&hook->list);
-+              spin_unlock_bh(&hooks_lock);
-+              if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
-+                      table->ft.type->setup(&table->ft, hook->ops.dev,
-+                                            FLOW_BLOCK_UNBIND);
-+              nf_unregister_net_hook(hook->net, &hook->ops);
-+              kfree(hook);
-+              goto restart;
-+      }
-+      spin_unlock_bh(&hooks_lock);
-+
-+      return active;
-+}
-+
-+static void
-+xt_flowoffload_check_hook(struct flow_offload *flow, void *data)
-+{
-+      struct xt_flowoffload_table *table = data;
-+      struct flow_offload_tuple *tuple = &flow->tuplehash[0].tuple;
-+      struct xt_flowoffload_hook *hook;
-+
-+      spin_lock_bh(&hooks_lock);
-+      hlist_for_each_entry(hook, &table->hooks, list) {
-+              int ifindex;
-+
-+              if (tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
-+                      ifindex = tuple->out.ifidx;
-+              else
-+                      ifindex = tuple->dst_cache->dev->ifindex;
-+
-+              if (hook->ops.dev->ifindex != tuple->iifidx &&
-+                  hook->ops.dev->ifindex != ifindex)
-+                      continue;
-+
-+              hook->used = true;
-+      }
-+      spin_unlock_bh(&hooks_lock);
-+
-+      cond_resched();
-+}
-+
-+static void
-+xt_flowoffload_hook_work(struct work_struct *work)
-+{
-+      struct xt_flowoffload_table *table;
-+      struct xt_flowoffload_hook *hook;
-+      int err;
-+
-+      table = container_of(work, struct xt_flowoffload_table, work.work);
-+
-+      spin_lock_bh(&hooks_lock);
-+      xt_flowoffload_register_hooks(table);
-+      hlist_for_each_entry(hook, &table->hooks, list)
-+              hook->used = false;
-+      spin_unlock_bh(&hooks_lock);
-+
-+      err = nf_flow_table_iterate(&table->ft, xt_flowoffload_check_hook,
-+                                  table);
-+      if (err && err != -EAGAIN)
-+              goto out;
-+
-+      if (!xt_flowoffload_cleanup_hooks(table))
-+              return;
-+
-+out:
-+      queue_delayed_work(system_power_efficient_wq, &table->work, HZ);
-+}
-+
-+static bool
-+xt_flowoffload_skip(struct sk_buff *skb, int family)
-+{
-+      if (skb_sec_path(skb))
-+              return true;
-+
-+      if (family == NFPROTO_IPV4) {
-+              const struct ip_options *opt = &(IPCB(skb)->opt);
-+
-+              if (unlikely(opt->optlen))
-+                      return true;
-+      }
-+
-+      return false;
-+}
-+
-+static bool flow_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
-+xt_flowoffload_route_check_path(struct nf_flow_route *route,
-+                              const struct nf_conn *ct,
-+                              enum ip_conntrack_dir dir,
-+                              struct net_device **out_dev)
-+{
-+      const struct dst_entry *dst = route->tuple[dir].dst;
-+      const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
-+      struct net_device_path_stack stack;
-+      enum net_device_path_type prev_type;
-+      struct net_device *dev = dst->dev;
-+      struct neighbour *n;
-+      bool last = false;
-+      u8 nud_state;
-+      int i;
-+
-+      route->tuple[!dir].in.ifindex = dev->ifindex;
-+
-+      if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
-+              return;
-+
-+      if ((dev->flags & IFF_LOOPBACK) ||
-+          dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||
-+          !is_valid_ether_addr(dev->dev_addr))
-+              return;
-+
-+      n = dst_neigh_lookup(dst, daddr);
-+      if (!n)
-+              return;
-+
-+      read_lock_bh(&n->lock);
-+      nud_state = n->nud_state;
-+      memcpy(route->tuple[dir].out.h_dest, n->ha, ETH_ALEN);
-+      read_unlock_bh(&n->lock);
-+      neigh_release(n);
-+
-+      if (!(nud_state & NUD_VALID))
-+              return;
-+
-+      if (dev_fill_forward_path(dev, route->tuple[dir].out.h_dest, &stack) ||
-+          !stack.num_paths)
-+              return;
-+
-+      prev_type = DEV_PATH_ETHERNET;
-+      for (i = 0; i <= stack.num_paths; i++) {
-+              const struct net_device_path *path = &stack.path[i];
-+              int n_vlans = route->tuple[!dir].in.num_vlans;
-+
-+              dev = (struct net_device *)path->dev;
-+              if (flow_is_valid_ether_device(dev)) {
-+                      if (route->tuple[dir].xmit_type != FLOW_OFFLOAD_XMIT_DIRECT)
-+                              memcpy(route->tuple[dir].out.h_source,
-+                                     dev->dev_addr, ETH_ALEN);
-+                      route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
-+                      route->tuple[dir].out.ifindex = dev->ifindex;
-+              }
-+
-+              switch (path->type) {
-+              case DEV_PATH_VLAN:
-+                      if (n_vlans >= NF_FLOW_TABLE_VLAN_MAX ||
-+                          i == stack.num_paths) {
-+                              last = true;
-+                              break;
-+                      }
-+
-+                      route->tuple[!dir].in.num_vlans++;
-+                      route->tuple[!dir].in.vid[n_vlans] = path->vlan.id;
-+                      route->tuple[!dir].in.vproto[n_vlans] = path->vlan.proto;
-+                      break;
-+              case DEV_PATH_BRIDGE:
-+                      switch (path->bridge.vlan_mode) {
-+                      case DEV_PATH_BR_VLAN_TAG:
-+                              if (n_vlans >= NF_FLOW_TABLE_VLAN_MAX ||
-+                                  i == stack.num_paths) {
-+                                      last = true;
-+                                      break;
-+                              }
-+
-+                              route->tuple[!dir].in.num_vlans++;
-+                              route->tuple[!dir].in.vid[n_vlans] =
-+                                      path->bridge.vlan_id;
-+                              route->tuple[!dir].in.vproto[n_vlans] =
-+                                      path->bridge.vlan_proto;
-+                              break;
-+                      case DEV_PATH_BR_VLAN_UNTAG_HW:
-+                              route->tuple[!dir].in.pvid.id =
-+                                      route->tuple[!dir].in.vid[n_vlans - 1];
-+                              route->tuple[!dir].in.pvid.proto =
-+                                      route->tuple[!dir].in.vproto[n_vlans - 1];
-+                              fallthrough;
-+                      case DEV_PATH_BR_VLAN_UNTAG:
-+                              route->tuple[!dir].in.num_vlans--;
-+                              break;
-+                      case DEV_PATH_BR_VLAN_KEEP:
-+                              break;
-+                      }
-+                      break;
-+              default:
-+                      last = true;
-+                      break;
-+              }
-+
-+              if (last)
-+                      break;
-+      }
-+
-+      *out_dev = dev;
-+      route->tuple[!dir].in.ifindex = dev->ifindex;
-+}
-+
-+static int
-+xt_flowoffload_route_dir(struct nf_flow_route *route, const struct nf_conn *ct,
-+                       enum ip_conntrack_dir dir,
-+                       const struct xt_action_param *par, int ifindex)
-+{
-+      struct dst_entry *dst = NULL;
-+      struct flowi fl;
-+
-+      memset(&fl, 0, sizeof(fl));
-+      switch (xt_family(par)) {
-+      case NFPROTO_IPV4:
-+              fl.u.ip4.daddr = ct->tuplehash[!dir].tuple.src.u3.ip;
-+              fl.u.ip4.flowi4_oif = ifindex;
-+              break;
-+      case NFPROTO_IPV6:
-+              fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.dst.u3.in6;
-+              fl.u.ip6.daddr = ct->tuplehash[!dir].tuple.src.u3.in6;
-+              fl.u.ip6.flowi6_oif = ifindex;
-+              break;
-+      }
-+
-+      nf_route(xt_net(par), &dst, &fl, false, xt_family(par));
-+      if (!dst)
-+              return -ENOENT;
-+
-+      route->tuple[dir].dst = dst;
-+      if (dst_xfrm(dst))
-+              route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_XFRM;
-+      else
-+              route->tuple[dir].xmit_type = FLOW_OFFLOAD_XMIT_NEIGH;
-+
-+      return 0;
-+}
-+
-+static int
-+xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct,
-+                   const struct xt_action_param *par,
-+                   struct nf_flow_route *route, enum ip_conntrack_dir dir,
-+                   struct net_device **dev)
-+{
-+      int ret;
-+
-+      ret = xt_flowoffload_route_dir(route, ct, dir, par,
-+                                     dev[dir]->ifindex);
-+      if (ret)
-+              return ret;
-+
-+      ret = xt_flowoffload_route_dir(route, ct, !dir, par,
-+                                     dev[!dir]->ifindex);
-+      if (ret)
-+              return ret;
-+
-+      xt_flowoffload_route_check_path(route, ct, dir, &dev[!dir]);
-+      xt_flowoffload_route_check_path(route, ct, !dir, &dev[dir]);
-+
-+      return 0;
-+}
-+
-+static unsigned int
-+flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
-+{
-+      struct xt_flowoffload_table *table;
-+      const struct xt_flowoffload_target_info *info = par->targinfo;
-+      struct tcphdr _tcph, *tcph = NULL;
-+      enum ip_conntrack_info ctinfo;
-+      enum ip_conntrack_dir dir;
-+      struct nf_flow_route route = {};
-+      struct flow_offload *flow = NULL;
-+      struct net_device *devs[2] = {};
-+      struct nf_conn *ct;
-+      struct net *net;
-+
-+      if (xt_flowoffload_skip(skb, xt_family(par)))
-+              return XT_CONTINUE;
-+
-+      ct = nf_ct_get(skb, &ctinfo);
-+      if (ct == NULL)
-+              return XT_CONTINUE;
-+
-+      switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
-+      case IPPROTO_TCP:
-+              if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
-+                      return XT_CONTINUE;
-+
-+              tcph = skb_header_pointer(skb, par->thoff,
-+                                        sizeof(_tcph), &_tcph);
-+              if (unlikely(!tcph || tcph->fin || tcph->rst))
-+                      return XT_CONTINUE;
-+              break;
-+      case IPPROTO_UDP:
-+              break;
-+      default:
-+              return XT_CONTINUE;
-+      }
-+
-+      if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) ||
-+          ct->status & IPS_SEQ_ADJUST)
-+              return XT_CONTINUE;
-+
-+      if (!nf_ct_is_confirmed(ct))
-+              return XT_CONTINUE;
-+
-+      devs[dir] = xt_out(par);
-+      devs[!dir] = xt_in(par);
-+
-+      if (!devs[dir] || !devs[!dir])
-+              return XT_CONTINUE;
-+
-+      if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
-+              return XT_CONTINUE;
-+
-+      dir = CTINFO2DIR(ctinfo);
-+
-+      if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0)
-+              goto err_flow_route;
-+
-+      flow = flow_offload_alloc(ct);
-+      if (!flow)
-+              goto err_flow_alloc;
-+
-+      if (flow_offload_route_init(flow, &route) < 0)
-+              goto err_flow_add;
-+
-+      if (tcph) {
-+              ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
-+              ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
-+      }
-+
-+      table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)];
-+      if (flow_offload_add(&table->ft, flow) < 0)
-+              goto err_flow_add;
-+
-+      xt_flowoffload_check_device(table, devs[0]);
-+      xt_flowoffload_check_device(table, devs[1]);
-+
-+      net = read_pnet(&table->ft.net);
-+      if (!net)
-+              write_pnet(&table->ft.net, xt_net(par));
-+
-+      dst_release(route.tuple[dir].dst);
-+      dst_release(route.tuple[!dir].dst);
-+
-+      return XT_CONTINUE;
-+
-+err_flow_add:
-+      flow_offload_free(flow);
-+err_flow_alloc:
-+      dst_release(route.tuple[dir].dst);
-+      dst_release(route.tuple[!dir].dst);
-+err_flow_route:
-+      clear_bit(IPS_OFFLOAD_BIT, &ct->status);
-+
-+      return XT_CONTINUE;
-+}
-+
-+static int flowoffload_chk(const struct xt_tgchk_param *par)
-+{
-+      struct xt_flowoffload_target_info *info = par->targinfo;
-+
-+      if (info->flags & ~XT_FLOWOFFLOAD_MASK)
-+              return -EINVAL;
-+
-+      return 0;
-+}
-+
-+static struct xt_target offload_tg_reg __read_mostly = {
-+      .family         = NFPROTO_UNSPEC,
-+      .name           = "FLOWOFFLOAD",
-+      .revision       = 0,
-+      .targetsize     = sizeof(struct xt_flowoffload_target_info),
-+      .usersize       = sizeof(struct xt_flowoffload_target_info),
-+      .checkentry     = flowoffload_chk,
-+      .target         = flowoffload_tg,
-+      .me             = THIS_MODULE,
-+};
-+
-+static int flow_offload_netdev_event(struct notifier_block *this,
-+                                   unsigned long event, void *ptr)
-+{
-+      struct xt_flowoffload_hook *hook0, *hook1;
-+      struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-+
-+      if (event != NETDEV_UNREGISTER)
-+              return NOTIFY_DONE;
-+
-+      spin_lock_bh(&hooks_lock);
-+      hook0 = flow_offload_lookup_hook(&flowtable[0], dev);
-+      if (hook0)
-+              hlist_del(&hook0->list);
-+
-+      hook1 = flow_offload_lookup_hook(&flowtable[1], dev);
-+      if (hook1)
-+              hlist_del(&hook1->list);
-+      spin_unlock_bh(&hooks_lock);
-+
-+      if (hook0) {
-+              nf_unregister_net_hook(hook0->net, &hook0->ops);
-+              kfree(hook0);
-+      }
-+
-+      if (hook1) {
-+              nf_unregister_net_hook(hook1->net, &hook1->ops);
-+              kfree(hook1);
-+      }
-+
-+      nf_flow_table_cleanup(dev);
-+
-+      return NOTIFY_DONE;
-+}
-+
-+static struct notifier_block flow_offload_netdev_notifier = {
-+      .notifier_call  = flow_offload_netdev_event,
-+};
-+
-+static unsigned int
-+nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb,
-+                        const struct nf_hook_state *state)
-+{
-+      switch (skb->protocol) {
-+      case htons(ETH_P_IP):
-+              return nf_flow_offload_ip_hook(priv, skb, state);
-+      case htons(ETH_P_IPV6):
-+              return nf_flow_offload_ipv6_hook(priv, skb, state);
-+      }
-+
-+      return NF_ACCEPT;
-+}
-+
-+static int nf_flow_rule_route_inet(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 *flow_tuple = &flow->tuplehash[dir].tuple;
-+      int err;
-+
-+      switch (flow_tuple->l3proto) {
-+      case NFPROTO_IPV4:
-+              err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
-+              break;
-+      case NFPROTO_IPV6:
-+              err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
-+              break;
-+      default:
-+              err = -1;
-+              break;
-+      }
-+
-+      return err;
-+}
-+
-+static struct nf_flowtable_type flowtable_inet = {
-+      .family         = NFPROTO_INET,
-+      .init           = nf_flow_table_init,
-+      .setup          = nf_flow_table_offload_setup,
-+      .action         = nf_flow_rule_route_inet,
-+      .free           = nf_flow_table_free,
-+      .hook           = nf_flow_offload_inet_hook,
-+      .owner          = THIS_MODULE,
-+};
-+
-+static int init_flowtable(struct xt_flowoffload_table *tbl)
-+{
-+      INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work);
-+      tbl->ft.type = &flowtable_inet;
-+
-+      return nf_flow_table_init(&tbl->ft);
-+}
-+
-+static int __init xt_flowoffload_tg_init(void)
-+{
-+      int ret;
-+
-+      register_netdevice_notifier(&flow_offload_netdev_notifier);
-+
-+      ret = init_flowtable(&flowtable[0]);
-+      if (ret)
-+              return ret;
-+
-+      ret = init_flowtable(&flowtable[1]);
-+      if (ret)
-+              goto cleanup;
-+
-+      flowtable[1].ft.flags = NF_FLOWTABLE_HW_OFFLOAD;
-+
-+      ret = xt_register_target(&offload_tg_reg);
-+      if (ret)
-+              goto cleanup2;
-+
-+      return 0;
-+
-+cleanup2:
-+      nf_flow_table_free(&flowtable[1].ft);
-+cleanup:
-+      nf_flow_table_free(&flowtable[0].ft);
-+      return ret;
-+}
-+
-+static void __exit xt_flowoffload_tg_exit(void)
-+{
-+      xt_unregister_target(&offload_tg_reg);
-+      unregister_netdevice_notifier(&flow_offload_netdev_notifier);
-+      nf_flow_table_free(&flowtable[0].ft);
-+      nf_flow_table_free(&flowtable[1].ft);
-+}
-+
-+MODULE_LICENSE("GPL");
-+module_init(xt_flowoffload_tg_init);
-+module_exit(xt_flowoffload_tg_exit);
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -7,7 +7,6 @@
- #include <linux/netdevice.h>
- #include <net/ip.h>
- #include <net/ip6_route.h>
--#include <net/netfilter/nf_tables.h>
- #include <net/netfilter/nf_flow_table.h>
- #include <net/netfilter/nf_conntrack.h>
- #include <net/netfilter/nf_conntrack_core.h>
-@@ -355,8 +354,7 @@ flow_offload_lookup(struct nf_flowtable
- }
- EXPORT_SYMBOL_GPL(flow_offload_lookup);
--static int
--nf_flow_table_iterate(struct nf_flowtable *flow_table,
-+int nf_flow_table_iterate(struct nf_flowtable *flow_table,
-                     void (*iter)(struct flow_offload *flow, void *data),
-                     void *data)
- {
-@@ -388,6 +386,7 @@ nf_flow_table_iterate(struct nf_flowtabl
-       return err;
- }
-+EXPORT_SYMBOL_GPL(nf_flow_table_iterate);
- static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
- {
---- /dev/null
-+++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
-@@ -0,0 +1,17 @@
-+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-+#ifndef _XT_FLOWOFFLOAD_H
-+#define _XT_FLOWOFFLOAD_H
-+
-+#include <linux/types.h>
-+
-+enum {
-+      XT_FLOWOFFLOAD_HW       = 1 << 0,
-+
-+      XT_FLOWOFFLOAD_MASK     = XT_FLOWOFFLOAD_HW
-+};
-+
-+struct xt_flowoffload_target_info {
-+      __u32 flags;
-+};
-+
-+#endif /* _XT_FLOWOFFLOAD_H */
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -265,6 +265,10 @@ void nf_flow_table_free(struct nf_flowta
- 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);
-+
- 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);
index 953446baabcb6c2cf315aa02dce53e07641a2987..603dddfc3651ca96a97343411519b972f4c6f435 100644 (file)
@@ -1,5 +1,5 @@
 From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Sun, 10 Jan 2021 15:53:58 +0100
+Date: Thu, 4 Mar 2021 23:18:11 +0100
 Subject: [PATCH] net: resolve forwarding path from virtual netdevice and
  HW destination address
 
@@ -91,7 +91,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
   *    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
++ *     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);
@@ -99,8 +99,8 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
        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);
++      int                     (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx,
++                                                         struct net_device_path *path);
  };
  
  /**
index ddc0de0d3b95b4152803d020dc46549c457ad625..26fb64509cef34167dd0ba453bdec6a5d7d05913 100644 (file)
@@ -42,7 +42,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +              struct {
 +                      u16             id;
 +                      __be16          proto;
-+              } vlan;
++              } encap;
 +      };
  };
  
@@ -59,8 +59,8 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +      struct vlan_dev_priv *vlan = vlan_dev_priv(ctx->dev);
 +
 +      path->type = DEV_PATH_VLAN;
-+      path->vlan.id = vlan->vlan_id;
-+      path->vlan.proto = vlan->vlan_proto;
++      path->encap.id = vlan->vlan_id;
++      path->encap.proto = vlan->vlan_proto;
 +      path->dev = ctx->dev;
 +      ctx->dev = vlan->real_dev;
 +
index 34724a569649f89101609ee12c0bad8759c23467..1f61cff09d0aaf79b6ffdbd6bc7ef821253c03e9 100644 (file)
@@ -89,7 +89,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +}
 +
 +struct nft_forward_info {
-+      const struct net_device *dev;
++      const struct net_device *indev;
 +};
 +
 +static void nft_dev_path_info(const struct net_device_path_stack *stack,
@@ -102,12 +102,12 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +              path = &stack->path[i];
 +              switch (path->type) {
 +              case DEV_PATH_ETHERNET:
-+                      info->dev = path->dev;
++                      info->indev = path->dev;
 +                      break;
 +              case DEV_PATH_VLAN:
 +              case DEV_PATH_BRIDGE:
 +              default:
-+                      info->dev = NULL;
++                      info->indev = NULL;
 +                      break;
 +              }
 +      }
@@ -142,10 +142,10 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +      if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
 +              nft_dev_path_info(&stack, &info);
 +
-+      if (!info.dev || !nft_flowtable_find_dev(info.dev, ft))
++      if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
 +              return;
 +
-+      route->tuple[!dir].in.ifindex = info.dev->ifindex;
++      route->tuple[!dir].in.ifindex = info.indev->ifindex;
 +}
 +
  static int nft_flow_route(const struct nft_pktinfo *pkt,
index d4bf68a690dff31ea3a1217e27c31e36cac6175f..ada14e6c7a6caf8e7e2546acf86953dcb20a6803 100644 (file)
@@ -1,5 +1,5 @@
 From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Fri, 20 Nov 2020 13:49:19 +0100
+Date: Thu, 4 Mar 2021 03:26:35 +0100
 Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
  obtain egress device
 
@@ -271,10 +271,11 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
        struct neighbour *n;
        u8 nud_state;
  
-@@ -66,22 +65,35 @@ static int nft_dev_fill_forward_path(con
+@@ -66,27 +65,43 @@ static int nft_dev_fill_forward_path(con
  
  struct nft_forward_info {
-       const struct net_device *dev;
+       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;
@@ -294,7 +295,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
                path = &stack->path[i];
                switch (path->type) {
                case DEV_PATH_ETHERNET:
-                       info->dev = path->dev;
+                       info->indev = path->dev;
 +                      if (is_zero_ether_addr(info->h_source))
 +                              memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
                        break;
@@ -307,9 +308,16 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +                      break;
 +              case DEV_PATH_VLAN:
                default:
-                       info->dev = NULL;
+                       info->indev = NULL;
                        break;
-@@ -114,14 +126,22 @@ static void nft_dev_forward_path(struct
+               }
+       }
++      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 = {};
@@ -320,15 +328,15 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +      if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
 +              nft_dev_path_info(&stack, &info, ha);
  
-       if (!info.dev || !nft_flowtable_find_dev(info.dev, ft))
+       if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
                return;
  
-       route->tuple[!dir].in.ifindex = info.dev->ifindex;
+       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.dev->ifindex;
++              route->tuple[dir].out.ifindex = info.outdev->ifindex;
 +              route->tuple[dir].xmit_type = info.xmit_type;
 +      }
  }
index 77aeaccbce771d4777d2bca032d381050ed2113c..fea1b59dc3fb1d5e9343c866c1a0aede86c745c3 100644 (file)
@@ -16,7 +16,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
        FLOW_OFFLOAD_XMIT_DIRECT,
  };
  
-+#define NF_FLOW_TABLE_VLAN_MAX        2
++#define NF_FLOW_TABLE_ENCAP_MAX               2
 +
  struct flow_offload_tuple {
        union {
@@ -28,7 +28,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +      struct {
 +              u16                     id;
 +              __be16                  proto;
-+      } in_vlan[NF_FLOW_TABLE_VLAN_MAX];
++      } encap[NF_FLOW_TABLE_ENCAP_MAX];
  
        /* All members above are keys for lookups, see flow_offload_hash(). */
        struct { }                      __hash;
@@ -38,17 +38,19 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 -
 +      u8                              dir:4,
 +                                      xmit_type:2,
-+                                      in_vlan_num:2;
++                                      encap_num:2;
        u16                             mtu;
        union {
                struct dst_entry        *dst_cache;
-@@ -174,6 +180,9 @@ struct nf_flow_route {
+@@ -174,6 +180,11 @@ struct nf_flow_route {
                struct dst_entry                *dst;
                struct {
                        u32                     ifindex;
-+                      u16                     vid[NF_FLOW_TABLE_VLAN_MAX];
-+                      __be16                  vproto[NF_FLOW_TABLE_VLAN_MAX];
-+                      u8                      num_vlans;
++                      struct {
++                              u16             id;
++                              __be16          proto;
++                      } encap[NF_FLOW_TABLE_ENCAP_MAX];
++                      u8                      num_encaps;
                } in;
                struct {
                        u32                     ifindex;
@@ -66,33 +68,36 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
        }
  
        flow_tuple->iifidx = route->tuple[dir].in.ifindex;
-+      for (i = route->tuple[dir].in.num_vlans - 1; i >= 0; i--) {
-+              flow_tuple->in_vlan[j].id = route->tuple[dir].in.vid[i];
-+              flow_tuple->in_vlan[j].proto = route->tuple[dir].in.vproto[i];
++      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->in_vlan_num = route->tuple[dir].in.num_vlans;
++      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,35 @@ static bool ip_has_options(unsigned int
+@@ -159,17 +159,38 @@ static bool ip_has_options(unsigned int
        return thoff != sizeof(struct iphdr);
  }
  
-+static void nf_flow_tuple_vlan(struct sk_buff *skb,
-+                             struct flow_offload_tuple *tuple)
++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->in_vlan[0].id = skb_vlan_tag_get(skb);
-+              tuple->in_vlan[0].proto = skb->vlan_proto;
++              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->in_vlan[1].id = ntohs(veth->h_vlan_TCI);
-+              tuple->in_vlan[1].proto = skb->protocol;
++              tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
++              tuple->encap[i].proto = skb->protocol;
 +      }
 +}
 +
@@ -116,7 +121,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
        thoff = iph->ihl * 4;
  
        if (ip_is_fragment(iph) ||
-@@ -191,11 +209,11 @@ static int nf_flow_tuple_ip(struct sk_bu
+@@ -191,11 +212,11 @@ static int nf_flow_tuple_ip(struct sk_bu
                return -1;
  
        thoff = iph->ihl * 4;
@@ -131,19 +136,19 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  
        tuple->src_v4.s_addr    = iph->saddr;
        tuple->dst_v4.s_addr    = iph->daddr;
-@@ -204,6 +222,7 @@ static int nf_flow_tuple_ip(struct sk_bu
+@@ -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_vlan(skb, tuple);
++      nf_flow_tuple_encap(skb, tuple);
  
        return 0;
  }
-@@ -248,6 +267,37 @@ static unsigned int nf_flow_xmit_xfrm(st
+@@ -248,6 +270,40 @@ static unsigned int nf_flow_xmit_xfrm(st
        return NF_STOLEN;
  }
  
-+static bool nf_flow_skb_vlan_protocol(const struct sk_buff *skb, __be16 proto)
++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;
@@ -156,29 +161,37 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +      return false;
 +}
 +
-+static void nf_flow_vlan_pop(struct sk_buff *skb,
-+                           struct flow_offload_tuple_rhash *tuplehash)
++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.in_vlan_num; i++) {
++      for (i = 0; i < tuplehash->tuple.encap_num; i++) {
 +              if (skb_vlan_tag_present(skb)) {
 +                      __vlan_hwaccel_clear_tag(skb);
 +                      continue;
 +              }
-+              vlan_hdr = (struct vlan_hdr *)skb->data;
-+              __skb_pull(skb, VLAN_HLEN);
-+              vlan_set_encap_proto(skb, vlan_hdr);
-+              skb_reset_network_header(skb);
++              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)
-@@ -280,9 +330,11 @@ nf_flow_offload_ip_hook(void *priv, stru
-       unsigned int thoff;
+@@ -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;
@@ -186,12 +199,17 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  
 -      if (skb->protocol != htons(ETH_P_IP))
 +      if (skb->protocol != htons(ETH_P_IP) &&
-+          !nf_flow_skb_vlan_protocol(skb, 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)
-@@ -298,11 +350,15 @@ nf_flow_offload_ip_hook(void *priv, stru
-       if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
+@@ -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)))
@@ -209,17 +227,17 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
                return NF_ACCEPT;
  
        flow_offload_refresh(flow_table, flow);
-@@ -312,6 +368,9 @@ nf_flow_offload_ip_hook(void *priv, stru
+@@ -312,6 +375,9 @@ nf_flow_offload_ip_hook(void *priv, stru
                return NF_ACCEPT;
        }
  
-+      nf_flow_vlan_pop(skb, tuplehash);
++      nf_flow_encap_pop(skb, tuplehash);
 +      thoff -= offset;
 +
        if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
                return NF_DROP;
  
-@@ -479,14 +538,17 @@ static int nf_flow_nat_ipv6(const struct
+@@ -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)
  {
@@ -240,7 +258,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  
        switch (ip6h->nexthdr) {
        case IPPROTO_TCP:
-@@ -503,11 +565,11 @@ static int nf_flow_tuple_ipv6(struct sk_
+@@ -503,11 +572,11 @@ static int nf_flow_tuple_ipv6(struct sk_
                return -1;
  
        thoff = sizeof(*ip6h);
@@ -255,29 +273,35 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  
        tuple->src_v6           = ip6h->saddr;
        tuple->dst_v6           = ip6h->daddr;
-@@ -516,6 +578,7 @@ static int nf_flow_tuple_ipv6(struct sk_
+@@ -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_vlan(skb, tuple);
++      nf_flow_tuple_encap(skb, tuple);
  
        return 0;
  }
-@@ -533,9 +596,11 @@ nf_flow_offload_ipv6_hook(void *priv, st
+@@ -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_vlan_protocol(skb, 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)
-@@ -551,8 +616,11 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
+@@ -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,
@@ -290,33 +314,35 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
                return NF_ACCEPT;
  
        flow_offload_refresh(flow_table, flow);
-@@ -562,6 +630,8 @@ nf_flow_offload_ipv6_hook(void *priv, st
+@@ -562,6 +639,8 @@ nf_flow_offload_ipv6_hook(void *priv, st
                return NF_ACCEPT;
        }
  
-+      nf_flow_vlan_pop(skb, tuplehash);
++      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
-@@ -65,6 +65,9 @@ static int nft_dev_fill_forward_path(con
+@@ -66,6 +66,11 @@ static int nft_dev_fill_forward_path(con
  struct nft_forward_info {
-       const struct net_device *dev;
-+      __u16 vid[NF_FLOW_TABLE_VLAN_MAX];
-+      __be16 vproto[NF_FLOW_TABLE_VLAN_MAX];
-+      u8 num_vlans;
+       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;
-@@ -83,9 +86,22 @@ static void nft_dev_path_info(const stru
+@@ -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->dev = path->dev;
+                       info->indev = path->dev;
                        if (is_zero_ether_addr(info->h_source))
                                memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
 +
@@ -324,25 +350,26 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +                              break;
 +
 +                      /* DEV_PATH_VLAN */
-+                      if (info->num_vlans >= NF_FLOW_TABLE_VLAN_MAX) {
-+                              info->dev = NULL;
++                      if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
++                              info->indev = NULL;
 +                              break;
 +                      }
-+                      info->vid[info->num_vlans] = path->vlan.id;
-+                      info->vproto[info->num_vlans] = path->vlan.proto;
-+                      info->num_vlans++;
++                      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))
-@@ -93,7 +109,6 @@ static void nft_dev_path_info(const stru
+@@ -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->dev = NULL;
+                       info->indev = NULL;
                        break;
-@@ -127,6 +142,7 @@ static void nft_dev_forward_path(struct
+@@ -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];
@@ -350,15 +377,15 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  
        if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
                nft_dev_path_info(&stack, &info, ha);
-@@ -135,6 +151,11 @@ static void nft_dev_forward_path(struct
+@@ -138,6 +157,11 @@ static void nft_dev_forward_path(struct
                return;
  
-       route->tuple[!dir].in.ifindex = info.dev->ifindex;
-+      for (i = 0; i < info.num_vlans; i++) {
-+              route->tuple[!dir].in.vid[i] = info.vid[i];
-+              route->tuple[!dir].in.vproto[i] = info.vproto[i];
+       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_vlans = info.num_vlans;
++      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);
index fb5f73664803d8afd496f9f79e4e4a9ffade4c2f..9bd1f6b4c6d807e37779d0e52ca1bb85237e9892 100644 (file)
@@ -18,7 +18,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 @@ -847,10 +847,20 @@ struct net_device_path {
                        u16             id;
                        __be16          proto;
-               } vlan;
+               } encap;
 +              struct {
 +                      enum {
 +                              DEV_PATH_BR_VLAN_KEEP,
@@ -52,7 +52,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 --- 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->vlan.proto = vlan->vlan_proto;
+       path->encap.proto = vlan->vlan_proto;
        path->dev = ctx->dev;
        ctx->dev = vlan->real_dev;
 +      if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
diff --git a/target/linux/generic/pending-5.10/640-10-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch b/target/linux/generic/pending-5.10/640-10-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch
deleted file mode 100644 (file)
index 64a2b14..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 7 Dec 2020 20:31:48 +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>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -837,6 +837,7 @@ enum net_device_path_type {
-       DEV_PATH_ETHERNET = 0,
-       DEV_PATH_VLAN,
-       DEV_PATH_BRIDGE,
-+      DEV_PATH_DSA,
- };
- struct net_device_path {
-@@ -856,6 +857,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
-@@ -1582,6 +1582,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,
-@@ -1607,6 +1622,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-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
new file mode 100644 (file)
index 0000000..86fd6bf
--- /dev/null
@@ -0,0 +1,31 @@
+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
new file mode 100644 (file)
index 0000000..34d9182
--- /dev/null
@@ -0,0 +1,100 @@
+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
+@@ -837,6 +837,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-11-netfilter-flowtable-add-offload-support-for-xmit-pat.patch b/target/linux/generic/pending-5.10/640-11-netfilter-flowtable-add-offload-support-for-xmit-pat.patch
deleted file mode 100644 (file)
index 508dc90..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->in_vlan_num; i++) {
-+              struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
-+
-+              entry->id = FLOW_ACTION_VLAN_PUSH;
-+              entry->vlan.vid = other_tuple->in_vlan[i].id;
-+              entry->vlan.proto = other_tuple->in_vlan[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-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
new file mode 100644 (file)
index 0000000..3e7a9ea
--- /dev/null
@@ -0,0 +1,60 @@
+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
+@@ -838,6 +838,7 @@ enum net_device_path_type {
+       DEV_PATH_VLAN,
+       DEV_PATH_BRIDGE,
+       DEV_PATH_PPPOE,
++      DEV_PATH_DSA,
+ };
+ struct net_device_path {
+@@ -857,6 +858,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
+@@ -1582,6 +1582,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,
+@@ -1607,6 +1622,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-12-netfilter-nft_flow_offload-add-dsa-support.patch b/target/linux/generic/pending-5.10/640-12-netfilter-nft_flow_offload-add-dsa-support.patch
deleted file mode 100644 (file)
index 3689a47..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Mon, 18 Jan 2021 22:27:45 +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
-@@ -86,6 +86,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:
-                       info->dev = path->dev;
-                       if (is_zero_ether_addr(info->h_source))
-@@ -93,6 +94,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 */
-                       if (info->num_vlans >= NF_FLOW_TABLE_VLAN_MAX) {
diff --git a/target/linux/generic/pending-5.10/640-13-dsa-slave-add-support-for-TC_SETUP_FT.patch b/target/linux/generic/pending-5.10/640-13-dsa-slave-add-support-for-TC_SETUP_FT.patch
deleted file mode 100644 (file)
index 58a2064..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
-@@ -1202,14 +1202,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-13-netfilter-flowtable-add-pppoe-support.patch b/target/linux/generic/pending-5.10/640-13-netfilter-flowtable-add-pppoe-support.patch
new file mode 100644 (file)
index 0000000..6a14514
--- /dev/null
@@ -0,0 +1,263 @@
+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
+@@ -848,6 +848,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-bridge-vlan-filtering.patch b/target/linux/generic/pending-5.10/640-14-netfilter-nft_flow_offload-add-bridge-vlan-filtering.patch
deleted file mode 100644 (file)
index 2e83311..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
-@@ -112,6 +112,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->vid[info->num_vlans] = path->vlan.id;
-+                              info->vproto[info->num_vlans] = path->vlan.proto;
-+                              info->num_vlans++;
-+                              break;
-+                      case DEV_PATH_BR_VLAN_UNTAG:
-+                              info->num_vlans--;
-+                              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-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
new file mode 100644 (file)
index 0000000..7ff5dad
--- /dev/null
@@ -0,0 +1,30 @@
+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
new file mode 100644 (file)
index 0000000..696ea03
--- /dev/null
@@ -0,0 +1,308 @@
+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-15-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch b/target/linux/generic/pending-5.10/640-15-netfilter-nft_flow_offload-use-direct-xmit-if-hardwa.patch
deleted file mode 100644 (file)
index 1c65a5e..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Tue, 2 Feb 2021 17:10:07 +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/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -73,9 +73,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;
-@@ -131,6 +140,10 @@ static void nft_dev_path_info(const stru
-                       break;
-               }
-       }
-+
-+      if (nf_flowtable_hw_offload(flowtable) &&
-+          nft_is_valid_ether_device(info->dev))
-+              info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
- }
- static bool nft_flowtable_find_dev(const struct net_device *dev,
-@@ -162,7 +175,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.dev || !nft_flowtable_find_dev(info.dev, ft))
-               return;
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
new file mode 100644 (file)
index 0000000..58a2064
--- /dev/null
@@ -0,0 +1,53 @@
+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
+@@ -1202,14 +1202,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-16-netfilter-nft_flow_offload-fix-bridge-vlan-tag-handl.patch b/target/linux/generic/pending-5.10/640-16-netfilter-nft_flow_offload-fix-bridge-vlan-tag-handl.patch
deleted file mode 100644 (file)
index 5877e91..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 10 Feb 2021 10:23:38 +0100
-Subject: [PATCH] netfilter: nft_flow_offload: fix bridge vlan tag handling
-
-the brigde type uses the path->bridge.vlan_* fields
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -123,8 +123,8 @@ static void nft_dev_path_info(const stru
-                       switch (path->bridge.vlan_mode) {
-                       case DEV_PATH_BR_VLAN_TAG:
--                              info->vid[info->num_vlans] = path->vlan.id;
--                              info->vproto[info->num_vlans] = path->vlan.proto;
-+                              info->vid[info->num_vlans] = path->bridge.vlan_id;
-+                              info->vproto[info->num_vlans] = path->bridge.vlan_proto;
-                               info->num_vlans++;
-                               break;
-                       case DEV_PATH_BR_VLAN_UNTAG:
diff --git a/target/linux/generic/pending-5.10/640-17-netfilter-flowtable-rework-ingress-vlan-matching.patch b/target/linux/generic/pending-5.10/640-17-netfilter-flowtable-rework-ingress-vlan-matching.patch
deleted file mode 100644 (file)
index 67ec263..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 10 Feb 2021 19:39:23 +0100
-Subject: [PATCH] netfilter: flowtable: rework ingress vlan matching
-
-When dealing with bridges with VLAN filtering and DSA/switchdev offload,
-the hardware could offload adding a VLAN tag configured in the bridge.
-Since there doesn't seem to be an easy way to detect that, this patch
-reworks the code to optionally match the last VLAN tag that would otherwise
-be inserted by the bridge.
-This matters when bypassing the bridge and attaching an ingress hook on
-a DSA port below it.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -115,14 +115,15 @@ struct flow_offload_tuple {
-       u8                              l3proto;
-       u8                              l4proto;
--      struct {
--              u16                     id;
--              __be16                  proto;
--      } in_vlan[NF_FLOW_TABLE_VLAN_MAX];
-       /* All members above are keys for lookups, see flow_offload_hash(). */
-       struct { }                      __hash;
-+      struct {
-+              u16                     id;
-+              __be16                  proto;
-+      } in_vlan[NF_FLOW_TABLE_VLAN_MAX], in_pvid;
-+
-       u8                              dir:4,
-                                       xmit_type:2,
-                                       in_vlan_num:2;
---- a/net/netfilter/nf_flow_table_ip.c
-+++ b/net/netfilter/nf_flow_table_ip.c
-@@ -281,12 +281,13 @@ static bool nf_flow_skb_vlan_protocol(co
- }
- static void nf_flow_vlan_pop(struct sk_buff *skb,
--                           struct flow_offload_tuple_rhash *tuplehash)
-+                           struct flow_offload_tuple_rhash *tuplehash,
-+                           bool strip_pvid)
- {
-       struct vlan_hdr *vlan_hdr;
-       int i;
--      for (i = 0; i < tuplehash->tuple.in_vlan_num; i++) {
-+      for (i = 0; i < tuplehash->tuple.in_vlan_num + strip_pvid; i++) {
-               if (skb_vlan_tag_present(skb)) {
-                       __vlan_hwaccel_clear_tag(skb);
-                       continue;
-@@ -316,6 +317,31 @@ static unsigned int nf_flow_queue_xmit(s
-       return NF_STOLEN;
- }
-+static bool
-+nf_flow_offload_check_vlan(struct flow_offload_tuple *tuple,
-+                         struct flow_offload_tuple *flow_tuple,
-+                         bool *strip_pvid)
-+{
-+      int i, cur = 0;
-+
-+      if (flow_tuple->in_pvid.proto &&
-+          !memcmp(&tuple->in_vlan[0], &flow_tuple->in_pvid,
-+                  sizeof(tuple->in_vlan[0])))
-+              cur++;
-+
-+      *strip_pvid = cur;
-+
-+      for (i = 0; i < flow_tuple->in_vlan_num; i++, cur++) {
-+              if (!memcmp(&tuple->in_vlan[cur], &flow_tuple->in_vlan[i],
-+                          sizeof(tuple->in_vlan[0])))
-+                      continue;
-+
-+              return false;
-+      }
-+
-+      return true;
-+}
-+
- unsigned int
- nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
-                       const struct nf_hook_state *state)
-@@ -329,6 +355,7 @@ nf_flow_offload_ip_hook(void *priv, stru
-       struct rtable *rt;
-       unsigned int thoff;
-       struct iphdr *iph;
-+      bool strip_pvid;
-       __be32 nexthop;
-       u32 offset = 0;
-       int ret;
-@@ -344,6 +371,10 @@ nf_flow_offload_ip_hook(void *priv, stru
-       if (tuplehash == NULL)
-               return NF_ACCEPT;
-+      if (!nf_flow_offload_check_vlan(&tuple, &tuplehash->tuple,
-+                                      &strip_pvid))
-+              return NF_ACCEPT;
-+
-       dir = tuplehash->tuple.dir;
-       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
-@@ -368,7 +399,7 @@ nf_flow_offload_ip_hook(void *priv, stru
-               return NF_ACCEPT;
-       }
--      nf_flow_vlan_pop(skb, tuplehash);
-+      nf_flow_vlan_pop(skb, tuplehash, strip_pvid);
-       thoff -= offset;
-       if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
-@@ -596,6 +627,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       struct net_device *outdev;
-       struct ipv6hdr *ip6h;
-       struct rt6_info *rt;
-+      bool strip_pvid;
-       u32 offset = 0;
-       int ret;
-@@ -610,6 +642,10 @@ nf_flow_offload_ipv6_hook(void *priv, st
-       if (tuplehash == NULL)
-               return NF_ACCEPT;
-+      if (!nf_flow_offload_check_vlan(&tuple, &tuplehash->tuple,
-+                                      &strip_pvid))
-+              return NF_ACCEPT;
-+
-       dir = tuplehash->tuple.dir;
-       flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
-@@ -630,7 +666,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
-               return NF_ACCEPT;
-       }
--      nf_flow_vlan_pop(skb, tuplehash);
-+      nf_flow_vlan_pop(skb, tuplehash, strip_pvid);
-       if (skb_try_make_writable(skb, sizeof(*ip6h)))
-               return NF_DROP;
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
new file mode 100644 (file)
index 0000000..26d172b
--- /dev/null
@@ -0,0 +1,108 @@
+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-flowtable-handle-bridge-vlan-filter-offloa.patch b/target/linux/generic/pending-5.10/640-18-netfilter-flowtable-handle-bridge-vlan-filter-offloa.patch
deleted file mode 100644 (file)
index ba480f3..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 10 Feb 2021 19:44:33 +0100
-Subject: [PATCH] netfilter: flowtable: handle bridge vlan filter offload
- tags from DSA/switchdev
-
-When a switchdev/DSA port is an untagged member of a bridge vlan, ingress
-packets could potentially be tagged with the id of the VLAN.
-When the VLAN port group has been uploaded to switchdev, report the bridge
-tag mode as DEV_PATH_BR_VLAN_UNTAG_HW instead of DEV_PATH_BR_VLAN_UNTAG
-and handle it in netfilter flow offloading by storing the tag in the tuple
-in_pvid field.
-This allows the ingress hook to detect the optional tag and remove it for
-software offload. This tag processing is for ingress only, egress needs to be
-fully untagged
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -853,6 +853,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
-@@ -183,6 +183,10 @@ struct nf_flow_route {
-                       u32                     ifindex;
-                       u16                     vid[NF_FLOW_TABLE_VLAN_MAX];
-                       __be16                  vproto[NF_FLOW_TABLE_VLAN_MAX];
-+                      struct {
-+                              u16             id;
-+                              __be16          proto;
-+                      } pvid;
-                       u8                      num_vlans;
-               } in;
-               struct {
---- 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
-@@ -98,6 +98,8 @@ static int flow_offload_fill_route(struc
-               j++;
-       }
-       flow_tuple->in_vlan_num = route->tuple[dir].in.num_vlans;
-+      flow_tuple->in_pvid.id = route->tuple[dir].in.pvid.id;
-+      flow_tuple->in_pvid.proto = route->tuple[dir].in.pvid.proto;
-       switch (route->tuple[dir].xmit_type) {
-       case FLOW_OFFLOAD_XMIT_DIRECT:
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -67,6 +67,10 @@ struct nft_forward_info {
-       const struct net_device *dev;
-       __u16 vid[NF_FLOW_TABLE_VLAN_MAX];
-       __be16 vproto[NF_FLOW_TABLE_VLAN_MAX];
-+      struct {
-+              __u16 id;
-+              __be16 proto;
-+      } pvid;
-       u8 num_vlans;
-       u8 h_source[ETH_ALEN];
-       u8 h_dest[ETH_ALEN];
-@@ -127,6 +131,10 @@ static void nft_dev_path_info(const stru
-                               info->vproto[info->num_vlans] = path->bridge.vlan_proto;
-                               info->num_vlans++;
-                               break;
-+                      case DEV_PATH_BR_VLAN_UNTAG_HW:
-+                              info->pvid.id = info->vid[info->num_vlans - 1];
-+                              info->pvid.proto = info->vproto[info->num_vlans - 1];
-+                              fallthrough;
-                       case DEV_PATH_BR_VLAN_UNTAG:
-                               info->num_vlans--;
-                               break;
-@@ -185,6 +193,8 @@ static void nft_dev_forward_path(struct
-               route->tuple[!dir].in.vid[i] = info.vid[i];
-               route->tuple[!dir].in.vproto[i] = info.vproto[i];
-       }
-+      route->tuple[!dir].in.pvid.id = info.pvid.id;
-+      route->tuple[!dir].in.pvid.proto = info.pvid.proto;
-       route->tuple[!dir].in.num_vlans = info.num_vlans;
-       if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
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
new file mode 100644 (file)
index 0000000..33600a8
--- /dev/null
@@ -0,0 +1,122 @@
+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
+@@ -855,6 +855,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
new file mode 100644 (file)
index 0000000..3eaad27
--- /dev/null
@@ -0,0 +1,26 @@
+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
new file mode 100644 (file)
index 0000000..fcafff1
--- /dev/null
@@ -0,0 +1,31 @@
+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 6d0c84c494d5bf09e4653d192d0ae0ed5777b9b2..0422221468887d88fe83f7d812daf9b05199958c 100644 (file)
@@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
 
 --- a/include/linux/netdevice.h
 +++ b/include/linux/netdevice.h
-@@ -2031,6 +2031,8 @@ struct net_device {
+@@ -2033,6 +2033,8 @@ struct net_device {
        struct netdev_hw_addr_list      mc;
        struct netdev_hw_addr_list      dev_addrs;
  
index b2c2d00ec004565c540a27c9fb128975f105eda7..24b78b94d731c92a60f0e67607714adbebe1e2df 100644 (file)
@@ -50,7 +50,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
  };
  
  enum gro_result {
-@@ -2411,6 +2414,26 @@ void netif_napi_add(struct net_device *d
+@@ -2413,6 +2416,26 @@ void netif_napi_add(struct net_device *d
                    int (*poll)(struct napi_struct *, int), int weight);
  
  /**
index fa9cbcafc8953a1502299811957cbd925635ae3b..2f1ae6cde63a2e7e2a332c8fe410d43112e8191c 100644 (file)
@@ -80,7 +80,7 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  #endif /* MTK_ETH_H */
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -0,0 +1,478 @@
+@@ -0,0 +1,491 @@
 +// SPDX-License-Identifier: GPL-2.0-only
 +/*
 + *  Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
@@ -113,6 +113,10 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +              __be16 proto;
 +              u8 num;
 +      } vlan;
++      struct {
++              u16 sid;
++              u8 num;
++      } pppoe;
 +};
 +
 +struct mtk_flow_entry {
@@ -311,13 +315,20 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +                      break;
 +              case FLOW_ACTION_VLAN_PUSH:
 +                      if (data.vlan.num == 1 ||
-+                          data.vlan.proto != ETH_P_8021Q)
++                          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;
 +              }
@@ -392,11 +403,13 @@ Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 +      }
 +
 +      if (data.vlan.num == 1) {
-+              if (data.vlan.proto != ETH_P_8021Q)
++              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)