aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2012-02-21 08:53:57 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2012-02-21 10:25:31 -0500
commitaf14cca162ddcdea017b648c21b9b091e4bf1fa4 (patch)
tree1d40e186b7afbd0c7485dd95e1fb6c24174f4d5c /net
parent88ba136d6635b262f77cc418d536115fb8e4d4ab (diff)
netfilter: ctnetlink: fix soft lockup when netlink adds new entries
Marcell Zambo and Janos Farago noticed and reported that when new conntrack entries are added via netlink and the conntrack table gets full, soft lockup happens. This is because the nf_conntrack_lock is held while nf_conntrack_alloc is called, which is in turn wants to lock nf_conntrack_lock while evicting entries from the full table. The patch fixes the soft lockup with limiting the holding of the nf_conntrack_lock to the minimum, where it's absolutely required. Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/nf_conntrack_netlink.c43
1 files changed, 16 insertions, 27 deletions
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 9307b033c0c9..cc705175765c 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1367,15 +1367,12 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
1367 nf_ct_protonum(ct)); 1367 nf_ct_protonum(ct));
1368 if (helper == NULL) { 1368 if (helper == NULL) {
1369 rcu_read_unlock(); 1369 rcu_read_unlock();
1370 spin_unlock_bh(&nf_conntrack_lock);
1371#ifdef CONFIG_MODULES 1370#ifdef CONFIG_MODULES
1372 if (request_module("nfct-helper-%s", helpname) < 0) { 1371 if (request_module("nfct-helper-%s", helpname) < 0) {
1373 spin_lock_bh(&nf_conntrack_lock);
1374 err = -EOPNOTSUPP; 1372 err = -EOPNOTSUPP;
1375 goto err1; 1373 goto err1;
1376 } 1374 }
1377 1375
1378 spin_lock_bh(&nf_conntrack_lock);
1379 rcu_read_lock(); 1376 rcu_read_lock();
1380 helper = __nf_conntrack_helper_find(helpname, 1377 helper = __nf_conntrack_helper_find(helpname,
1381 nf_ct_l3num(ct), 1378 nf_ct_l3num(ct),
@@ -1469,7 +1466,10 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
1469 tstamp->start = ktime_to_ns(ktime_get_real()); 1466 tstamp->start = ktime_to_ns(ktime_get_real());
1470 1467
1471 add_timer(&ct->timeout); 1468 add_timer(&ct->timeout);
1469 spin_lock_bh(&nf_conntrack_lock);
1472 nf_conntrack_hash_insert(ct); 1470 nf_conntrack_hash_insert(ct);
1471 nf_conntrack_get(&ct->ct_general);
1472 spin_unlock_bh(&nf_conntrack_lock);
1473 rcu_read_unlock(); 1473 rcu_read_unlock();
1474 1474
1475 return ct; 1475 return ct;
@@ -1490,6 +1490,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
1490 struct nf_conntrack_tuple otuple, rtuple; 1490 struct nf_conntrack_tuple otuple, rtuple;
1491 struct nf_conntrack_tuple_hash *h = NULL; 1491 struct nf_conntrack_tuple_hash *h = NULL;
1492 struct nfgenmsg *nfmsg = nlmsg_data(nlh); 1492 struct nfgenmsg *nfmsg = nlmsg_data(nlh);
1493 struct nf_conn *ct;
1493 u_int8_t u3 = nfmsg->nfgen_family; 1494 u_int8_t u3 = nfmsg->nfgen_family;
1494 u16 zone; 1495 u16 zone;
1495 int err; 1496 int err;
@@ -1512,25 +1513,22 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
1512 1513
1513 spin_lock_bh(&nf_conntrack_lock); 1514 spin_lock_bh(&nf_conntrack_lock);
1514 if (cda[CTA_TUPLE_ORIG]) 1515 if (cda[CTA_TUPLE_ORIG])
1515 h = __nf_conntrack_find(net, zone, &otuple); 1516 h = nf_conntrack_find_get(net, zone, &otuple);
1516 else if (cda[CTA_TUPLE_REPLY]) 1517 else if (cda[CTA_TUPLE_REPLY])
1517 h = __nf_conntrack_find(net, zone, &rtuple); 1518 h = nf_conntrack_find_get(net, zone, &rtuple);
1519 spin_unlock_bh(&nf_conntrack_lock);
1518 1520
1519 if (h == NULL) { 1521 if (h == NULL) {
1520 err = -ENOENT; 1522 err = -ENOENT;
1521 if (nlh->nlmsg_flags & NLM_F_CREATE) { 1523 if (nlh->nlmsg_flags & NLM_F_CREATE) {
1522 struct nf_conn *ct;
1523 enum ip_conntrack_events events; 1524 enum ip_conntrack_events events;
1524 1525
1525 ct = ctnetlink_create_conntrack(net, zone, cda, &otuple, 1526 ct = ctnetlink_create_conntrack(net, zone, cda, &otuple,
1526 &rtuple, u3); 1527 &rtuple, u3);
1527 if (IS_ERR(ct)) { 1528 if (IS_ERR(ct))
1528 err = PTR_ERR(ct); 1529 return PTR_ERR(ct);
1529 goto out_unlock; 1530
1530 }
1531 err = 0; 1531 err = 0;
1532 nf_conntrack_get(&ct->ct_general);
1533 spin_unlock_bh(&nf_conntrack_lock);
1534 if (test_bit(IPS_EXPECTED_BIT, &ct->status)) 1532 if (test_bit(IPS_EXPECTED_BIT, &ct->status))
1535 events = IPCT_RELATED; 1533 events = IPCT_RELATED;
1536 else 1534 else
@@ -1545,23 +1543,19 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
1545 ct, NETLINK_CB(skb).pid, 1543 ct, NETLINK_CB(skb).pid,
1546 nlmsg_report(nlh)); 1544 nlmsg_report(nlh));
1547 nf_ct_put(ct); 1545 nf_ct_put(ct);
1548 } else 1546 }
1549 spin_unlock_bh(&nf_conntrack_lock);
1550 1547
1551 return err; 1548 return err;
1552 } 1549 }
1553 /* implicit 'else' */ 1550 /* implicit 'else' */
1554 1551
1555 /* We manipulate the conntrack inside the global conntrack table lock,
1556 * so there's no need to increase the refcount */
1557 err = -EEXIST; 1552 err = -EEXIST;
1553 ct = nf_ct_tuplehash_to_ctrack(h);
1558 if (!(nlh->nlmsg_flags & NLM_F_EXCL)) { 1554 if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
1559 struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); 1555 spin_lock_bh(&nf_conntrack_lock);
1560
1561 err = ctnetlink_change_conntrack(ct, cda); 1556 err = ctnetlink_change_conntrack(ct, cda);
1557 spin_unlock_bh(&nf_conntrack_lock);
1562 if (err == 0) { 1558 if (err == 0) {
1563 nf_conntrack_get(&ct->ct_general);
1564 spin_unlock_bh(&nf_conntrack_lock);
1565 nf_conntrack_eventmask_report((1 << IPCT_REPLY) | 1559 nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
1566 (1 << IPCT_ASSURED) | 1560 (1 << IPCT_ASSURED) |
1567 (1 << IPCT_HELPER) | 1561 (1 << IPCT_HELPER) |
@@ -1570,15 +1564,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
1570 (1 << IPCT_MARK), 1564 (1 << IPCT_MARK),
1571 ct, NETLINK_CB(skb).pid, 1565 ct, NETLINK_CB(skb).pid,
1572 nlmsg_report(nlh)); 1566 nlmsg_report(nlh));
1573 nf_ct_put(ct); 1567 }
1574 } else
1575 spin_unlock_bh(&nf_conntrack_lock);
1576
1577 return err;
1578 } 1568 }
1579 1569
1580out_unlock: 1570 nf_ct_put(ct);
1581 spin_unlock_bh(&nf_conntrack_lock);
1582 return err; 1571 return err;
1583} 1572}
1584 1573