aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2012-11-27 15:30:52 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2012-12-03 09:06:33 -0500
commit04dac0111da7e1d284952cd415162451ffaa094d (patch)
tree21c52dcd61146f0560728cfe3662b1a9283e794e
parent9076aea76538556224e7d73ab718f8841330818a (diff)
netfilter: nf_conntrack: improve nf_conn object traceability
This patch modifies the conntrack subsystem so that all existing allocated conntrack objects can be found in any of the following places: * the hash table, this is the typical place for alive conntrack objects. * the unconfirmed list, this is the place for newly created conntrack objects that are still traversing the stack. * the dying list, this is where you can find conntrack objects that are dying or that should die anytime soon (eg. once the destroy event is delivered to the conntrackd daemon). Thus, we make sure that we follow the track for all existing conntrack objects. This patch, together with some extension of the ctnetlink interface to dump the content of the dying and unconfirmed lists, will help in case to debug suspected nf_conn object leaks. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/net/netfilter/nf_conntrack.h2
-rw-r--r--net/netfilter/nf_conntrack_core.c25
-rw-r--r--net/netfilter/nf_conntrack_netlink.c2
3 files changed, 11 insertions, 18 deletions
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index f1494feba79f..caca0c4d6b4b 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -182,7 +182,7 @@ __nf_conntrack_find(struct net *net, u16 zone,
182 182
183extern int nf_conntrack_hash_check_insert(struct nf_conn *ct); 183extern int nf_conntrack_hash_check_insert(struct nf_conn *ct);
184extern void nf_ct_delete_from_lists(struct nf_conn *ct); 184extern void nf_ct_delete_from_lists(struct nf_conn *ct);
185extern void nf_ct_insert_dying_list(struct nf_conn *ct); 185extern void nf_ct_dying_timeout(struct nf_conn *ct);
186 186
187extern void nf_conntrack_flush_report(struct net *net, u32 pid, int report); 187extern void nf_conntrack_flush_report(struct net *net, u32 pid, int report);
188 188
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 0f241be28f9e..af175166fffa 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -221,11 +221,9 @@ destroy_conntrack(struct nf_conntrack *nfct)
221 * too. */ 221 * too. */
222 nf_ct_remove_expectations(ct); 222 nf_ct_remove_expectations(ct);
223 223
224 /* We overload first tuple to link into unconfirmed list. */ 224 /* We overload first tuple to link into unconfirmed or dying list.*/
225 if (!nf_ct_is_confirmed(ct)) { 225 BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode));
226 BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode)); 226 hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
227 hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
228 }
229 227
230 NF_CT_STAT_INC(net, delete); 228 NF_CT_STAT_INC(net, delete);
231 spin_unlock_bh(&nf_conntrack_lock); 229 spin_unlock_bh(&nf_conntrack_lock);
@@ -247,6 +245,9 @@ void nf_ct_delete_from_lists(struct nf_conn *ct)
247 * Otherwise we can get spurious warnings. */ 245 * Otherwise we can get spurious warnings. */
248 NF_CT_STAT_INC(net, delete_list); 246 NF_CT_STAT_INC(net, delete_list);
249 clean_from_lists(ct); 247 clean_from_lists(ct);
248 /* add this conntrack to the dying list */
249 hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
250 &net->ct.dying);
250 spin_unlock_bh(&nf_conntrack_lock); 251 spin_unlock_bh(&nf_conntrack_lock);
251} 252}
252EXPORT_SYMBOL_GPL(nf_ct_delete_from_lists); 253EXPORT_SYMBOL_GPL(nf_ct_delete_from_lists);
@@ -268,31 +269,23 @@ static void death_by_event(unsigned long ul_conntrack)
268 } 269 }
269 /* we've got the event delivered, now it's dying */ 270 /* we've got the event delivered, now it's dying */
270 set_bit(IPS_DYING_BIT, &ct->status); 271 set_bit(IPS_DYING_BIT, &ct->status);
271 spin_lock(&nf_conntrack_lock);
272 hlist_nulls_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
273 spin_unlock(&nf_conntrack_lock);
274 nf_ct_put(ct); 272 nf_ct_put(ct);
275} 273}
276 274
277void nf_ct_insert_dying_list(struct nf_conn *ct) 275void nf_ct_dying_timeout(struct nf_conn *ct)
278{ 276{
279 struct net *net = nf_ct_net(ct); 277 struct net *net = nf_ct_net(ct);
280 struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct); 278 struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct);
281 279
282 BUG_ON(ecache == NULL); 280 BUG_ON(ecache == NULL);
283 281
284 /* add this conntrack to the dying list */
285 spin_lock_bh(&nf_conntrack_lock);
286 hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
287 &net->ct.dying);
288 spin_unlock_bh(&nf_conntrack_lock);
289 /* set a new timer to retry event delivery */ 282 /* set a new timer to retry event delivery */
290 setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct); 283 setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct);
291 ecache->timeout.expires = jiffies + 284 ecache->timeout.expires = jiffies +
292 (random32() % net->ct.sysctl_events_retry_timeout); 285 (random32() % net->ct.sysctl_events_retry_timeout);
293 add_timer(&ecache->timeout); 286 add_timer(&ecache->timeout);
294} 287}
295EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list); 288EXPORT_SYMBOL_GPL(nf_ct_dying_timeout);
296 289
297static void death_by_timeout(unsigned long ul_conntrack) 290static void death_by_timeout(unsigned long ul_conntrack)
298{ 291{
@@ -307,7 +300,7 @@ static void death_by_timeout(unsigned long ul_conntrack)
307 unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) { 300 unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) {
308 /* destroy event was not delivered */ 301 /* destroy event was not delivered */
309 nf_ct_delete_from_lists(ct); 302 nf_ct_delete_from_lists(ct);
310 nf_ct_insert_dying_list(ct); 303 nf_ct_dying_timeout(ct);
311 return; 304 return;
312 } 305 }
313 set_bit(IPS_DYING_BIT, &ct->status); 306 set_bit(IPS_DYING_BIT, &ct->status);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 7bbfb3deea30..34370a928360 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -989,7 +989,7 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
989 nlmsg_report(nlh)) < 0) { 989 nlmsg_report(nlh)) < 0) {
990 nf_ct_delete_from_lists(ct); 990 nf_ct_delete_from_lists(ct);
991 /* we failed to report the event, try later */ 991 /* we failed to report the event, try later */
992 nf_ct_insert_dying_list(ct); 992 nf_ct_dying_timeout(ct);
993 nf_ct_put(ct); 993 nf_ct_put(ct);
994 return 0; 994 return 0;
995 } 995 }