mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
netfilter: nf_conncount: split count_tree_node rbtree walk into helper
Add find_tree_node() helper that fetches a matching rbtree node. This is used by followup patch to optionally search the tree again while preventing concurrent updates via tree lock. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
eae341ecfc
commit
2e064ae859
|
|
@ -488,6 +488,34 @@ insert_tree(struct net *net,
|
|||
return count;
|
||||
}
|
||||
|
||||
static struct nf_conncount_rb *
|
||||
find_tree_node(struct nf_conncount_root *root, struct nf_conncount_data *data,
|
||||
const u32 *key)
|
||||
{
|
||||
struct rb_node *parent;
|
||||
|
||||
parent = rcu_dereference_check(root->root.rb_node,
|
||||
lockdep_is_held(&root->lock));
|
||||
while (parent) {
|
||||
struct nf_conncount_rb *rbconn;
|
||||
int diff;
|
||||
|
||||
rbconn = rb_entry(parent, struct nf_conncount_rb, node);
|
||||
|
||||
diff = key_diff(key, rbconn->key, data->keylen);
|
||||
if (diff < 0)
|
||||
parent = rcu_dereference_check(parent->rb_left,
|
||||
lockdep_is_held(&root->lock));
|
||||
else if (diff > 0)
|
||||
parent = rcu_dereference_check(parent->rb_right,
|
||||
lockdep_is_held(&root->lock));
|
||||
else
|
||||
return rbconn;
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
count_tree(struct net *net,
|
||||
const struct sk_buff *skb,
|
||||
|
|
@ -496,59 +524,53 @@ count_tree(struct net *net,
|
|||
const u32 *key)
|
||||
{
|
||||
struct nf_conncount_root *root;
|
||||
struct rb_node *parent;
|
||||
struct nf_conncount_rb *rbconn;
|
||||
unsigned int hash;
|
||||
int ret;
|
||||
|
||||
hash = jhash2(key, data->keylen, data->initval) % CONNCOUNT_SLOTS;
|
||||
root = &data->root[hash];
|
||||
|
||||
parent = rcu_dereference(root->root.rb_node);
|
||||
while (parent) {
|
||||
int diff;
|
||||
rbconn = find_tree_node(root, data, key);
|
||||
if (IS_ERR(rbconn)) {
|
||||
if (PTR_ERR(rbconn) == -ENOENT) {
|
||||
if (!skb)
|
||||
return 0;
|
||||
|
||||
rbconn = rb_entry(parent, struct nf_conncount_rb, node);
|
||||
|
||||
diff = key_diff(key, rbconn->key, data->keylen);
|
||||
if (diff < 0) {
|
||||
parent = rcu_dereference(parent->rb_left);
|
||||
} else if (diff > 0) {
|
||||
parent = rcu_dereference(parent->rb_right);
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
if (!skb) {
|
||||
nf_conncount_gc_list(net, &rbconn->list);
|
||||
return rbconn->list.count;
|
||||
}
|
||||
|
||||
spin_lock_bh(&rbconn->list.list_lock);
|
||||
/* Node might be about to be free'd.
|
||||
* We need to defer to insert_tree() in this case.
|
||||
*/
|
||||
if (rbconn->list.count == 0) {
|
||||
spin_unlock_bh(&rbconn->list.list_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
/* same source network -> be counted! */
|
||||
ret = __nf_conncount_add(net, skb, l3num, &rbconn->list);
|
||||
spin_unlock_bh(&rbconn->list.list_lock);
|
||||
if (ret && ret != -EEXIST) {
|
||||
return 0; /* hotdrop */
|
||||
} else {
|
||||
/* -EEXIST means add was skipped, update the list */
|
||||
if (ret == -EEXIST)
|
||||
nf_conncount_gc_list(net, &rbconn->list);
|
||||
return rbconn->list.count;
|
||||
}
|
||||
return insert_tree(net, skb, l3num, data, hash, key);
|
||||
}
|
||||
DEBUG_NET_WARN_ON_ONCE(IS_ERR(rbconn));
|
||||
}
|
||||
|
||||
if (!skb)
|
||||
DEBUG_NET_WARN_ON_ONCE(IS_ERR_OR_NULL(rbconn));
|
||||
if (IS_ERR_OR_NULL(rbconn))
|
||||
return 0;
|
||||
|
||||
return insert_tree(net, skb, l3num, data, hash, key);
|
||||
if (!skb) {
|
||||
nf_conncount_gc_list(net, &rbconn->list);
|
||||
return rbconn->list.count;
|
||||
}
|
||||
|
||||
spin_lock_bh(&rbconn->list.list_lock);
|
||||
/* Node might be about to be free'd.
|
||||
* We need to defer to insert_tree() in this case.
|
||||
*/
|
||||
if (rbconn->list.count == 0) {
|
||||
spin_unlock_bh(&rbconn->list.list_lock);
|
||||
return insert_tree(net, skb, l3num, data, hash, key);
|
||||
}
|
||||
|
||||
/* same source network -> be counted! */
|
||||
ret = __nf_conncount_add(net, skb, l3num, &rbconn->list);
|
||||
spin_unlock_bh(&rbconn->list.list_lock);
|
||||
|
||||
if (ret && ret != -EEXIST)
|
||||
return 0; /* hotdrop */
|
||||
/* -EEXIST means add was skipped, update the list */
|
||||
if (ret == -EEXIST)
|
||||
nf_conncount_gc_list(net, &rbconn->list);
|
||||
|
||||
return rbconn->list.count;
|
||||
}
|
||||
|
||||
static void tree_gc_worker(struct work_struct *work)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user