From 179fccce34db7af112be3c6e8ad114802cb235bd Mon Sep 17 00:00:00 2001 From: Yi-Hung Wei Date: Fri, 17 Aug 2018 02:05:02 -0700 Subject: [PATCH] compat: Backport nf_ct_netns_{get, put}() 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 Signed-off-by: Justin Pettit --- acinclude.m4 | 4 + datapath/linux/Modules.mk | 4 +- .../include/net/netfilter/nf_conntrack.h | 8 ++ .../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 diff --git a/acinclude.m4 b/acinclude.m4 index 9fffe9c17..f0248612d 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -589,6 +589,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])]) @@ -611,6 +613,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 104c32fa1..04ea5b756 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 bb40b0f6d..50db914a3 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 000000000..56895b17b --- /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 + +/* + * 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 000000000..e877d7638 --- /dev/null +++ b/datapath/linux/compat/nf_conntrack_proto.c @@ -0,0 +1,112 @@ +#include + +#include +#include + +/* + * 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) */ -- 2.39.2