diff options
Diffstat (limited to 'target/linux/generic/backport-4.19/322-v4.16-netfilter-add-generic-flow-table-infrastructure.patch')
-rw-r--r-- | target/linux/generic/backport-4.19/322-v4.16-netfilter-add-generic-flow-table-infrastructure.patch | 586 |
1 files changed, 0 insertions, 586 deletions
diff --git a/target/linux/generic/backport-4.19/322-v4.16-netfilter-add-generic-flow-table-infrastructure.patch b/target/linux/generic/backport-4.19/322-v4.16-netfilter-add-generic-flow-table-infrastructure.patch deleted file mode 100644 index 16de9571a8..0000000000 --- a/target/linux/generic/backport-4.19/322-v4.16-netfilter-add-generic-flow-table-infrastructure.patch +++ /dev/null @@ -1,586 +0,0 @@ -From: Pablo Neira Ayuso <pablo@netfilter.org> -Date: Sun, 7 Jan 2018 01:04:11 +0100 -Subject: [PATCH] netfilter: add generic flow table infrastructure - -This patch defines the API to interact with flow tables, this allows to -add, delete and lookup for entries in the flow table. This also adds the -generic garbage code that removes entries that have expired, ie. no -traffic has been seen for a while. - -Users of the flow table infrastructure can delete entries via -flow_offload_dead(), which sets the dying bit, this signals the garbage -collector to release an entry from user context. - -Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> ---- - create mode 100644 net/netfilter/nf_flow_table.c - ---- a/include/net/netfilter/nf_flow_table.h -+++ b/include/net/netfilter/nf_flow_table.h -@@ -1,7 +1,12 @@ - #ifndef _NF_FLOW_TABLE_H - #define _NF_FLOW_TABLE_H - -+#include <linux/in.h> -+#include <linux/in6.h> -+#include <linux/netdevice.h> - #include <linux/rhashtable.h> -+#include <linux/rcupdate.h> -+#include <net/dst.h> - - struct nf_flowtable; - -@@ -20,4 +25,93 @@ struct nf_flowtable { - struct delayed_work gc_work; - }; - -+enum flow_offload_tuple_dir { -+ FLOW_OFFLOAD_DIR_ORIGINAL, -+ FLOW_OFFLOAD_DIR_REPLY, -+ __FLOW_OFFLOAD_DIR_MAX = FLOW_OFFLOAD_DIR_REPLY, -+}; -+#define FLOW_OFFLOAD_DIR_MAX (__FLOW_OFFLOAD_DIR_MAX + 1) -+ -+struct flow_offload_tuple { -+ union { -+ struct in_addr src_v4; -+ struct in6_addr src_v6; -+ }; -+ union { -+ struct in_addr dst_v4; -+ struct in6_addr dst_v6; -+ }; -+ struct { -+ __be16 src_port; -+ __be16 dst_port; -+ }; -+ -+ int iifidx; -+ -+ u8 l3proto; -+ u8 l4proto; -+ u8 dir; -+ -+ int oifidx; -+ -+ struct dst_entry *dst_cache; -+}; -+ -+struct flow_offload_tuple_rhash { -+ struct rhash_head node; -+ struct flow_offload_tuple tuple; -+}; -+ -+#define FLOW_OFFLOAD_SNAT 0x1 -+#define FLOW_OFFLOAD_DNAT 0x2 -+#define FLOW_OFFLOAD_DYING 0x4 -+ -+struct flow_offload { -+ struct flow_offload_tuple_rhash tuplehash[FLOW_OFFLOAD_DIR_MAX]; -+ u32 flags; -+ union { -+ /* Your private driver data here. */ -+ u32 timeout; -+ }; -+}; -+ -+#define NF_FLOW_TIMEOUT (30 * HZ) -+ -+struct nf_flow_route { -+ struct { -+ struct dst_entry *dst; -+ int ifindex; -+ } tuple[FLOW_OFFLOAD_DIR_MAX]; -+}; -+ -+struct flow_offload *flow_offload_alloc(struct nf_conn *ct, -+ struct nf_flow_route *route); -+void flow_offload_free(struct flow_offload *flow); -+ -+int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow); -+void flow_offload_del(struct nf_flowtable *flow_table, struct flow_offload *flow); -+struct flow_offload_tuple_rhash *flow_offload_lookup(struct nf_flowtable *flow_table, -+ struct flow_offload_tuple *tuple); -+int nf_flow_table_iterate(struct nf_flowtable *flow_table, -+ void (*iter)(struct flow_offload *flow, void *data), -+ void *data); -+void nf_flow_offload_work_gc(struct work_struct *work); -+extern const struct rhashtable_params nf_flow_offload_rhash_params; -+ -+void flow_offload_dead(struct flow_offload *flow); -+ -+int nf_flow_snat_port(const struct flow_offload *flow, -+ struct sk_buff *skb, unsigned int thoff, -+ u8 protocol, enum flow_offload_tuple_dir dir); -+int nf_flow_dnat_port(const struct flow_offload *flow, -+ struct sk_buff *skb, unsigned int thoff, -+ u8 protocol, enum flow_offload_tuple_dir dir); -+ -+struct flow_ports { -+ __be16 source, dest; -+}; -+ -+#define MODULE_ALIAS_NF_FLOWTABLE(family) \ -+ MODULE_ALIAS("nf-flowtable-" __stringify(family)) -+ - #endif /* _FLOW_OFFLOAD_H */ ---- a/net/netfilter/Kconfig -+++ b/net/netfilter/Kconfig -@@ -667,6 +667,13 @@ endif # NF_TABLES_NETDEV - - endif # NF_TABLES - -+config NF_FLOW_TABLE -+ tristate "Netfilter flow table module" -+ help -+ This option adds the flow table core infrastructure. -+ -+ To compile it as a module, choose M here. -+ - config NETFILTER_XTABLES - tristate "Netfilter Xtables support (required for ip_tables)" - default m if NETFILTER_ADVANCED=n ---- a/net/netfilter/Makefile -+++ b/net/netfilter/Makefile -@@ -110,6 +110,9 @@ obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_ - obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o - obj-$(CONFIG_NFT_FWD_NETDEV) += nft_fwd_netdev.o - -+# flow table infrastructure -+obj-$(CONFIG_NF_FLOW_TABLE) += nf_flow_table.o -+ - # generic X tables - obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o - ---- /dev/null -+++ b/net/netfilter/nf_flow_table.c -@@ -0,0 +1,429 @@ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/netfilter.h> -+#include <linux/rhashtable.h> -+#include <linux/netdevice.h> -+#include <net/netfilter/nf_flow_table.h> -+#include <net/netfilter/nf_conntrack.h> -+#include <net/netfilter/nf_conntrack_core.h> -+#include <net/netfilter/nf_conntrack_tuple.h> -+ -+struct flow_offload_entry { -+ struct flow_offload flow; -+ struct nf_conn *ct; -+ struct rcu_head rcu_head; -+}; -+ -+struct flow_offload * -+flow_offload_alloc(struct nf_conn *ct, struct nf_flow_route *route) -+{ -+ struct flow_offload_entry *entry; -+ struct flow_offload *flow; -+ -+ if (unlikely(nf_ct_is_dying(ct) || -+ !atomic_inc_not_zero(&ct->ct_general.use))) -+ return NULL; -+ -+ entry = kzalloc(sizeof(*entry), GFP_ATOMIC); -+ if (!entry) -+ goto err_ct_refcnt; -+ -+ flow = &entry->flow; -+ -+ if (!dst_hold_safe(route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].dst)) -+ goto err_dst_cache_original; -+ -+ if (!dst_hold_safe(route->tuple[FLOW_OFFLOAD_DIR_REPLY].dst)) -+ goto err_dst_cache_reply; -+ -+ entry->ct = ct; -+ -+ switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num) { -+ case NFPROTO_IPV4: -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v4 = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.in; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v4 = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v4 = -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.in; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v4 = -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.in; -+ break; -+ case NFPROTO_IPV6: -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6 = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.in6; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6 = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v6 = -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.in6; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v6 = -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.in6; -+ break; -+ } -+ -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l3proto = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l4proto = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.l3proto = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.l4proto = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; -+ -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_cache = -+ route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].dst; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_cache = -+ route->tuple[FLOW_OFFLOAD_DIR_REPLY].dst; -+ -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_port = -+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_port = -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.tcp.port; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port = -+ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; -+ -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dir = -+ FLOW_OFFLOAD_DIR_ORIGINAL; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dir = -+ FLOW_OFFLOAD_DIR_REPLY; -+ -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.iifidx = -+ route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].ifindex; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.oifidx = -+ route->tuple[FLOW_OFFLOAD_DIR_REPLY].ifindex; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.iifidx = -+ route->tuple[FLOW_OFFLOAD_DIR_REPLY].ifindex; -+ flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.oifidx = -+ route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].ifindex; -+ -+ if (ct->status & IPS_SRC_NAT) -+ flow->flags |= FLOW_OFFLOAD_SNAT; -+ else if (ct->status & IPS_DST_NAT) -+ flow->flags |= FLOW_OFFLOAD_DNAT; -+ -+ return flow; -+ -+err_dst_cache_reply: -+ dst_release(route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].dst); -+err_dst_cache_original: -+ kfree(entry); -+err_ct_refcnt: -+ nf_ct_put(ct); -+ -+ return NULL; -+} -+EXPORT_SYMBOL_GPL(flow_offload_alloc); -+ -+void flow_offload_free(struct flow_offload *flow) -+{ -+ struct flow_offload_entry *e; -+ -+ dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_cache); -+ dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_cache); -+ e = container_of(flow, struct flow_offload_entry, flow); -+ kfree(e); -+} -+EXPORT_SYMBOL_GPL(flow_offload_free); -+ -+void flow_offload_dead(struct flow_offload *flow) -+{ -+ flow->flags |= FLOW_OFFLOAD_DYING; -+} -+EXPORT_SYMBOL_GPL(flow_offload_dead); -+ -+int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) -+{ -+ flow->timeout = (u32)jiffies; -+ -+ rhashtable_insert_fast(&flow_table->rhashtable, -+ &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node, -+ *flow_table->type->params); -+ rhashtable_insert_fast(&flow_table->rhashtable, -+ &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, -+ *flow_table->type->params); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(flow_offload_add); -+ -+void flow_offload_del(struct nf_flowtable *flow_table, -+ struct flow_offload *flow) -+{ -+ struct flow_offload_entry *e; -+ -+ rhashtable_remove_fast(&flow_table->rhashtable, -+ &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node, -+ *flow_table->type->params); -+ rhashtable_remove_fast(&flow_table->rhashtable, -+ &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, -+ *flow_table->type->params); -+ -+ e = container_of(flow, struct flow_offload_entry, flow); -+ kfree_rcu(e, rcu_head); -+} -+EXPORT_SYMBOL_GPL(flow_offload_del); -+ -+struct flow_offload_tuple_rhash * -+flow_offload_lookup(struct nf_flowtable *flow_table, -+ struct flow_offload_tuple *tuple) -+{ -+ return rhashtable_lookup_fast(&flow_table->rhashtable, tuple, -+ *flow_table->type->params); -+} -+EXPORT_SYMBOL_GPL(flow_offload_lookup); -+ -+static void nf_flow_release_ct(const struct flow_offload *flow) -+{ -+ struct flow_offload_entry *e; -+ -+ e = container_of(flow, struct flow_offload_entry, flow); -+ nf_ct_delete(e->ct, 0, 0); -+ nf_ct_put(e->ct); -+} -+ -+int nf_flow_table_iterate(struct nf_flowtable *flow_table, -+ void (*iter)(struct flow_offload *flow, void *data), -+ void *data) -+{ -+ struct flow_offload_tuple_rhash *tuplehash; -+ struct rhashtable_iter hti; -+ struct flow_offload *flow; -+ int err; -+ -+ err = rhashtable_walk_init(&flow_table->rhashtable, &hti, GFP_KERNEL); -+ if (err) -+ return err; -+ -+ rhashtable_walk_start(&hti); -+ -+ while ((tuplehash = rhashtable_walk_next(&hti))) { -+ if (IS_ERR(tuplehash)) { -+ err = PTR_ERR(tuplehash); -+ if (err != -EAGAIN) -+ goto out; -+ -+ continue; -+ } -+ if (tuplehash->tuple.dir) -+ continue; -+ -+ flow = container_of(tuplehash, struct flow_offload, tuplehash[0]); -+ -+ iter(flow, data); -+ } -+out: -+ rhashtable_walk_stop(&hti); -+ rhashtable_walk_exit(&hti); -+ -+ return err; -+} -+EXPORT_SYMBOL_GPL(nf_flow_table_iterate); -+ -+static inline bool nf_flow_has_expired(const struct flow_offload *flow) -+{ -+ return (__s32)(flow->timeout - (u32)jiffies) <= 0; -+} -+ -+static inline bool nf_flow_is_dying(const struct flow_offload *flow) -+{ -+ return flow->flags & FLOW_OFFLOAD_DYING; -+} -+ -+void nf_flow_offload_work_gc(struct work_struct *work) -+{ -+ struct flow_offload_tuple_rhash *tuplehash; -+ struct nf_flowtable *flow_table; -+ struct rhashtable_iter hti; -+ struct flow_offload *flow; -+ int err; -+ -+ flow_table = container_of(work, struct nf_flowtable, gc_work.work); -+ -+ err = rhashtable_walk_init(&flow_table->rhashtable, &hti, GFP_KERNEL); -+ if (err) -+ goto schedule; -+ -+ rhashtable_walk_start(&hti); -+ -+ while ((tuplehash = rhashtable_walk_next(&hti))) { -+ if (IS_ERR(tuplehash)) { -+ err = PTR_ERR(tuplehash); -+ if (err != -EAGAIN) -+ goto out; -+ -+ continue; -+ } -+ if (tuplehash->tuple.dir) -+ continue; -+ -+ flow = container_of(tuplehash, struct flow_offload, tuplehash[0]); -+ -+ if (nf_flow_has_expired(flow) || -+ nf_flow_is_dying(flow)) { -+ flow_offload_del(flow_table, flow); -+ nf_flow_release_ct(flow); -+ } -+ } -+out: -+ rhashtable_walk_stop(&hti); -+ rhashtable_walk_exit(&hti); -+schedule: -+ queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ); -+} -+EXPORT_SYMBOL_GPL(nf_flow_offload_work_gc); -+ -+static u32 flow_offload_hash(const void *data, u32 len, u32 seed) -+{ -+ const struct flow_offload_tuple *tuple = data; -+ -+ return jhash(tuple, offsetof(struct flow_offload_tuple, dir), seed); -+} -+ -+static u32 flow_offload_hash_obj(const void *data, u32 len, u32 seed) -+{ -+ const struct flow_offload_tuple_rhash *tuplehash = data; -+ -+ return jhash(&tuplehash->tuple, offsetof(struct flow_offload_tuple, dir), seed); -+} -+ -+static int flow_offload_hash_cmp(struct rhashtable_compare_arg *arg, -+ const void *ptr) -+{ -+ const struct flow_offload_tuple *tuple = arg->key; -+ const struct flow_offload_tuple_rhash *x = ptr; -+ -+ if (memcmp(&x->tuple, tuple, offsetof(struct flow_offload_tuple, dir))) -+ return 1; -+ -+ return 0; -+} -+ -+const struct rhashtable_params nf_flow_offload_rhash_params = { -+ .head_offset = offsetof(struct flow_offload_tuple_rhash, node), -+ .hashfn = flow_offload_hash, -+ .obj_hashfn = flow_offload_hash_obj, -+ .obj_cmpfn = flow_offload_hash_cmp, -+ .automatic_shrinking = true, -+}; -+EXPORT_SYMBOL_GPL(nf_flow_offload_rhash_params); -+ -+static int nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff, -+ __be16 port, __be16 new_port) -+{ -+ struct tcphdr *tcph; -+ -+ if (!pskb_may_pull(skb, thoff + sizeof(*tcph)) || -+ skb_try_make_writable(skb, thoff + sizeof(*tcph))) -+ return -1; -+ -+ tcph = (void *)(skb_network_header(skb) + thoff); -+ inet_proto_csum_replace2(&tcph->check, skb, port, new_port, true); -+ -+ return 0; -+} -+ -+static int nf_flow_nat_port_udp(struct sk_buff *skb, unsigned int thoff, -+ __be16 port, __be16 new_port) -+{ -+ struct udphdr *udph; -+ -+ if (!pskb_may_pull(skb, thoff + sizeof(*udph)) || -+ skb_try_make_writable(skb, thoff + sizeof(*udph))) -+ return -1; -+ -+ udph = (void *)(skb_network_header(skb) + thoff); -+ if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) { -+ inet_proto_csum_replace2(&udph->check, skb, port, -+ new_port, true); -+ if (!udph->check) -+ udph->check = CSUM_MANGLED_0; -+ } -+ -+ return 0; -+} -+ -+static int nf_flow_nat_port(struct sk_buff *skb, unsigned int thoff, -+ u8 protocol, __be16 port, __be16 new_port) -+{ -+ switch (protocol) { -+ case IPPROTO_TCP: -+ if (nf_flow_nat_port_tcp(skb, thoff, port, new_port) < 0) -+ return NF_DROP; -+ break; -+ case IPPROTO_UDP: -+ if (nf_flow_nat_port_udp(skb, thoff, port, new_port) < 0) -+ return NF_DROP; -+ break; -+ } -+ -+ return 0; -+} -+ -+int nf_flow_snat_port(const struct flow_offload *flow, -+ struct sk_buff *skb, unsigned int thoff, -+ u8 protocol, enum flow_offload_tuple_dir dir) -+{ -+ struct flow_ports *hdr; -+ __be16 port, new_port; -+ -+ if (!pskb_may_pull(skb, thoff + sizeof(*hdr)) || -+ skb_try_make_writable(skb, thoff + sizeof(*hdr))) -+ return -1; -+ -+ hdr = (void *)(skb_network_header(skb) + thoff); -+ -+ switch (dir) { -+ case FLOW_OFFLOAD_DIR_ORIGINAL: -+ port = hdr->source; -+ new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port; -+ hdr->source = new_port; -+ break; -+ case FLOW_OFFLOAD_DIR_REPLY: -+ port = hdr->dest; -+ new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port; -+ hdr->dest = new_port; -+ break; -+ default: -+ return -1; -+ } -+ -+ return nf_flow_nat_port(skb, thoff, protocol, port, new_port); -+} -+EXPORT_SYMBOL_GPL(nf_flow_snat_port); -+ -+int nf_flow_dnat_port(const struct flow_offload *flow, -+ struct sk_buff *skb, unsigned int thoff, -+ u8 protocol, enum flow_offload_tuple_dir dir) -+{ -+ struct flow_ports *hdr; -+ __be16 port, new_port; -+ -+ if (!pskb_may_pull(skb, thoff + sizeof(*hdr)) || -+ skb_try_make_writable(skb, thoff + sizeof(*hdr))) -+ return -1; -+ -+ hdr = (void *)(skb_network_header(skb) + thoff); -+ -+ switch (dir) { -+ case FLOW_OFFLOAD_DIR_ORIGINAL: -+ port = hdr->dest; -+ new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_port; -+ hdr->dest = new_port; -+ break; -+ case FLOW_OFFLOAD_DIR_REPLY: -+ port = hdr->source; -+ new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_port; -+ hdr->source = new_port; -+ break; -+ default: -+ return -1; -+ } -+ -+ return nf_flow_nat_port(skb, thoff, protocol, port, new_port); -+} -+EXPORT_SYMBOL_GPL(nf_flow_dnat_port); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); |