aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorFlorian Westphal2013-04-10 23:22:39 -0500
committerGreg Kroah-Hartman2013-05-11 15:53:54 -0500
commite0b95c5fdefb0e9eca0c1754ef4e2fa84323ff96 (patch)
tree5710ab14d949652c1c3b0d0de2632b7cec2be015 /net
parent023477dfd365edcbca4e157b72f7c4bd099fed81 (diff)
downloadkernel-video-e0b95c5fdefb0e9eca0c1754ef4e2fa84323ff96.tar.gz
kernel-video-e0b95c5fdefb0e9eca0c1754ef4e2fa84323ff96.tar.xz
kernel-video-e0b95c5fdefb0e9eca0c1754ef4e2fa84323ff96.zip
netfilter: nf_nat: fix race when unloading protocol modules
commit c2d421e171868586939c328dfb91bab840fe4c49 upstream. following oops was reported: RIP: 0010:[<ffffffffa03227f2>] [<ffffffffa03227f2>] nf_nat_cleanup_conntrack+0x42/0x70 [nf_nat] RSP: 0018:ffff880202c63d40 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff8801ac7bec28 RCX: ffff8801d0eedbe0 RDX: dead000000200200 RSI: 0000000000000011 RDI: ffffffffa03265b8 [..] Call Trace: [..] [<ffffffffa02febed>] destroy_conntrack+0xbd/0x110 [nf_conntrack] Happens when a conntrack timeout expires right after first part of the nat cleanup has completed (bysrc hash removal), but before part 2 has completed (re-initialization of nat area). [ destroy callback tries to delete bysrc again ] Patrick suggested to just remove the affected conntracks -- the connections won't work properly anyway without nat transformation. So, lets do that. Reported-by: CAI Qian <caiqian@redhat.com> Cc: Patrick McHardy <kaber@trash.net> Signed-off-by: Florian Westphal <fw@strlen.de> Acked-by: Patrick McHardy <kaber@trash.net> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/nf_nat_core.c40
1 files changed, 7 insertions, 33 deletions
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index 5f2f9109f46..4bc2aafcd41 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -468,33 +468,22 @@ EXPORT_SYMBOL_GPL(nf_nat_packet);
468struct nf_nat_proto_clean { 468struct nf_nat_proto_clean {
469 u8 l3proto; 469 u8 l3proto;
470 u8 l4proto; 470 u8 l4proto;
471 bool hash;
472}; 471};
473 472
474/* Clear NAT section of all conntracks, in case we're loaded again. */ 473/* kill conntracks with affected NAT section */
475static int nf_nat_proto_clean(struct nf_conn *i, void *data) 474static int nf_nat_proto_remove(struct nf_conn *i, void *data)
476{ 475{
477 const struct nf_nat_proto_clean *clean = data; 476 const struct nf_nat_proto_clean *clean = data;
478 struct nf_conn_nat *nat = nfct_nat(i); 477 struct nf_conn_nat *nat = nfct_nat(i);
479 478
480 if (!nat) 479 if (!nat)
481 return 0; 480 return 0;
482 if (!(i->status & IPS_SRC_NAT_DONE)) 481
483 return 0;
484 if ((clean->l3proto && nf_ct_l3num(i) != clean->l3proto) || 482 if ((clean->l3proto && nf_ct_l3num(i) != clean->l3proto) ||
485 (clean->l4proto && nf_ct_protonum(i) != clean->l4proto)) 483 (clean->l4proto && nf_ct_protonum(i) != clean->l4proto))
486 return 0; 484 return 0;
487 485
488 if (clean->hash) { 486 return i->status & IPS_NAT_MASK ? 1 : 0;
489 spin_lock_bh(&nf_nat_lock);
490 hlist_del_rcu(&nat->bysource);
491 spin_unlock_bh(&nf_nat_lock);
492 } else {
493 memset(nat, 0, sizeof(*nat));
494 i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK |
495 IPS_SEQ_ADJUST);
496 }
497 return 0;
498} 487}
499 488
500static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto) 489static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto)
@@ -506,16 +495,8 @@ static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto)
506 struct net *net; 495 struct net *net;
507 496
508 rtnl_lock(); 497 rtnl_lock();
509 /* Step 1 - remove from bysource hash */
510 clean.hash = true;
511 for_each_net(net) 498 for_each_net(net)
512 nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean); 499 nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean);
513 synchronize_rcu();
514
515 /* Step 2 - clean NAT section */
516 clean.hash = false;
517 for_each_net(net)
518 nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean);
519 rtnl_unlock(); 500 rtnl_unlock();
520} 501}
521 502
@@ -527,16 +508,9 @@ static void nf_nat_l3proto_clean(u8 l3proto)
527 struct net *net; 508 struct net *net;
528 509
529 rtnl_lock(); 510 rtnl_lock();
530 /* Step 1 - remove from bysource hash */
531 clean.hash = true;
532 for_each_net(net)
533 nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean);
534 synchronize_rcu();
535 511
536 /* Step 2 - clean NAT section */
537 clean.hash = false;
538 for_each_net(net) 512 for_each_net(net)
539 nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean); 513 nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean);
540 rtnl_unlock(); 514 rtnl_unlock();
541} 515}
542 516
@@ -774,7 +748,7 @@ static void __net_exit nf_nat_net_exit(struct net *net)
774{ 748{
775 struct nf_nat_proto_clean clean = {}; 749 struct nf_nat_proto_clean clean = {};
776 750
777 nf_ct_iterate_cleanup(net, &nf_nat_proto_clean, &clean); 751 nf_ct_iterate_cleanup(net, &nf_nat_proto_remove, &clean);
778 synchronize_rcu(); 752 synchronize_rcu();
779 nf_ct_free_hashtable(net->ct.nat_bysource, net->ct.nat_htable_size); 753 nf_ct_free_hashtable(net->ct.nat_bysource, net->ct.nat_htable_size);
780} 754}