--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
-@@ -75,8 +75,6 @@ config NF_TABLES_ARP
+@@ -76,8 +76,6 @@ config NF_TABLES_ARP
help
This option enables the ARP support for nf_tables.
config NF_FLOW_TABLE_IPV4
tristate "Netfilter flow table IPv4 module"
depends on NF_FLOW_TABLE
-@@ -85,6 +83,8 @@ config NF_FLOW_TABLE_IPV4
+@@ -86,6 +84,8 @@ config NF_FLOW_TABLE_IPV4
To compile it as a module, choose M here.
depends on !NF_CONNTRACK || NF_CONNTRACK
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
-@@ -69,7 +69,6 @@ config NFT_FIB_IPV6
+@@ -97,7 +97,6 @@ config NFT_FIB_IPV6
multicast or blackhole.
endif # NF_TABLES_IPV6
config NF_FLOW_TABLE_IPV6
tristate "Netfilter flow table IPv6 module"
-@@ -79,6 +78,8 @@ config NF_FLOW_TABLE_IPV6
+@@ -107,6 +106,8 @@ config NF_FLOW_TABLE_IPV6
To compile it as a module, choose M here.
depends on !NF_CONNTRACK || NF_CONNTRACK
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -665,8 +665,6 @@ config NFT_FIB_NETDEV
+@@ -671,8 +671,6 @@ config NFT_FIB_NETDEV
endif # NF_TABLES_NETDEV
config NF_FLOW_TABLE_INET
tristate "Netfilter flow table mixed IPv4/IPv6 module"
depends on NF_FLOW_TABLE
-@@ -675,11 +673,12 @@ config NF_FLOW_TABLE_INET
+@@ -681,11 +679,12 @@ config NF_FLOW_TABLE_INET
To compile it as a module, choose M here.
help
This option adds the flow table core infrastructure.
-@@ -959,6 +958,15 @@ config NETFILTER_XT_TARGET_NOTRACK
+@@ -974,6 +973,15 @@ config NETFILTER_XT_TARGET_NOTRACK
depends on NETFILTER_ADVANCED
select NETFILTER_XT_TARGET_CT
depends on NETFILTER_ADVANCED
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
-@@ -133,6 +133,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF
+@@ -134,6 +134,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_LED) += xt_LED.o
--- /dev/null
+++ b/net/netfilter/xt_FLOWOFFLOAD.c
-@@ -0,0 +1,338 @@
+@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2018 Felix Fietkau <nbd@nbd.name>
+ *
+#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_flow_table.h>
+struct xt_flowoffload_hook {
+ struct hlist_node list;
+ struct nf_hook_ops ops;
++ struct net *net;
+ bool registered;
+ bool used;
+};
+ continue;
+
+ hook->registered = true;
++ hook->net = dev_net(hook->ops.dev);
+ spin_unlock_bh(&hooks_lock);
-+ nf_register_net_hook(dev_net(hook->ops.dev), &hook->ops);
++ nf_register_net_hook(hook->net, &hook->ops);
+ spin_lock_bh(&hooks_lock);
+ goto restart;
+ }
+
+ hlist_del(&hook->list);
+ spin_unlock_bh(&hooks_lock);
-+ nf_unregister_net_hook(dev_net(hook->ops.dev), &hook->ops);
++ nf_unregister_net_hook(hook->net, &hook->ops);
+ kfree(hook);
+ spin_lock_bh(&hooks_lock);
+ goto restart;
+ return false;
+}
+
-+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)
++static struct dst_entry *
++xt_flowoffload_dst(const struct nf_conn *ct, enum ip_conntrack_dir dir,
++ const struct xt_action_param *par)
+{
-+ struct dst_entry *this_dst = skb_dst(skb);
-+ struct dst_entry *other_dst = NULL;
++ 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.dst.u3.ip;
++ fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip;
+ break;
+ case NFPROTO_IPV6:
-+ fl.u.ip6.daddr = ct->tuplehash[!dir].tuple.dst.u3.in6;
++ fl.u.ip6.saddr = ct->tuplehash[dir].tuple.dst.u3.in6;
++ fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6;
+ break;
+ }
+
-+ nf_route(xt_net(par), &other_dst, &fl, false, xt_family(par));
-+ if (!other_dst)
++ nf_route(xt_net(par), &dst, &fl, false, xt_family(par));
++
++ return dst;
++}
++
++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 dst_entry *this_dst, *other_dst;
++
++ this_dst = xt_flowoffload_dst(ct, dir, par);
++ other_dst = xt_flowoffload_dst(ct, !dir, par);
++ if (!this_dst || !other_dst)
+ return -ENOENT;
+
++ if (dst_xfrm(this_dst) || dst_xfrm(other_dst))
++ return -EINVAL;
++
+ route->tuple[dir].dst = this_dst;
+ route->tuple[dir].ifindex = xt_in(par)->ifindex;
+ route->tuple[!dir].dst = other_dst;
+static unsigned int
+flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
+{
++ const struct xt_flowoffload_target_info *info = par->targinfo;
+ enum ip_conntrack_info ctinfo;
+ enum ip_conntrack_dir dir;
+ struct nf_flow_route route;
+ xt_flowoffload_check_device(xt_in(par));
+ xt_flowoffload_check_device(xt_out(par));
+
++ if (info->flags & XT_FLOWOFFLOAD_HW)
++ nf_flow_offload_hw_add(xt_net(par), flow, ct);
++
+ return XT_CONTINUE;
+
+err_flow_add:
+
+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;
+}
+
+ .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 xt_flowoffload_table_init(struct nf_flowtable *table)
+{
++ table->flags = NF_FLOWTABLE_F_HW;
+ nf_flow_table_init(table);
+ return 0;
+}
#include <net/netfilter/nf_flow_table.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_core.h>
+--- /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 */