summaryrefslogtreecommitdiff
path: root/datapath/linux/compat/nf_conntrack_proto.c
blob: fe291dbf25e1e2fc7617dd80b3a4e4e695df9473 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <linux/types.h>

#include <net/netfilter/nf_conntrack.h>
#ifdef HAVE_NF_CONNTRACK_L3PROATO_H
#include <net/netfilter/nf_conntrack_l3proto.h>
#endif

/*
 * 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_GET is false.
 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
#ifdef HAVE_NET_NS_GET
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_GET */
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_GET */
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0) */