Flow offload infrastructure
Subsystems (3)
Pablo Neira Ayuso
maintainerv2 cover Flow offload infrastructure
Hi,
This patchset is a new iteration of the flow offload infrastructure [1]. This round adds a netlink control plane to configure flow table, so there is no one single flow table, as in the previous patchset, that gets registered unconditionally.
The following example shows how to create a flow table whose name is 'w', and a rule that specifies what flows are offloaded into this flow table.
table ip x {
flowtable w {
hook ingress priority -100 devices = { eth0, eth1 };
}
chain y {
type filter hook forward priority 0; policy accept;
ip protocol tcp flow offload @w
}
}The flow table control plane is useful to set on specific flow table configurations, including what devices you want to bind the flow table to, the priority in the netfilter pipeline at the ingress hooks, custom timeout for the flow table, and anything else that needs a toggle to be enabled/disabled through this control plane.
* Patch 1/6 adds the IPS_OFFLOAD status bit for conntrack, the conntrack garbage collector does not expire entries that has been offloaded. Conntrack entries that have been offloaded in the conntrack table look like this:
ipv4 2 tcp 6 src=10.141.10.2 dst=147.75.205.195 sport=36392 dport=443 src=147.75.205.195 dst=192.168.2.195 sport=443 dport=36392 [OFFLOAD] use=3
* Patch 2/6 adds a netlink control plane, that allows to create, list and delete flow tables. This patch also introduces the nf_flow_table object, that uses a rhashtable, garbage collector to remove entries that has expired, ie. those that we see no traffic for a while, and the flow table type, to allow to register IPv4 and IPv6 flow table. It's basically boiler plate netlink code that integrates into nf_tables.
* Patch 3/6 adds the generic flow table representation, this includes the flow table API to create, remove and lookup for entries in the flow table, and the generic garbage collector to expire entries. This is basically the common code to all flow table types.
* Patch 4/6 provides the IPv4 flow table flavour, that is the only type so far. This provides the ingress hook for IPv4, basically to look up for an entry in the flow table, then in case of hit, decrement TTL and pass it on to the neighbour layer for transmission at a given device, otherwise fall back to classic forwarding path.
* Patch 5/6 introduces the "flow offload" action. This allocates the flow entry and adds it to the flow table. This allows you to decide at what stage you want to offload flows through policy.
* Patch 6/6 adds the net_device ndo to offload flows to hardware, if driver implements this feature. This adds a new workqueue to configure hardware flow offload from user context. There is no driver so far available using this, but I've been approached by several hardware driver developers, from different companies, willing to implement this, so I'm inclined to keep this in a branch in my nf-next tree until we have the first client of this.
This is my TODO list, things I would like to finish:
* netns support.
* IPv6 support.
* Port address translation, so far only layer 3 NATs.
* PMTU interactions.
* stateful flow tracking.
Among other things that I would like to polish, just more fine grain details.
Cc'ing everyone that have provided feedback privately or publicly since the last time. If I forgot anyone to be Cc'ed, please accept my apologies.
Comments welcome, thanks.
[1] https://lwn.net/Articles/738214/
Pablo Neira Ayuso (6):
netfilter: nf_conntrack: add IPS_OFFLOAD status bit
netfilter: nf_tables: add flow table netlink frontend
netfilter: add generic flow table infrastructure
netfilter: flow table support for IPv4
netfilter: nf_tables: flow offload expression
netfilter: nft_flow_offload: add ndo hooks for hardware offload
include/linux/netdevice.h | 9 + include/net/netfilter/nf_flow_table.h | 96 +++ include/net/netfilter/nf_tables.h | 51 ++ include/uapi/linux/netfilter/nf_conntrack_common.h | 4 + include/uapi/linux/netfilter/nf_tables.h | 64 ++ net/ipv4/netfilter/Kconfig | 8 + net/ipv4/netfilter/Makefile | 3 + net/ipv4/netfilter/nf_flow_table_ipv4.c | 316 +++++++++ net/netfilter/Kconfig | 14 + net/netfilter/Makefile | 4 + net/netfilter/nf_conntrack_core.c | 19 + net/netfilter/nf_conntrack_netlink.c | 15 +- net/netfilter/nf_conntrack_proto_tcp.c | 3 + net/netfilter/nf_conntrack_standalone.c | 12 +- net/netfilter/nf_flow_table.c | 295 ++++++++ net/netfilter/nf_tables_api.c | 749 ++++++++++++++++++++- net/netfilter/nft_flow_offload.c | 353 ++++++++++ 17 files changed, 2009 insertions(+), 6 deletions(-) create mode 100644 include/net/netfilter/nf_flow_table.h create mode 100644 net/ipv4/netfilter/nf_flow_table_ipv4.c create mode 100644 net/netfilter/nf_flow_table.c create mode 100644 net/netfilter/nft_flow_offload.c
Signature · 1 line
2.11.0
Pablo Neira Ayuso
maintainerRFC PATCH v2 1/6 netfilter: nf_conntrack: add IPS_OFFLOAD status bit
This new bit tells us that the conntrack entry is owned by the flow table offload infrastructure.
# cat /proc/net/nf_conntrack ipv4 2 tcp 6 src=10.141.10.2 dst=147.75.205.195 sport=36392 dport=443 src=147.75.205.195 dst=192.168.2.195 sport=443 dport=36392 [OFFLOAD] mark=0 zone=0 use=2
Note the [OFFLOAD] tag in the listing.
The timer of such conntrack entries look like stopped from userspace. In practise, to make sure the conntrack entry does not go away, the conntrack timer is periodically set to an arbitrary large value that gets refreshed on every iteration from the garbage collector, so it never expires- and they display no internal state in the case of TCP flows. This allows us to save a bitcheck from the packet path via nf_ct_is_expired().
Conntrack entries that have been offloaded to the flow table infrastructure cannot be deleted/flushed via ctnetlink. The flow table infrastructure is also responsible for releasing this conntrack entry.
--- include/uapi/linux/netfilter/nf_conntrack_common.h | 4 ++++ net/netfilter/nf_conntrack_core.c | 19 +++++++++++++++++++ net/netfilter/nf_conntrack_netlink.c | 15 ++++++++++++++- net/netfilter/nf_conntrack_proto_tcp.c | 3 +++ net/netfilter/nf_conntrack_standalone.c | 12 ++++++++---- 5 files changed, 48 insertions(+), 5 deletions(-)
include/uapi/linux/netfilter/nf_conntrack_common.h +4 -0
@@ -100,6 +100,10 @@ enum ip_conntrack_status {IPS_HELPER_BIT =13 ,IPS_HELPER = (1 <<IPS_HELPER_BIT ), +/* Conntrack has been offloaded to flow table. */ +IPS_OFFLOAD_BIT =14 , +IPS_OFFLOAD = (1 <<IPS_OFFLOAD_BIT ), +/* Be careful here, modifying these bits can make things messy, * so don't let users modify them directly. */
net/netfilter/nf_conntrack_core.c +19 -0
@@ -901,6 +901,9 @@ static unsigned int early_drop_list(struct net *net,hlist_nulls_for_each_entry_rcu (h, n, head, hnnode) { tmp =nf_ct_tuplehash_to_ctrack (h); +if (test_bit (IPS_OFFLOAD_BIT , &tmp->status)) +continue ; +if (nf_ct_is_expired (tmp)) {nf_ct_gc_expired (tmp);continue ;
@@ -975,6 +978,17 @@ static bool gc_worker_can_early_drop(const struct nf_conn *ct)return false; } +#define DAY (86400 * HZ) + +/* Set an arbitrary timeout large enough not to ever expire, this save +* us a check for the IPS_OFFLOAD_BIT from the packet path via +* nf_ct_is_expired(). +*/ +static void nf_ct_offload_timeout (struct nf_conn *ct) +{ + ct->timeout = nfct_time_stamp +DAY ; +} +static void gc_worker (struct work_struct *work) {unsigned int min_interval =max (HZ /GC_MAX_BUCKETS_DIV ,1u );
@@ -1011,6 +1025,11 @@ static void gc_worker(struct work_struct *work) tmp =nf_ct_tuplehash_to_ctrack (h); scanned++; +if (test_bit (IPS_OFFLOAD_BIT , &tmp->status)) { +nf_ct_offload_timeout (tmp); +continue ; + } +if (nf_ct_is_expired (tmp)) {nf_ct_gc_expired (tmp); expired_count++;
net/netfilter/nf_conntrack_netlink.c +14 -1
@@ -1105,6 +1105,14 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { .len = NF_CT_LABELS_MAX_SIZE }, }; +static int ctnetlink_flush_iterate (struct nf_conn *ct,void *data) +{ +if (test_bit (IPS_OFFLOAD_BIT , &ct->status)) +return 0 ; + +return ctnetlink_filter_match (ct, data); +} +static int ctnetlink_flush_conntrack (struct net *net,const struct nlattr *const cda[],u32 portid,int report)
@@ -1117,7 +1125,7 @@ static int ctnetlink_flush_conntrack(struct net *net,return PTR_ERR (filter); } -nf_ct_iterate_cleanup_net (net, ctnetlink_filter_match, filter, +nf_ct_iterate_cleanup_net (net, ctnetlink_flush_iterate, filter, portid, report);kfree (filter);
@@ -1163,6 +1171,11 @@ static int ctnetlink_del_conntrack(struct net *net, struct sock *ctnl, ct =nf_ct_tuplehash_to_ctrack (h); +if (test_bit (IPS_OFFLOAD_BIT , &ct->status)) { +nf_ct_put (ct); +return -EBUSY ; + } +if (cda[CTA_ID ]) {u_int32_t id =ntohl (nla_get_be32 (cda[CTA_ID ]));if (id != (u32 )(unsigned long )ct) {
net/netfilter/nf_conntrack_proto_tcp.c +3 -0
@@ -305,6 +305,9 @@ static bool tcp_invert_tuple(struct nf_conntrack_tuple *tuple,/* Print out the private part of the conntrack. */ static void tcp_print_conntrack (struct seq_file *s,struct nf_conn *ct) { +if (test_bit (IPS_OFFLOAD_BIT , &ct->status)) +return ; +seq_printf (s,"%s " , tcp_conntrack_names[ct->proto.tcp.state]); }#endif
net/netfilter/nf_conntrack_standalone.c +8 -4
@@ -309,10 +309,12 @@ static int ct_seq_show(struct seq_file *s, void *v)WARN_ON (!l4proto); ret = -ENOSPC ; -seq_printf (s,"%-8s %u %-8s %u %ld " , +seq_printf (s,"%-8s %u %-8s %u " ,l3proto_name (l3proto->l3proto),nf_ct_l3num (ct), -l4proto_name (l4proto->l4proto),nf_ct_protonum (ct), -nf_ct_expires (ct) /HZ ); +l4proto_name (l4proto->l4proto),nf_ct_protonum (ct)); + +if (!test_bit (IPS_OFFLOAD_BIT , &ct->status)) +seq_printf (s,"%ld " ,nf_ct_expires (ct) /HZ );if (l4proto->print_conntrack) l4proto->print_conntrack (s, ct);
@@ -339,7 +341,9 @@ static int ct_seq_show(struct seq_file *s, void *v)if (seq_print_acct (s, ct,IP_CT_DIR_REPLY )) goto release; -if (test_bit (IPS_ASSURED_BIT , &ct->status)) +if (test_bit (IPS_OFFLOAD_BIT , &ct->status)) +seq_puts (s,"[OFFLOAD] " ); +else if (test_bit (IPS_ASSURED_BIT , &ct->status))seq_puts (s,"[ASSURED] " );if (seq_has_overflowed (s))
Signature · 1 line
2.11.0
Florian Westphal
maintainerRe: [PATCH nf-next RFC,v2 1/6] netfilter: nf_conntrack: add IPS_OFFLOAD status bit
Quoted 5 lines · Pablo Neira Ayuso
include/uapi/linux/netfilter/nf_conntrack_common.h +0
-0
include/uapi/linux/netfilter/nf_conntrack_common.h enum ip_conntrack_status { · line 109
Quoted 10 lines
include/uapi/linux/netfilter/nf_conntrack_common.h +4
-0@@ -100,6 +100,10 @@ enum ip_conntrack_status {IPS_HELPER_BIT =13 ,IPS_HELPER = (1 <<IPS_HELPER_BIT ), +/* Conntrack has been offloaded to flow table. */ +IPS_OFFLOAD_BIT =14 , +IPS_OFFLOAD = (1 <<IPS_OFFLOAD_BIT ), +/* Be careful here, modifying these bits can make things messy, * so don't let users modify them directly. */
I think this new bit has to be added to the UNCHANGEABLE mask below.
net/netfilter/nf_conntrack_core.c static unsigned int early_drop_list(struct net *net, · line 906
Quoted 11 lines
net/netfilter/nf_conntrack_core.c +3
-0@@ -901,6 +901,9 @@ static unsigned int early_drop_list(struct net *net,hlist_nulls_for_each_entry_rcu (h, n, head, hnnode) { tmp =nf_ct_tuplehash_to_ctrack (h); +if (test_bit (IPS_OFFLOAD_BIT , &tmp->status)) +continue ; +
nit: I would move this below the ASSURED bit check, AFAIU most (all?) offloaded conntracks are not in ASSURED state since they never see two-way communication but in case we've mixed flows or no offloading in place then the ASSURED check takes care of skipping earlydrop already.
net/netfilter/nf_conntrack_core.c static bool gc_worker_can_early_drop(const struct nf_conn *ct) · line 990
Quoted 8 lines
net/netfilter/nf_conntrack_core.c +8
-0+/* Set an arbitrary timeout large enough not to ever expire, this save +* us a check for the IPS_OFFLOAD_BIT from the packet path via +* nf_ct_is_expired(). +*/ +static void nf_ct_offload_timeout (struct nf_conn *ct) +{ + ct->timeout = nfct_time_stamp +DAY ; +}
Not sure if its worth adding a test to avoid unconditional write, e.g. something like
net/netfilter/nf_conntrack_core.c static bool gc_worker_can_early_drop(const struct nf_conn *ct) · line 988
Quoted 2 lines
net/netfilter/nf_conntrack_core.c +2
-0+static void nf_ct_offload_timeout (struct nf_conn *ct) +{
if (nf_ct_expires(ct) < DAY/2))
Quoted 1 line
+
ct->timeout=nfct_time_stamp+ DAY;
but perhaps not worth it, gc_worker is infrequent.
Pablo Neira Ayuso
maintainerRe: [PATCH nf-next RFC,v2 1/6] netfilter: nf_conntrack: add IPS_OFFLOAD status bit
Quoted 9 lines · Florian Westphal
Quoted 8 lines · Pablo Neira Ayuso
include/uapi/linux/netfilter/nf_conntrack_common.h +0
-0@@ -100,6 +100,10 @@ enum ip_conntrack_status {IPS_HELPER_BIT =13 , IPS_HELPER = (1 << IPS_HELPER_BIT),
Quoted 9 lines
include/uapi/linux/netfilter/nf_conntrack_common.h enum ip_conntrack_status { · line 109
Quoted 7 lines
include/uapi/linux/netfilter/nf_conntrack_common.h +4
-0+/* Conntrack has been offloaded to flow table. */ +IPS_OFFLOAD_BIT =14 , + IPS_OFFLOAD = (1 << IPS_OFFLOAD_BIT), +/* Be careful here, modifying these bits can make things messy, * so don't let users modify them directly. */ I think this new bit has to be added to the UNCHANGEABLE mask below.
Right.
Quoted 8 lines
Quoted 8 lines
net/netfilter/nf_conntrack_core.c +0
-0@@ -901,6 +901,9 @@ static unsigned int early_drop_list(struct net *net,hlist_nulls_for_each_entry_rcu (h, n, head, hnnode) { tmp =nf_ct_tuplehash_to_ctrack (h);
Quoted 9 lines
net/netfilter/nf_conntrack_core.c static unsigned int early_drop_list(struct net *net, · line 906
Quoted 3 lines
net/netfilter/nf_conntrack_core.c +3
-0+if (test_bit (IPS_OFFLOAD_BIT , &tmp->status)) +continue ; +nit: I would move this below the ASSURED bit check, AFAIU most (all?) offloaded conntracks are not in ASSURED state since they never see two-way communication but in case we've mixed flows or no offloading in place then the ASSURED check takes care of skipping earlydrop already.
Offload happens once we enter established state (or later if your rule postpone it to a later stage), that is before we observe the full 3-way handshake in tcp, hence the assured bit. So I think we still need this here.
Quoted 12 lines
net/netfilter/nf_conntrack_core.c static bool gc_worker_can_early_drop(const struct nf_conn *ct) · line 990
Quoted 8 lines
net/netfilter/nf_conntrack_core.c +8
-0+/* Set an arbitrary timeout large enough not to ever expire, this save +* us a check for the IPS_OFFLOAD_BIT from the packet path via +* nf_ct_is_expired(). +*/ +static void nf_ct_offload_timeout (struct nf_conn *ct) +{ + ct->timeout = nfct_time_stamp +DAY ; +}Not sure if its worth adding a test to avoid unconditional write, e.g. something like
Quoted 6 lines
net/netfilter/nf_conntrack_core.c static bool gc_worker_can_early_drop(const struct nf_conn *ct) · line 988
Quoted 2 lines
net/netfilter/nf_conntrack_core.c +2
-0+static void nf_ct_offload_timeout (struct nf_conn *ct) +{if (nf_ct_expires(ct) < DAY/2))Quoted 1 line
+
ct->timeout=nfct_time_stamp+ DAY;but perhaps not worth it,
gc_workeris infrequent.
That's fine, I'll do this.
Pablo Neira Ayuso
maintainerRFC PATCH v2 2/6 netfilter: nf_tables: add flow table netlink frontend
In reply to Pablo Neira AyusoThis patch introduces a netlink control plane to create, delete and dump flow tables. Flow tables are identified by name, this name is used from rules to refer to an specific flow table. Flow tables use the rhashtable class and a generic garbage collector to remove expired entries.
This also adds the infrastructure to add different flow table types, so we can add one for each layer 3 protocol family.
--- include/net/netfilter/nf_flow_table.h | 24 + include/net/netfilter/nf_tables.h | 51 +++ include/uapi/linux/netfilter/nf_tables.h | 53 +++ net/netfilter/nf_tables_api.c | 749 ++++++++++++++++++++++++++++++- 4 files changed, 876 insertions(+), 1 deletion(-) create mode 100644 include/net/netfilter/nf_flow_table.h
include/net/netfilter/nf_flow_table.h new file +24 -0
@@ -0,0 +1,24 @@ +#ifndef _NF_FLOW_TABLE_H +#define _NF_FLOW_TABLE_H + +#include <linux/rhashtable.h> + +struct nf_flowtable ; + +struct nf_flowtable_type { +struct list_head list; +int family; +int (*init)(struct nf_flowtable *); +void (*destroy)(struct nf_flowtable *); +const struct rhashtable_params *params; +nf_hookfn *hook; +struct module *owner; +}; + +struct nf_flowtable { +struct rhashtable rhashtable; +const struct nf_flowtable_type *type; +struct delayed_work gc_work; +}; + +#endif /* _FLOW_OFFLOAD_H */
include/net/netfilter/nf_tables.h +51 -0
85 lines not on this page · raw message
include/uapi/linux/netfilter/nf_tables.h +53 -0
71 lines not on this page · raw message
net/netfilter/nf_tables_api.c +748 -1
860 lines not on this page · raw message
Signature · 1 line
2.11.0
Pablo Neira Ayuso
maintainerRFC PATCH v2 3/6 netfilter: add generic flow table infrastructure
In reply to Pablo Neira AyusoThis 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.
--- include/net/netfilter/nf_flow_table.h | 71 +++++++++ net/netfilter/Kconfig | 7 + net/netfilter/Makefile | 3 + net/netfilter/nf_flow_table.c | 269 ++++++++++++++++++++++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 net/netfilter/nf_flow_table.c
include/net/netfilter/nf_flow_table.h +71 -0
@@ -1,7 +1,11 @@#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> struct nf_flowtable ;
@@ -21,4 +25,71 @@ 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; + }; + +u8 l3proto; +u8 l4proto; +u8 dir; + +int iifidx; +int oifidx; + +union { +__be32 gateway; +struct in6_addr gateway6; + }; +}; + +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; + }; +}; + +struct flow_offload *flow_offload_alloc (struct nf_conn *ct,int *ifindex, +union nf_inet_addr *gw); +void flow_offload_free (const 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); + +void flow_offload_dead (struct flow_offload *flow); +#endif /* _FLOW_OFFLOAD_H */
net/netfilter/Kconfig +7 -0
@@ -649,6 +649,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
net/netfilter/Makefile +3 -0
@@ -106,6 +106,9 @@ obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_netdev.o 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
net/netfilter/nf_flow_table.c new file +269 -0
269 lines not on this page · raw message
Signature · 1 line
2.11.0
Pablo Neira Ayuso
maintainerRFC PATCH v2 4/6 netfilter: flow table support for IPv4
In reply to Pablo Neira AyusoThis patch adds the IPv4 flow table type, that implements the datapath flow table to forward IPv4 traffic. Rationale is:
1) Look up for the packet in the flow table, from the ingress hook.
2) If there's a hit, decrement ttl and pass it on to the neighbour layer for transmission.
3) If there's a miss, packet is passed up to the classic forwarding path.
This patch also supports layer 3 source and destination NAT.
--- net/ipv4/netfilter/Kconfig | 8 + net/ipv4/netfilter/Makefile | 3 + net/ipv4/netfilter/nf_flow_table_ipv4.c | 316 ++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 net/ipv4/netfilter/nf_flow_table_ipv4.c
net/ipv4/netfilter/Kconfig +8 -0
@@ -177,6 +177,14 @@ config NF_NAT_H323 endif # NF_NAT_IPV4 +config NF_FLOW_TABLE_IPV4 + select NF_FLOW_TABLE + tristate "Netfilter flow table IPv4 module" + help + This option adds the flow table IPv4 support. + + To compile it as a module, choose M here. + config IP_NF_IPTABLES tristate "IP tables support (required for filtering/masq/NAT)" default m if NETFILTER_ADVANCED=n
net/ipv4/netfilter/Makefile +3 -0
@@ -52,6 +52,9 @@ obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o +# flow table support +obj-$(CONFIG_NF_FLOW_TABLE_IPV4) += nf_flow_table_ipv4.o + # matches obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o
net/ipv4/netfilter/nf_flow_table_ipv4.c new file +316 -0
316 lines not on this page · raw message
Signature · 1 line
2.11.0
Florian Westphal
maintainerRe: [PATCH nf-next RFC,v2 4/6] netfilter: flow table support for IPv4
Quoted 8 lines · Pablo Neira Ayuso
This patch adds the IPv4 flow table type, that implements the datapath flow table to forward IPv4 traffic. Rationale is:
1) Look up for the packet in the flow table, from the ingress hook.
2) If there's a hit, decrement ttl and pass it on to the neighbour layer for transmission.
3) If there's a miss, packet is passed up to the classic forwarding path.
Is there a plan to also handle zone IDs in future?
I't going to be messy for sure since we'd need to tell HW how to do the zone mapping. Perhaps only support a builtin list, e.g. vlan id == zone...?
Don't yet see how it could be done in a generic way as the mappings can be arbitrarily complex.
Right now afaics one could install one flow table per zone and map this in nft, but then we still miss the part that tells the hardware how the zone identifier was derived.
net/ipv4/netfilter/nf_flow_table_ipv4.c line 159
Quoted 3 lines
net/ipv4/netfilter/nf_flow_table_ipv4.c new file +3
-0+static bool ip_has_options (unsigned int thoff) +{ +return thoff >sizeof (struct iphdr );
I'd use
thoff != sizeof(...)
to catch case where ihl is < struct iphdr.
Quoted 9 lines
net/ipv4/netfilter/nf_flow_table_ipv4.c new file +9
-0+nf_flow_offload_hook(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ +struct flow_offload_tuple_rhash *tuplehash; +struct nf_flowtable *flow_table = priv; +struct flow_offload_tuple tuple = {}; +union nf_inet_addr nexthop; +struct flow_offload *flow; +struct net_device *outdev;
net/ipv4/netfilter/nf_flow_table_ipv4.c line 220
Quoted 10 lines
net/ipv4/netfilter/nf_flow_table_ipv4.c new file +10
-0+struct iphdr *iph; + +if (nf_flow_tuple_ip (skb, &tuple) <0 ) +return NF_ACCEPT ; + + tuplehash =flow_offload_lookup (flow_table, &tuple); +if (tuplehash ==NULL ) +return NF_ACCEPT ; + + outdev =dev_get_by_index_rcu (&init_net, tuplehash->tuple.oifidx);
state->net ?
Pablo Neira Ayuso
maintainerRe: [PATCH nf-next RFC,v2 4/6] netfilter: flow table support for IPv4
Quoted 11 lines · Florian Westphal
Quoted 8 lines · Pablo Neira Ayuso
This patch adds the IPv4 flow table type, that implements the datapath flow table to forward IPv4 traffic. Rationale is:
1) Look up for the packet in the flow table, from the ingress hook.
2) If there's a hit, decrement ttl and pass it on to the neighbour layer for transmission.
3) If there's a miss, packet is passed up to the classic forwarding path.Is there a plan to also handle zone IDs in future?
Zone ID is meaningful to whoever applies the policy: in this offload approach this patchset implements, the policy resides in the kernel.
Quoted 3 lines
I't going to be messy for sure since we'd need to tell HW how to do the zone mapping. Perhaps only support a builtin list, e.g. vlan id == zone...?
I've been considering a more simple solution, ie. add the input ifindex device in the flowtable hash lookup, as part of the flow tuple. All examples I've been observing for zones are basically mapping network interfaces to zones.
Quoted 7 lines
Don't yet see how it could be done in a generic way as the mappings can be arbitrarily complex.
Right now afaics one could install one flow table per zone and map this in nft, but then we still miss the part that tells the hardware how the zone identifier was derived.
Quoted 8 lines
net/ipv4/netfilter/nf_flow_table_ipv4.c line 159
Quoted 3 lines
net/ipv4/netfilter/nf_flow_table_ipv4.c new file +3
-0+static bool ip_has_options (unsigned int thoff) +{ +return thoff >sizeof (struct iphdr );I'd use
thoff != sizeof(...)to catch case where ihl is < struct iphdr.
ok.
Quoted 13 lines
Quoted 13 lines
net/ipv4/netfilter/nf_flow_table_ipv4.c new file +13
-0+nf_flow_offload_hook(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ +struct flow_offload_tuple_rhash *tuplehash; +struct nf_flowtable *flow_table = priv; +struct flow_offload_tuple tuple = {}; +union nf_inet_addr nexthop; +struct flow_offload *flow; +struct net_device *outdev; +struct iphdr *iph; + +if (nf_flow_tuple_ip (skb, &tuple) <0 ) +return NF_ACCEPT ;
Quoted 8 lines
net/ipv4/netfilter/nf_flow_table_ipv4.c line 220
Quoted 6 lines
net/ipv4/netfilter/nf_flow_table_ipv4.c new file +6
-0+ + tuplehash =flow_offload_lookup (flow_table, &tuple); +if (tuplehash ==NULL ) +return NF_ACCEPT ; + + outdev =dev_get_by_index_rcu (&init_net, tuplehash->tuple.oifidx);
state->net?
Yes, netns support is in my TODO list.
Pablo Neira Ayuso
maintainerRFC PATCH v2 5/6 netfilter: nf_tables: flow offload expression
In reply to Pablo Neira AyusoAdd new instruction for the nf_tables VM that allows us to specify what flows are offloaded into a given flow table via name. This new instruction creates the flow entry and adds it to the flow table.
Only established flows, ie. we have seen traffic in both directions, are added to the flow table. You can still decide to offload entries at a later stage via packet counting or checking the ct status in case you want to offload assured conntracks.
This has an explicit dependency with the conntrack subsystem.
--- include/uapi/linux/netfilter/nf_tables.h | 11 ++ net/netfilter/Kconfig | 7 + net/netfilter/Makefile | 1 + net/netfilter/nft_flow_offload.c | 274 +++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+) create mode 100644 net/netfilter/nft_flow_offload.c
include/uapi/linux/netfilter/nf_tables.h +11 -0
@@ -954,6 +954,17 @@ enum nft_ct_attributes { };#define NFTA_CT_MAX (__NFTA_CT_MAX - 1) +/** +* enum nft_flow_attributes - ct offload expression attributes +* @NFTA_FLOW_TABLE_NAME: flow table name (NLA_STRING) +*/ +enum nft_offload_attributes { +NFTA_FLOW_UNSPEC , +NFTA_FLOW_TABLE_NAME , + __NFTA_FLOW_MAX, +}; +#define NFTA_FLOW_MAX (__NFTA_FLOW_MAX - 1) +enum nft_limit_type {NFT_LIMIT_PKTS ,NFT_LIMIT_PKT_BYTES
net/netfilter/Kconfig +7 -0
@@ -497,6 +497,13 @@ config NFT_CT This option adds the "ct" expression that you can use to match connection tracking information such as the flow state. +config NFT_FLOW_OFFLOAD + depends on NF_CONNTRACK + tristate "Netfilter nf_tables hardware flow offload module" + help + This option adds the "flow_offload" expression that you can use to + choose what flows are placed into the hardware. + config NFT_SET_RBTREE tristate "Netfilter nf_tables rbtree set module" help
net/netfilter/Makefile +1 -0
@@ -83,6 +83,7 @@ obj-$(CONFIG_NFT_META) += nft_meta.o obj-$(CONFIG_NFT_RT) += nft_rt.o obj-$(CONFIG_NFT_NUMGEN) += nft_numgen.o obj-$(CONFIG_NFT_CT) += nft_ct.o +obj-$(CONFIG_NFT_FLOW_OFFLOAD) += nft_flow_offload.o obj-$(CONFIG_NFT_LIMIT) += nft_limit.o obj-$(CONFIG_NFT_NAT) += nft_nat.o obj-$(CONFIG_NFT_OBJREF) += nft_objref.o
net/netfilter/nft_flow_offload.c new file +274 -0
274 lines not on this page · raw message
Signature · 1 line
2.11.0
Pablo Neira Ayuso
maintainerRFC PATCH v2 6/6 netfilter: nft_flow_offload: add ndo hooks for hardware offload
In reply to Pablo Neira AyusoThis patch adds the infrastructure to offload flows to hardware, in case the nic/switch comes with built-in flow tables capabilities.
If the hardware comes with no hardware flow tables or they have limitations in terms of features, this falls back to the software generic flow table implementation.
The software flow table garbage collector skips entries that resides in the hardware, so the hardware will be responsible for releasing this flow table entry too via flow_offload_dead(). In the next garbage collector run, this removes the entries both in the software and hardware flow table from user context.
---
@Florian: I still owe you one here, you mentioned about inmediate schedule of the workqueue thread, and I need to revisit this, the quick patch I made is hitting splats when calling queue_delayed_work() from packet path, this may be my fault though.
include/linux/netdevice.h | 9 ++++ include/net/netfilter/nf_flow_table.h | 1 + net/netfilter/nf_flow_table.c | 26 ++++++++++++ net/netfilter/nft_flow_offload.c | 79 +++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+)
include/linux/netdevice.h +9 -0
@@ -826,6 +826,13 @@ struct xfrmdev_ops { };#endif +struct flow_offload ; + +enum flow_offload_type { +FLOW_OFFLOAD_ADD =0 , +FLOW_OFFLOAD_DEL , +}; +/* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are
@@ -1281,6 +1288,8 @@ struct net_device_ops {int (*ndo_bridge_dellink)(struct net_device *dev,struct nlmsghdr *nlh,u16 flags); +int (*ndo_flow_offload)(enum flow_offload_type type, +struct flow_offload *flow);int (*ndo_change_carrier)(struct net_device *dev,bool new_carrier); int (*ndo_get_phys_port_id)(struct net_device *dev,
include/net/netfilter/nf_flow_table.h +1 -0
@@ -67,6 +67,7 @@ struct flow_offload_tuple_rhash {#define FLOW_OFFLOAD_SNAT 0x1#define FLOW_OFFLOAD_DNAT 0x2#define FLOW_OFFLOAD_DYING 0x4 +#define FLOW_OFFLOAD_HW 0x8struct flow_offload {struct flow_offload_tuple_rhash tuplehash[FLOW_OFFLOAD_DIR_MAX ];
net/netfilter/nf_flow_table.c +26 -0
@@ -212,6 +212,21 @@ int nf_flow_table_iterate(struct nf_flowtable *flow_table, }EXPORT_SYMBOL_GPL (nf_flow_table_iterate); +static void flow_offload_hw_del (struct flow_offload *flow) +{ +struct net_device *indev; +int ret, ifindex; + +rtnl_lock (); + ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL ].tuple.iifidx; + indev =__dev_get_by_index (&init_net, ifindex); +if (WARN_ON (!indev)) +return ; + + ret = indev->netdev_ops->ndo_flow_offload (FLOW_OFFLOAD_DEL , flow); +rtnl_unlock (); +} +static inline bool nf_flow_has_expired (const struct flow_offload *flow) {return (__s32)(flow->timeout - (u32 )jiffies) <=0 ;
@@ -222,6 +237,11 @@ static inline bool nf_flow_is_dying(const struct flow_offload *flow)return flow->flags &FLOW_OFFLOAD_DYING ; } +static inline bool nf_flow_in_hw (const struct flow_offload *flow) +{ +return flow->flags &FLOW_OFFLOAD_HW ; +} +void nf_flow_offload_work_gc (struct work_struct *work) {struct flow_offload_tuple_rhash *tuplehash;
@@ -250,10 +270,16 @@ void nf_flow_offload_work_gc(struct work_struct *work) flow = container_of(tuplehash, struct flow_offload, tuplehash[0]); +if (nf_flow_in_hw (flow) && + !nf_flow_is_dying (flow)) +continue ; +if (nf_flow_has_expired (flow) ||nf_flow_is_dying (flow)) {flow_offload_del (flow_table, flow);nf_flow_release_ct (flow); +if (nf_flow_in_hw (flow)) +flow_offload_hw_del (flow); } counter++; }
net/netfilter/nft_flow_offload.c +79 -0
109 lines not on this page · raw message
Signature · 1 line
2.11.0
Florian Westphal
maintainerRe: [PATCH nf-next RFC,v2 6/6] netfilter: nft_flow_offload: add ndo hooks for hardware offload
Quoted 6 lines · Pablo Neira Ayuso
The software flow table garbage collector skips entries that resides in the hardware, so the hardware will be responsible for releasing this flow table entry too via
flow_offload_dead(). In the next garbage collector run, this removes the entries both in the software and hardware flow table from user context.
Quoted 6 lines
Signed-off-by Pablo Neira Ayuso <[email protected]>---
@Florian: I still owe you one here, you mentioned about inmediate schedule of the workqueue thread, and I need to revisit this, the quick patch I made is hitting splats when callingqueue_delayed_work()from packet path, this may be my fault though.
OK. IIRC I had suggested to just use schedule_work() instead. In most cases (assuming system is busy) the workqueue will already be pending anyway.
Quoted 6 lines
net/netfilter/nf_flow_table.c +0
-0@@ -212,6 +212,21 @@ int nf_flow_table_iterate(struct nf_flowtable *flow_table, }
net/netfilter/nf_flow_table.c int nf_flow_table_iterate(struct nf_flowtable *flow_table, · line 222
Quoted 10 lines
net/netfilter/nf_flow_table.c +8
-0EXPORT_SYMBOL_GPL (nf_flow_table_iterate); +static void flow_offload_hw_del (struct flow_offload *flow) +{ +struct net_device *indev; +int ret, ifindex; + +rtnl_lock (); + ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL ].tuple.iifidx; + indev =__dev_get_by_index (&init_net, ifindex);
I think this should pass struct net * as arg to flow_offload_hw_del.
net/netfilter/nf_flow_table.c int nf_flow_table_iterate(struct nf_flowtable *flow_table, · line 228
Quoted 6 lines
net/netfilter/nf_flow_table.c +6
-0+if (WARN_ON (!indev)) +return ; + + ret = indev->netdev_ops->ndo_flow_offload (FLOW_OFFLOAD_DEL , flow); +rtnl_unlock (); +}
Please no rtnl lock unless absolutely needed. Seems this could even avoid the mutex completely by using dev_get_by_index + dev_put.
net/netfilter/nf_flow_table.c int nf_flow_table_iterate(struct nf_flowtable *flow_table,
Quoted 8 lines
net/netfilter/nf_flow_table.c +8
-0+static int do_flow_offload (struct flow_offload *flow) +{ +struct net_device *indev; +int ret, ifindex; + +rtnl_lock (); + ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL ].tuple.iifidx; + indev =__dev_get_by_index (&init_net, ifindex);
likewise.
net/netfilter/nf_flow_table.c
Quoted 3 lines
net/netfilter/nf_flow_table.c +3
-0+#define FLOW_HW_WORK_TIMEOUT msecs_to_jiffies(100) + +static struct delayed_work nft_flow_offload_dwork;
I would go with struct work and no delay at all.
Pablo Neira Ayuso
maintainerRe: [PATCH nf-next RFC,v2 6/6] netfilter: nft_flow_offload: add ndo hooks for hardware offload
Quoted 1 line · Florian Westphal
Pablo Neira Ayuso <[email protected]> wrote:
[...]
Quoted 20 lines
Quoted 7 lines
net/netfilter/nf_flow_table.c +0
-0@@ -212,6 +212,21 @@ int nf_flow_table_iterate(struct nf_flowtable *flow_table, }net/netfilter/nf_flow_table.c int nf_flow_table_iterate(struct nf_flowtable *flow_table, · line 222
Quoted 10 lines
net/netfilter/nf_flow_table.c +8
-0EXPORT_SYMBOL_GPL (nf_flow_table_iterate); +static void flow_offload_hw_del (struct flow_offload *flow) +{ +struct net_device *indev; +int ret, ifindex; + +rtnl_lock (); + ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL ].tuple.iifidx; + indev =__dev_get_by_index (&init_net, ifindex);I think this should pass struct net * as arg to
flow_offload_hw_del.
Quoted 10 lines
net/netfilter/nf_flow_table.c int nf_flow_table_iterate(struct nf_flowtable *flow_table, · line 228
Quoted 6 lines
net/netfilter/nf_flow_table.c +6
-0+if (WARN_ON (!indev)) +return ; + + ret = indev->netdev_ops->ndo_flow_offload (FLOW_OFFLOAD_DEL , flow); +rtnl_unlock (); +}Please no rtnl lock unless absolutely needed. Seems this could even avoid the mutex completely by using
dev_get_by_index+dev_put.
OK, we still need to make sure that we additions and deletions from hardware don't occur concurrently, but that we can probably do it with another mutex.
Quoted 11 lines
net/netfilter/nf_flow_table.c int nf_flow_table_iterate(struct nf_flowtable *flow_table,
Quoted 8 lines
net/netfilter/nf_flow_table.c +8
-0+static int do_flow_offload (struct flow_offload *flow) +{ +struct net_device *indev; +int ret, ifindex; + +rtnl_lock (); + ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL ].tuple.iifidx; + indev =__dev_get_by_index (&init_net, ifindex);likewise.
Quoted 5 lines
net/netfilter/nf_flow_table.c
Quoted 3 lines
net/netfilter/nf_flow_table.c +3
-0+#define FLOW_HW_WORK_TIMEOUT msecs_to_jiffies(100) + +static struct delayed_work nft_flow_offload_dwork;I would go with struct work and no delay at all.
Will have a look into this, thanks!