aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2011-11-21 18:16:51 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2011-11-21 18:34:47 -0500
commit70e9942f17a6193e9172a804e6569a8806633d6b (patch)
tree5cb0fe0a38a99aa51b70c01f7a7e729480ec5c73 /net
parent5e2afba4ecd7931ea06e6fa116ab28e6943dbd42 (diff)
netfilter: nf_conntrack: make event callback registration per-netns
This patch fixes an oops that can be triggered following this recipe: 0) make sure nf_conntrack_netlink and nf_conntrack_ipv4 are loaded. 1) container is started. 2) connect to it via lxc-console. 3) generate some traffic with the container to create some conntrack entries in its table. 4) stop the container: you hit one oops because the conntrack table cleanup tries to report the destroy event to user-space but the per-netns nfnetlink socket has already gone (as the nfnetlink socket is per-netns but event callback registration is global). To fix this situation, we make the ctnl_notifier per-netns so the callback is registered/unregistered if the container is created/destroyed. Alex Bligh and Alexey Dobriyan originally proposed one small patch to check if the nfnetlink socket is gone in nfnetlink_has_listeners, but this is a very visited path for events, thus, it may reduce performance and it looks a bit hackish to check for the nfnetlink socket only to workaround this situation. As a result, I decided to follow the bigger path choice, which seems to look nicer to me. Cc: Alexey Dobriyan <adobriyan@gmail.com> Reported-by: Alex Bligh <alex@alex.org.uk> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/nf_conntrack_ecache.c37
-rw-r--r--net/netfilter/nf_conntrack_netlink.c73
2 files changed, 70 insertions, 40 deletions
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index 6b368be937c6..b62c4148b921 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -27,22 +27,17 @@
27 27
28static DEFINE_MUTEX(nf_ct_ecache_mutex); 28static DEFINE_MUTEX(nf_ct_ecache_mutex);
29 29
30struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb __read_mostly;
31EXPORT_SYMBOL_GPL(nf_conntrack_event_cb);
32
33struct nf_exp_event_notifier __rcu *nf_expect_event_cb __read_mostly;
34EXPORT_SYMBOL_GPL(nf_expect_event_cb);
35
36/* deliver cached events and clear cache entry - must be called with locally 30/* deliver cached events and clear cache entry - must be called with locally
37 * disabled softirqs */ 31 * disabled softirqs */
38void nf_ct_deliver_cached_events(struct nf_conn *ct) 32void nf_ct_deliver_cached_events(struct nf_conn *ct)
39{ 33{
34 struct net *net = nf_ct_net(ct);
40 unsigned long events; 35 unsigned long events;
41 struct nf_ct_event_notifier *notify; 36 struct nf_ct_event_notifier *notify;
42 struct nf_conntrack_ecache *e; 37 struct nf_conntrack_ecache *e;
43 38
44 rcu_read_lock(); 39 rcu_read_lock();
45 notify = rcu_dereference(nf_conntrack_event_cb); 40 notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
46 if (notify == NULL) 41 if (notify == NULL)
47 goto out_unlock; 42 goto out_unlock;
48 43
@@ -83,19 +78,20 @@ out_unlock:
83} 78}
84EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events); 79EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events);
85 80
86int nf_conntrack_register_notifier(struct nf_ct_event_notifier *new) 81int nf_conntrack_register_notifier(struct net *net,
82 struct nf_ct_event_notifier *new)
87{ 83{
88 int ret = 0; 84 int ret = 0;
89 struct nf_ct_event_notifier *notify; 85 struct nf_ct_event_notifier *notify;
90 86
91 mutex_lock(&nf_ct_ecache_mutex); 87 mutex_lock(&nf_ct_ecache_mutex);
92 notify = rcu_dereference_protected(nf_conntrack_event_cb, 88 notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb,
93 lockdep_is_held(&nf_ct_ecache_mutex)); 89 lockdep_is_held(&nf_ct_ecache_mutex));
94 if (notify != NULL) { 90 if (notify != NULL) {
95 ret = -EBUSY; 91 ret = -EBUSY;
96 goto out_unlock; 92 goto out_unlock;
97 } 93 }
98 RCU_INIT_POINTER(nf_conntrack_event_cb, new); 94 RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, new);
99 mutex_unlock(&nf_ct_ecache_mutex); 95 mutex_unlock(&nf_ct_ecache_mutex);
100 return ret; 96 return ret;
101 97
@@ -105,32 +101,34 @@ out_unlock:
105} 101}
106EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier); 102EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
107 103
108void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *new) 104void nf_conntrack_unregister_notifier(struct net *net,
105 struct nf_ct_event_notifier *new)
109{ 106{
110 struct nf_ct_event_notifier *notify; 107 struct nf_ct_event_notifier *notify;
111 108
112 mutex_lock(&nf_ct_ecache_mutex); 109 mutex_lock(&nf_ct_ecache_mutex);
113 notify = rcu_dereference_protected(nf_conntrack_event_cb, 110 notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb,
114 lockdep_is_held(&nf_ct_ecache_mutex)); 111 lockdep_is_held(&nf_ct_ecache_mutex));
115 BUG_ON(notify != new); 112 BUG_ON(notify != new);
116 RCU_INIT_POINTER(nf_conntrack_event_cb, NULL); 113 RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL);
117 mutex_unlock(&nf_ct_ecache_mutex); 114 mutex_unlock(&nf_ct_ecache_mutex);
118} 115}
119EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); 116EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
120 117
121int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *new) 118int nf_ct_expect_register_notifier(struct net *net,
119 struct nf_exp_event_notifier *new)
122{ 120{
123 int ret = 0; 121 int ret = 0;
124 struct nf_exp_event_notifier *notify; 122 struct nf_exp_event_notifier *notify;
125 123
126 mutex_lock(&nf_ct_ecache_mutex); 124 mutex_lock(&nf_ct_ecache_mutex);
127 notify = rcu_dereference_protected(nf_expect_event_cb, 125 notify = rcu_dereference_protected(net->ct.nf_expect_event_cb,
128 lockdep_is_held(&nf_ct_ecache_mutex)); 126 lockdep_is_held(&nf_ct_ecache_mutex));
129 if (notify != NULL) { 127 if (notify != NULL) {
130 ret = -EBUSY; 128 ret = -EBUSY;
131 goto out_unlock; 129 goto out_unlock;
132 } 130 }
133 RCU_INIT_POINTER(nf_expect_event_cb, new); 131 RCU_INIT_POINTER(net->ct.nf_expect_event_cb, new);
134 mutex_unlock(&nf_ct_ecache_mutex); 132 mutex_unlock(&nf_ct_ecache_mutex);
135 return ret; 133 return ret;
136 134
@@ -140,15 +138,16 @@ out_unlock:
140} 138}
141EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier); 139EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier);
142 140
143void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *new) 141void nf_ct_expect_unregister_notifier(struct net *net,
142 struct nf_exp_event_notifier *new)
144{ 143{
145 struct nf_exp_event_notifier *notify; 144 struct nf_exp_event_notifier *notify;
146 145
147 mutex_lock(&nf_ct_ecache_mutex); 146 mutex_lock(&nf_ct_ecache_mutex);
148 notify = rcu_dereference_protected(nf_expect_event_cb, 147 notify = rcu_dereference_protected(net->ct.nf_expect_event_cb,
149 lockdep_is_held(&nf_ct_ecache_mutex)); 148 lockdep_is_held(&nf_ct_ecache_mutex));
150 BUG_ON(notify != new); 149 BUG_ON(notify != new);
151 RCU_INIT_POINTER(nf_expect_event_cb, NULL); 150 RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL);
152 mutex_unlock(&nf_ct_ecache_mutex); 151 mutex_unlock(&nf_ct_ecache_mutex);
153} 152}
154EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier); 153EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index e58aa9b1fe8a..ef21b221f036 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -4,7 +4,7 @@
4 * (C) 2001 by Jay Schulist <jschlst@samba.org> 4 * (C) 2001 by Jay Schulist <jschlst@samba.org>
5 * (C) 2002-2006 by Harald Welte <laforge@gnumonks.org> 5 * (C) 2002-2006 by Harald Welte <laforge@gnumonks.org>
6 * (C) 2003 by Patrick Mchardy <kaber@trash.net> 6 * (C) 2003 by Patrick Mchardy <kaber@trash.net>
7 * (C) 2005-2008 by Pablo Neira Ayuso <pablo@netfilter.org> 7 * (C) 2005-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
8 * 8 *
9 * Initial connection tracking via netlink development funded and 9 * Initial connection tracking via netlink development funded and
10 * generally made possible by Network Robots, Inc. (www.networkrobots.com) 10 * generally made possible by Network Robots, Inc. (www.networkrobots.com)
@@ -2163,6 +2163,54 @@ MODULE_ALIAS("ip_conntrack_netlink");
2163MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK); 2163MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK);
2164MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP); 2164MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP);
2165 2165
2166static int __net_init ctnetlink_net_init(struct net *net)
2167{
2168#ifdef CONFIG_NF_CONNTRACK_EVENTS
2169 int ret;
2170
2171 ret = nf_conntrack_register_notifier(net, &ctnl_notifier);
2172 if (ret < 0) {
2173 pr_err("ctnetlink_init: cannot register notifier.\n");
2174 goto err_out;
2175 }
2176
2177 ret = nf_ct_expect_register_notifier(net, &ctnl_notifier_exp);
2178 if (ret < 0) {
2179 pr_err("ctnetlink_init: cannot expect register notifier.\n");
2180 goto err_unreg_notifier;
2181 }
2182#endif
2183 return 0;
2184
2185#ifdef CONFIG_NF_CONNTRACK_EVENTS
2186err_unreg_notifier:
2187 nf_conntrack_unregister_notifier(net, &ctnl_notifier);
2188err_out:
2189 return ret;
2190#endif
2191}
2192
2193static void ctnetlink_net_exit(struct net *net)
2194{
2195#ifdef CONFIG_NF_CONNTRACK_EVENTS
2196 nf_ct_expect_unregister_notifier(net, &ctnl_notifier_exp);
2197 nf_conntrack_unregister_notifier(net, &ctnl_notifier);
2198#endif
2199}
2200
2201static void __net_exit ctnetlink_net_exit_batch(struct list_head *net_exit_list)
2202{
2203 struct net *net;
2204
2205 list_for_each_entry(net, net_exit_list, exit_list)
2206 ctnetlink_net_exit(net);
2207}
2208
2209static struct pernet_operations ctnetlink_net_ops = {
2210 .init = ctnetlink_net_init,
2211 .exit_batch = ctnetlink_net_exit_batch,
2212};
2213
2166static int __init ctnetlink_init(void) 2214static int __init ctnetlink_init(void)
2167{ 2215{
2168 int ret; 2216 int ret;
@@ -2180,28 +2228,15 @@ static int __init ctnetlink_init(void)
2180 goto err_unreg_subsys; 2228 goto err_unreg_subsys;
2181 } 2229 }
2182 2230
2183#ifdef CONFIG_NF_CONNTRACK_EVENTS 2231 if (register_pernet_subsys(&ctnetlink_net_ops)) {
2184 ret = nf_conntrack_register_notifier(&ctnl_notifier); 2232 pr_err("ctnetlink_init: cannot register pernet operations\n");
2185 if (ret < 0) {
2186 pr_err("ctnetlink_init: cannot register notifier.\n");
2187 goto err_unreg_exp_subsys; 2233 goto err_unreg_exp_subsys;
2188 } 2234 }
2189 2235
2190 ret = nf_ct_expect_register_notifier(&ctnl_notifier_exp);
2191 if (ret < 0) {
2192 pr_err("ctnetlink_init: cannot expect register notifier.\n");
2193 goto err_unreg_notifier;
2194 }
2195#endif
2196
2197 return 0; 2236 return 0;
2198 2237
2199#ifdef CONFIG_NF_CONNTRACK_EVENTS
2200err_unreg_notifier:
2201 nf_conntrack_unregister_notifier(&ctnl_notifier);
2202err_unreg_exp_subsys: 2238err_unreg_exp_subsys:
2203 nfnetlink_subsys_unregister(&ctnl_exp_subsys); 2239 nfnetlink_subsys_unregister(&ctnl_exp_subsys);
2204#endif
2205err_unreg_subsys: 2240err_unreg_subsys:
2206 nfnetlink_subsys_unregister(&ctnl_subsys); 2241 nfnetlink_subsys_unregister(&ctnl_subsys);
2207err_out: 2242err_out:
@@ -2213,11 +2248,7 @@ static void __exit ctnetlink_exit(void)
2213 pr_info("ctnetlink: unregistering from nfnetlink.\n"); 2248 pr_info("ctnetlink: unregistering from nfnetlink.\n");
2214 2249
2215 nf_ct_remove_userspace_expectations(); 2250 nf_ct_remove_userspace_expectations();
2216#ifdef CONFIG_NF_CONNTRACK_EVENTS 2251 unregister_pernet_subsys(&ctnetlink_net_ops);
2217 nf_ct_expect_unregister_notifier(&ctnl_notifier_exp);
2218 nf_conntrack_unregister_notifier(&ctnl_notifier);
2219#endif
2220
2221 nfnetlink_subsys_unregister(&ctnl_exp_subsys); 2252 nfnetlink_subsys_unregister(&ctnl_exp_subsys);
2222 nfnetlink_subsys_unregister(&ctnl_subsys); 2253 nfnetlink_subsys_unregister(&ctnl_subsys);
2223} 2254}