[ovs-dev,v2,01/11] compat: Backport nf_ct_netns_{get, put}()

Message ID 1533170156-769-2-git-send-email-yihung.wei@gmail.com
State Changes Requested
Headers show
Series
  • conntrack zone limitation
Related show

Commit Message

Yi-Hung Wei Aug. 2, 2018, 12:35 a.m.
This patch backports nf_ct_netns_get/put() in order to support a feature
in the follow up patch.

nf_ct_netns_{get,put} were first introduced in upstream net-next commit
ecb2421b5ddf ("netfilter: add and use nf_ct_netns_get/put") in kernel
v4.10, and then updated in commmit 7e35ec0e8044 ("netfilter: conntrack:
move nf_ct_netns_{get,put}() to core") in kernel v4.15.  We need to
invoke nf_ct_netns_get/put() when the underlying nf_conntrack_l3proto
supports net_ns_{get,put}().

Therefore, there are 3 cases that we need to consider.
1) Before nf_ct_{get,put}() is introduced.
    We just mock nf_ct_nets_{get,put}() and do nothing.

2) After 1) and before v4.15
    Backports based on commit 7e35ec0e8044 .

3) Staring from v4.15
    Use the upstream version.

Signed-off-by: Yi-Hung Wei <yihung.wei@gmail.com>
---
 acinclude.m4                                       |   4 +
 datapath/linux/Modules.mk                          |   4 +-
 .../compat/include/net/netfilter/nf_conntrack.h    |   8 ++
 .../linux/compat/include/uapi/linux/netfilter.h    |  14 +++
 datapath/linux/compat/nf_conntrack_proto.c         | 112 +++++++++++++++++++++
 5 files changed, 141 insertions(+), 1 deletion(-)
 create mode 100644 datapath/linux/compat/include/uapi/linux/netfilter.h
 create mode 100644 datapath/linux/compat/nf_conntrack_proto.c

Patch

diff --git a/acinclude.m4 b/acinclude.m4
index ad6b5b5e067e..731bc07be8fa 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -588,6 +588,8 @@  AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
                   [OVS_DEFINE([HAVE_NF_HOOKFN_ARG_PRIV])])
   OVS_FIND_FIELD_IFELSE([$KSRC/include/linux/netfilter.h], [nf_hook_ops],
                         [owner], [OVS_DEFINE([HAVE_NF_HOOKS_OPS_OWNER])])
+  OVS_GREP_IFELSE([$KSRC/include/linux/netfilter.h], [NFPROTO_INET])
+
 
   OVS_FIND_FIELD_IFELSE([$KSRC/include/linux/netfilter_ipv6.h], [nf_ipv6_ops],
                         [fragment.*sock], [OVS_DEFINE([HAVE_NF_IPV6_OPS_FRAGMENT])])
@@ -610,6 +612,8 @@  AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
                   [nf_ct_is_untracked])
   OVS_GREP_IFELSE([$KSRC/include/net/netfilter/nf_conntrack_zones.h],
                   [nf_ct_zone_init])
+  OVS_FIND_FIELD_IFELSE([$KSRC/include/net/netfilter/nf_conntrack_l3proto.h],
+                        [net_ns_get])
   OVS_GREP_IFELSE([$KSRC/include/net/netfilter/nf_conntrack_labels.h],
                   [nf_connlabels_get])
   OVS_FIND_PARAM_IFELSE([$KSRC/include/net/netfilter/nf_conntrack_labels.h],
diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk
index 104c32fa16ea..04ea5b756b6c 100644
--- a/datapath/linux/Modules.mk
+++ b/datapath/linux/Modules.mk
@@ -18,6 +18,7 @@  openvswitch_sources += \
 	linux/compat/lisp.c \
 	linux/compat/netdevice.c \
 	linux/compat/nf_conntrack_core.c \
+	linux/compat/nf_conntrack_proto.c \
 	linux/compat/nf_conntrack_reasm.c \
 	linux/compat/reciprocal_div.c \
 	linux/compat/skbuff-openvswitch.c \
@@ -107,5 +108,6 @@  openvswitch_headers += \
 	linux/compat/include/net/netfilter/nf_nat.h \
 	linux/compat/include/net/netfilter/ipv6/nf_defrag_ipv6.h \
 	linux/compat/include/net/sctp/checksum.h \
-	linux/compat/include/net/erspan.h
+	linux/compat/include/net/erspan.h \
+	linux/compat/include/uapi/linux/netfilter.h
 EXTRA_DIST += linux/compat/build-aux/export-check-whitelist
diff --git a/datapath/linux/compat/include/net/netfilter/nf_conntrack.h b/datapath/linux/compat/include/net/netfilter/nf_conntrack.h
index bb40b0f6da2a..50db914a39a1 100644
--- a/datapath/linux/compat/include/net/netfilter/nf_conntrack.h
+++ b/datapath/linux/compat/include/net/netfilter/nf_conntrack.h
@@ -22,4 +22,12 @@  nf_ct_set(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info info)
        skb->nfctinfo = info;
 }
 #endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
+int rpl_nf_ct_netns_get(struct net *net, u8 nfproto);
+void rpl_nf_ct_netns_put(struct net *net, u8 nfproto);
+#define nf_ct_netns_get rpl_nf_ct_netns_get
+#define nf_ct_netns_put rpl_nf_ct_netns_put
+#endif
+
 #endif /* _NF_CONNTRACK_WRAPPER_H */
diff --git a/datapath/linux/compat/include/uapi/linux/netfilter.h b/datapath/linux/compat/include/uapi/linux/netfilter.h
new file mode 100644
index 000000000000..56895b17b334
--- /dev/null
+++ b/datapath/linux/compat/include/uapi/linux/netfilter.h
@@ -0,0 +1,14 @@ 
+#ifndef _NETFILTER_WRAPPER_H
+#define _NETFILTER_WRAPPER_H
+
+#include_next <uapi/linux/netfilter.h>
+
+/*
+ * NFPROTO_INET was introduced in net-next commit 1d49144c0aaa
+ * ("netfilter: nf_tables: add "inet" table for IPv4/IPv6") in v3.14.
+ * Define this symbol to support back to v3.10 kernel. */
+#ifndef HAVE_NFPROTO_INET
+#define NFPROTO_INET 1
+#endif
+
+#endif /* _NETFILTER_WRAPPER_H */
diff --git a/datapath/linux/compat/nf_conntrack_proto.c b/datapath/linux/compat/nf_conntrack_proto.c
new file mode 100644
index 000000000000..e877d763892d
--- /dev/null
+++ b/datapath/linux/compat/nf_conntrack_proto.c
@@ -0,0 +1,112 @@ 
+#include <linux/types.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_l3proto.h>
+
+/*
+ * Upstream net-next commmit 7e35ec0e8044
+ * ("netfilter: conntrack: move nf_ct_netns_{get,put}() to core")
+ * is introduced in v4.15, and it supports NFPROTO_INET in
+ * nf_ct_netns_{get,put}() that OVS conntrack uses this feature.
+ *
+ * However, we only need this feature if the underlying nf_conntrack_l3proto
+ * supports net_ns_get/put.  Thus, we just mock the functions if
+ * HAVE_NET_NS_SET is false.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
+#ifdef HAVE_NET_NS_SET
+static int nf_ct_netns_do_get(struct net *net, u8 nfproto)
+{
+	const struct nf_conntrack_l3proto *l3proto;
+	int ret;
+
+	might_sleep();
+
+	ret = nf_ct_l3proto_try_module_get(nfproto);
+	if (ret < 0)
+		return ret;
+
+	/* we already have a reference, can't fail */
+	rcu_read_lock();
+	l3proto = __nf_ct_l3proto_find(nfproto);
+	rcu_read_unlock();
+
+	if (!l3proto->net_ns_get)
+		return 0;
+
+	ret = l3proto->net_ns_get(net);
+	if (ret < 0)
+		nf_ct_l3proto_module_put(nfproto);
+
+	return ret;
+}
+
+int rpl_nf_ct_netns_get(struct net *net, u8 nfproto)
+{
+	int err;
+
+	if (nfproto == NFPROTO_INET) {
+		err = nf_ct_netns_do_get(net, NFPROTO_IPV4);
+		if (err < 0)
+			goto err1;
+		err = nf_ct_netns_do_get(net, NFPROTO_IPV6);
+		if (err < 0)
+			goto err2;
+	} else {
+		err = nf_ct_netns_do_get(net, nfproto);
+		if (err < 0)
+			goto err1;
+	}
+	return 0;
+
+err2:
+	nf_ct_netns_put(net, NFPROTO_IPV4);
+err1:
+	return err;
+}
+EXPORT_SYMBOL_GPL(rpl_nf_ct_netns_get);
+
+static void nf_ct_netns_do_put(struct net *net, u8 nfproto)
+{
+	const struct nf_conntrack_l3proto *l3proto;
+
+	might_sleep();
+
+	/* same as nf_conntrack_netns_get(), reference assumed */
+	rcu_read_lock();
+	l3proto = __nf_ct_l3proto_find(nfproto);
+	rcu_read_unlock();
+
+	if (WARN_ON(!l3proto))
+		return;
+
+	if (l3proto->net_ns_put)
+		l3proto->net_ns_put(net);
+
+	nf_ct_l3proto_module_put(nfproto);
+}
+
+void rpl_nf_ct_netns_put(struct net *net, uint8_t nfproto)
+{
+	if (nfproto == NFPROTO_INET) {
+		nf_ct_netns_do_put(net, NFPROTO_IPV4);
+		nf_ct_netns_do_put(net, NFPROTO_IPV6);
+	} else
+		nf_ct_netns_do_put(net, nfproto);
+}
+EXPORT_SYMBOL_GPL(rpl_nf_ct_netns_put);
+
+#else /* !HAVE_NET_NS_SET */
+void rpl_nf_ct_netns_put(struct net *net, uint8_t nfproto)
+{
+}
+EXPORT_SYMBOL_GPL(rpl_nf_ct_netns_put);
+
+int rpl_nf_ct_netns_get(struct net *net, u8 nfproto)
+{
+    return 0;
+}
+EXPORT_SYMBOL_GPL(rpl_nf_ct_netns_get);
+
+#endif /* HAVE_NET_NS_SET */
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0) */