Skip to contentSkip to conversation

netfilter: conntrack: route cache for forwarded connections

PATCH In review Florian Westphal 3 participants 7 messages Dec 8, 2014 – Apr 28, 2016

Subsystems (3)
lore
Share & tools
lore

Florian Westphal

author maintainer
#0 lore raw

cover netfilter: conntrack: route cache for forwarded connections

[ Pablo, in case you deem this too late for -next just let me know and I will resend once its open again ]

This adds an optional forward routing cache extension for netfilter connection tracking.

The memory cost is an additional 32 bytes per conntrack entry on x86_64.

Unlike any other currently implemented connection tracking extension the rtcache has no run-time tunables, it is always active.

Also, unlike other conntrack extensions, it can be built as a module, in this case modprobe/rmmod are used to enable/disable the cache.

Forward test using netperf UDP_STREAM between two network namespaces (connected via veth devices), tput:

With conntrack + reverse path filtering (rp_filter sysctl=1):
MIGRATED UDP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.1.12.2 () port 0 AF_INET
Socket Message Elapsed Messages

 Size    Size     Time         Okay Errors   Throughput
 bytes   bytes    secs            #      #   10^6bits/sec
  212992      64   120.00    26333996      0     112.36
  212992           120.00    26279399            112.13

same, but with rtcache (this patch series):

  212992      64   120.00    34508693      0     147.24
  212992           120.00    34507838            147.23

same but with rp_filter=0 and no conntrack modules active:

  212992      64   120.00    42288748      0     180.43
  212992           120.00    42283439            180.41

IOW, this is only useful if conntrack is used anyway.

author maintainer
#1 lore raw

PATCH 1/2 netfilter: conntrack: cache route for forwarded connections

... to avoid per-packet FIB lookup if possible.

The cached dst is re-used provided the input interface is the same as that of the previous packet in the same direction.

If not, the cached dst is invalidated.

For ipv6 we also need to store sernum, else dst_check doesn't work, pointed out by Eric Dumazet.

This should speed up forwarding when conntrack is already in use anyway, especially when using reverse path filtering -- active RPF enforces two FIB lookups for each packet.

Before the routing cache removal this didn't matter since RPF was performed only when route cache didn't yield a result; but without route cache it comes at higher price.

Julian Anastasov suggested to add NETDEV_UNREGISTER handler to avoid holding on to dsts of 'frozen' conntracks.

---
Changes since RFC:
- add NETDEV_UNREGISTER, suggested by Julian
- cache fib sernum to make ipv6 work, pointed out by Eric
- make module unload work
- remove ASSURED test, in case of -j DROP in prerouting or forward cache forward hook won't be reached anyway

 include/net/netfilter/nf_conntrack_extend.h  |   4 +
 include/net/netfilter/nf_conntrack_rtcache.h |  34 +++
 net/netfilter/Kconfig                        |  12 +
 net/netfilter/Makefile                       |   3 +
 net/netfilter/nf_conntrack_rtcache.c         | 387 +++++++++++++++++++++++++++
 5 files changed, 440 insertions(+)
 create mode 100644 include/net/netfilter/nf_conntrack_rtcache.h
 create mode 100644 net/netfilter/nf_conntrack_rtcache.c
include/net/netfilter/nf_conntrack_extend.h +4 -0
index 55d1504..1b00d57 100644
@@ -30,6 +30,9 @@ enum nf_ct_ext_id {
 #if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY)
 	NF_CT_EXT_SYNPROXY,
 #endif
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE)
+	NF_CT_EXT_RTCACHE,
+#endif
 	NF_CT_EXT_NUM,
 };
 
@@ -43,6 +46,7 @@ enum nf_ct_ext_id {
 #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout
 #define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels
 #define NF_CT_EXT_SYNPROXY_TYPE struct nf_conn_synproxy
+#define NF_CT_EXT_RTCACHE_TYPE struct nf_conn_rtcache
 
 /* Extensions: optional stuff which isn't permanently in struct. */
 struct nf_ct_ext {
include/net/netfilter/nf_conntrack_rtcache.h new file +34 -0
new file mode 100644
index 0000000..e2fb302
@@ -0,0 +1,34 @@
+#include <linux/gfp.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_extend.h>
+
+struct dst_entry;
+
+struct nf_conn_dst_cache {
+	struct dst_entry *dst;
+	int iif;
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
+	u32 cookie;
+#endif
+
+};
+
+struct nf_conn_rtcache {
+	struct nf_conn_dst_cache cached_dst[IP_CT_DIR_MAX];
+};
+
+static inline
+struct nf_conn_rtcache *nf_ct_rtcache_find(const struct nf_conn *ct)
+{
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE)
+	return nf_ct_ext_find(ct, NF_CT_EXT_RTCACHE);
+#else
+	return NULL;
+#endif
+}
+
+static inline int nf_conn_rtcache_iif_get(const struct nf_conn_rtcache *rtc,
+					  enum ip_conntrack_dir dir)
+{
+	return rtc->cached_dst[dir].iif;
+}
net/netfilter/Kconfig +12 -0
index b02660f..c213a61 100644
@@ -106,6 +106,18 @@ config NF_CONNTRACK_EVENTS
 
 	  If unsure, say `N'.
 
+config NF_CONNTRACK_RTCACHE
+	tristate "Cache route entries in conntrack objects"
+	depends on NETFILTER_ADVANCED
+	depends on NF_CONNTRACK
+	help
+	  If this option is enabled, the connection tracking code will
+	  cache routing information for each connection that is being
+	  forwarded, at a cost of 32 bytes per conntrack object.
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+	  The module will be called nf_conntrack_rtcache.
+
 config NF_CONNTRACK_TIMEOUT
 	bool  'Connection tracking timeout'
 	depends on NETFILTER_ADVANCED
net/netfilter/Makefile +3 -0
index 89f73a9..c174ab2 100644
@@ -18,6 +18,9 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
 # connection tracking
 obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
 
+# optional conntrack route cache extension
+obj-$(CONFIG_NF_CONNTRACK_RTCACHE) += nf_conntrack_rtcache.o
+
 # SCTP protocol connection tracking
 obj-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o
 obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o
net/netfilter/nf_conntrack_rtcache.c new file +387 -0

387 lines not on this page · raw message

Signature · 1 line
2.0.4
author maintainer
#2 lore raw

Re: [PATCH nf-next 1/2] netfilter: conntrack: cache route for forwarded connections

net/netfilter/nf_conntrack_rtcache.c line 175

Quoted 2 lines · Florian Westphal
net/netfilter/nf_conntrack_rtcache.c new file +2 -0
+	if (likely(dst))
+		skb_dst_set_noref_force(skb, dst);

Note that Hannes submitted a patch vs. net-next that removes skb_dst_set_noref_force().

So depending on the oder in which the patches are applied (iff they are...) the above should read

skb_dst_set_noref(skb, dst);

instead.

#3 lore raw

Re: [nf-next, 1/2] netfilter: conntrack: cache route for forwarded connections

In reply to Florian Westphal

2014-12-08 16:36 GMT+01:00 Florian Westphal <[email protected]>:

Quoted 6 lines

... to avoid per-packet FIB lookup if possible.

The cached dst is re-used provided the input interface is the same as that of the previous packet in the same direction.

If not, the cached dst is invalidated.

It looks like someone found out that this patch causes crashes. This was reported to OpenWrt in ticket https://dev.openwrt.org/ticket/22283

author maintainer
#4 lore raw

PATCH 2/2 netfilter: use conntrack rtcache if available

In reply to Florian Westphal

skip the reverse lookup if the iif matches the cached one. In this case we know that a previous rpfilter check did not result in packet drop.

This shortcut only works if rtcache is available and rule is placed in mangle table (raw table is too early; skb->nfct will not be set).

While it would be possible to enforce rtcache, it would a) force a dependency on conntrack and b) break backwards compatibility since we'd have to restrict it to mangle table.

---
 include/net/netfilter/nf_conntrack_rtcache.h | 12 +++++++++++
 net/ipv4/netfilter/ipt_rpfilter.c            |  4 +++-
 net/ipv6/netfilter/ip6t_rpfilter.c           |  4 +++-
 net/netfilter/core.c                         | 30 ++++++++++++++++++++++++++++
 4 files changed, 48 insertions(+), 2 deletions(-)
include/net/netfilter/nf_conntrack_rtcache.h +12 -0
index e2fb302..19265ad 100644
@@ -27,6 +27,18 @@ struct nf_conn_rtcache *nf_ct_rtcache_find(const struct nf_conn *ct)
 #endif
 }
 
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE)
+bool nf_conn_rtcache_match_dev(const struct sk_buff *skb,
+			       const struct net_device *dev);
+#else
+static inline bool
+nf_conn_rtcache_match_dev(const struct sk_buff *skb,
+			  const struct net_device *dev)
+{
+	return false;
+}
+#endif
+
 static inline int nf_conn_rtcache_iif_get(const struct nf_conn_rtcache *rtc,
 					  enum ip_conntrack_dir dir)
 {
net/ipv4/netfilter/ipt_rpfilter.c +3 -1
index 4bfaedf..f39e934 100644
@@ -19,6 +19,8 @@
 #include <linux/netfilter/xt_rpfilter.h>
 #include <linux/netfilter/x_tables.h>
 
+#include <net/netfilter/nf_conntrack_rtcache.h>
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Florian Westphal <[email protected]>");
 MODULE_DESCRIPTION("iptables: ipv4 reverse path filter match");
@@ -82,7 +84,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
 	info = par->matchinfo;
 	invert = info->flags & XT_RPFILTER_INVERT;
 
-	if (rpfilter_is_local(skb))
+	if (rpfilter_is_local(skb) || nf_conn_rtcache_match_dev(skb, par->in))
 		return true ^ invert;
 
 	iph = ip_hdr(skb);
net/ipv6/netfilter/ip6t_rpfilter.c +3 -1
index 790e0c6..8db9fe7 100644
@@ -16,6 +16,8 @@
 #include <linux/netfilter/xt_rpfilter.h>
 #include <linux/netfilter/x_tables.h>
 
+#include <net/netfilter/nf_conntrack_rtcache.h>
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Florian Westphal <[email protected]>");
 MODULE_DESCRIPTION("Xtables: IPv6 reverse path filter match");
@@ -85,7 +87,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
 	struct ipv6hdr *iph;
 	bool invert = info->flags & XT_RPFILTER_INVERT;
 
-	if (rpfilter_is_local(skb))
+	if (rpfilter_is_local(skb) || nf_conn_rtcache_match_dev(skb, par->in))
 		return true ^ invert;
 
 	iph = ipv6_hdr(skb);
net/netfilter/core.c +30 -0
index fea9ef5..651b4c6 100644
@@ -25,6 +25,8 @@
 #include <net/net_namespace.h>
 #include <net/sock.h>
 
+#include <net/netfilter/nf_conntrack_rtcache.h>
+
 #include "nf_internals.h"
 
 static DEFINE_MUTEX(afinfo_mutex);
@@ -267,6 +269,34 @@ EXPORT_SYMBOL_GPL(nfq_ct_hook);
 struct nfq_ct_nat_hook __rcu *nfq_ct_nat_hook __read_mostly;
 EXPORT_SYMBOL_GPL(nfq_ct_nat_hook);
 
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE)
+/* returns true if dev matches the last recorded
+ * input interface of the conntrack attached to skb.
+ *
+ * This is not in conntrack to avoid module dependency.
+ */
+bool nf_conn_rtcache_match_dev(const struct sk_buff *skb,
+			       const struct net_device *dev)
+{
+	struct nf_conn_rtcache *rtc;
+	enum ip_conntrack_info ctinfo;
+	enum ip_conntrack_dir dir;
+	struct nf_conn *ct;
+	int iif;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	rtc = nf_ct_rtcache_find(ct);
+	if (!rtc)
+		return false;
+
+	dir = CTINFO2DIR(ctinfo);
+	iif = nf_conn_rtcache_iif_get(rtc, dir);
+
+	return iif == dev->ifindex;
+}
+EXPORT_SYMBOL_GPL(nf_conn_rtcache_match_dev);
+#endif
+
 #endif /* CONFIG_NF_CONNTRACK */
 
 #ifdef CONFIG_NF_NAT_NEEDED
Signature · 1 line
2.0.4
#5 lore raw
In reply to Florian Westphal
Quoted 6 lines · Florian Westphal

[ Pablo, in case you deem this too late for -next just let me know and I will resend once its open again ]

This adds an optional forward routing cache extension for netfilter connection tracking.

Quoted 8 lines

The memory cost is an additional 32 bytes per conntrack entry on x86_64.

Unlike any other currently implemented connection tracking extension the rtcache has no run-time tunables, it is always active.

Also, unlike other conntrack extensions, it can be built as a module, in this case modprobe/rmmod are used to enable/disable the cache.

I expect distributors will provide this a module. I think we should provide features that can be enable/disable in some way, in this case it can be modprobe/rmmod.

BTW, did you evaluate Eric's alternative? Any comment on that?

Quoted 5 lines · Florian Westphal

Review

Quoted 2 lines
(quoted diff) +2 -0
+     if (likely(dst))
+             skb_dst_set_noref_force(skb, dst);

Note that Hannes submitted a patch vs. net-next that removes skb_dst_set_noref_force().

I refreshed the nf-next tree, this patch is now there.

If the merge window remains open, I'll take the pending patches in patchwork and send a new batch for David by tomorrow morning.

Let me know, thanks.

author maintainer
#6 lore raw
Quoted 13 lines · Pablo Neira Ayuso
Quoted 12 lines · Florian Westphal

[ Pablo, in case you deem this too late for -next just let me know and I will resend once its open again ]

This adds an optional forward routing cache extension for netfilter connection tracking.

The memory cost is an additional 32 bytes per conntrack entry on x86_64.

Unlike any other currently implemented connection tracking extension the rtcache has no run-time tunables, it is always active.

Quoted 6 lines
Quoted 2 lines

Also, unlike other conntrack extensions, it can be built as a module, in this case modprobe/rmmod are used to enable/disable the cache.

I expect distributors will provide this a module. I think we should provide features that can be enable/disable in some way, in this case it can be modprobe/rmmod.

Yes, I just wanted to mention this in case someone thinks a sysctl is must-have.

I did not want to add sysctl "just because" as we cannot easily rip them out later.

Quoted 1 line

BTW, did you evaluate Eric's alternative? Any comment on that?

Right, sorry. So Eric suggested to leverage existing early demux.

This would work, but I found several disadvantages, namely it would:

1. only work for protocols that have early demux support
2. have to add some sort of new mini-sk to the "right" data structure (udp_table, tcp_hashinfo) so not just af but l4 specific handling
3. add more code to L4 protocol handlers
4. add some upper ceiling for such "forward sockets".
5. devise a scheme by which to zap the entry again

4+5 can be avoided by embedding the "forward sock" inside conntrack, but that would mean we get larger code than this patch while still retaining the conntrack dependency.

And without conntrack, I'm not sure how one would go about adding such route cache without adding back all the problems it had.

Quoted 4 lines

If the merge window remains open, I'll take the pending patches in patchwork and send a new batch for David by tomorrow morning.

Let me know, thanks.

Ok. I can send a rebased V3. OTOH, I don't want to rush things.

If you think further discussion is needed before deciding to go with a conntrack-based route cache then lets do that.

In this case I can resend the v3 once next is open again plus a summary of the alternatives/problems and we can take it from there.