Commit 976afca1 authored by Yi-Hung Wei's avatar Yi-Hung Wei Committed by Pablo Neira Ayuso

netfilter: nf_conncount: Early exit in nf_conncount_lookup() and cleanup

This patch is originally from Florian Westphal.

This patch does the following three tasks.

It applies the same early exit technique for nf_conncount_lookup().

Since now we keep the number of connections in 'struct nf_conncount_list',
we no longer need to return the count in nf_conncount_lookup().

Moreover, we expose the garbage collection function nf_conncount_gc_list()
for nft_connlimit.
Signed-off-by: default avatarYi-Hung Wei <yihung.wei@gmail.com>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent cb2b36f5
...@@ -21,10 +21,10 @@ unsigned int nf_conncount_count(struct net *net, ...@@ -21,10 +21,10 @@ unsigned int nf_conncount_count(struct net *net,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_zone *zone); const struct nf_conntrack_zone *zone);
unsigned int nf_conncount_lookup(struct net *net, struct nf_conncount_list *list, void nf_conncount_lookup(struct net *net, struct nf_conncount_list *list,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_zone *zone, const struct nf_conntrack_zone *zone,
bool *addit); bool *addit);
void nf_conncount_list_init(struct nf_conncount_list *list); void nf_conncount_list_init(struct nf_conncount_list *list);
...@@ -32,6 +32,9 @@ bool nf_conncount_add(struct nf_conncount_list *list, ...@@ -32,6 +32,9 @@ bool nf_conncount_add(struct nf_conncount_list *list,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_zone *zone); const struct nf_conntrack_zone *zone);
void nf_conncount_gc_list(struct net *net,
struct nf_conncount_list *list);
void nf_conncount_cache_free(struct nf_conncount_list *list); void nf_conncount_cache_free(struct nf_conncount_list *list);
#endif #endif
...@@ -144,26 +144,29 @@ find_or_evict(struct net *net, struct nf_conncount_list *list, ...@@ -144,26 +144,29 @@ find_or_evict(struct net *net, struct nf_conncount_list *list,
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
} }
unsigned int nf_conncount_lookup(struct net *net, void nf_conncount_lookup(struct net *net,
struct nf_conncount_list *list, struct nf_conncount_list *list,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_zone *zone, const struct nf_conntrack_zone *zone,
bool *addit) bool *addit)
{ {
const struct nf_conntrack_tuple_hash *found; const struct nf_conntrack_tuple_hash *found;
struct nf_conncount_tuple *conn, *conn_n; struct nf_conncount_tuple *conn, *conn_n;
struct nf_conn *found_ct; struct nf_conn *found_ct;
unsigned int length = 0; unsigned int collect = 0;
/* best effort only */
*addit = tuple ? true : false; *addit = tuple ? true : false;
/* check the saved connections */ /* check the saved connections */
list_for_each_entry_safe(conn, conn_n, &list->head, node) { list_for_each_entry_safe(conn, conn_n, &list->head, node) {
if (collect > CONNCOUNT_GC_MAX_NODES)
break;
found = find_or_evict(net, list, conn); found = find_or_evict(net, list, conn);
if (IS_ERR(found)) { if (IS_ERR(found)) {
/* Not found, but might be about to be confirmed */ /* Not found, but might be about to be confirmed */
if (PTR_ERR(found) == -EAGAIN) { if (PTR_ERR(found) == -EAGAIN) {
length++;
if (!tuple) if (!tuple)
continue; continue;
...@@ -171,8 +174,8 @@ unsigned int nf_conncount_lookup(struct net *net, ...@@ -171,8 +174,8 @@ unsigned int nf_conncount_lookup(struct net *net,
nf_ct_zone_id(&conn->zone, conn->zone.dir) == nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
nf_ct_zone_id(zone, zone->dir)) nf_ct_zone_id(zone, zone->dir))
*addit = false; *addit = false;
} } else if (PTR_ERR(found) == -ENOENT)
collect++;
continue; continue;
} }
...@@ -181,9 +184,10 @@ unsigned int nf_conncount_lookup(struct net *net, ...@@ -181,9 +184,10 @@ unsigned int nf_conncount_lookup(struct net *net,
if (tuple && nf_ct_tuple_equal(&conn->tuple, tuple) && if (tuple && nf_ct_tuple_equal(&conn->tuple, tuple) &&
nf_ct_zone_equal(found_ct, zone, zone->dir)) { nf_ct_zone_equal(found_ct, zone, zone->dir)) {
/* /*
* Just to be sure we have it only once in the list.
* We should not see tuples twice unless someone hooks * We should not see tuples twice unless someone hooks
* this into a table without "-p tcp --syn". * this into a table without "-p tcp --syn".
*
* Attempt to avoid a re-add in this case.
*/ */
*addit = false; *addit = false;
} else if (already_closed(found_ct)) { } else if (already_closed(found_ct)) {
...@@ -193,14 +197,12 @@ unsigned int nf_conncount_lookup(struct net *net, ...@@ -193,14 +197,12 @@ unsigned int nf_conncount_lookup(struct net *net,
*/ */
nf_ct_put(found_ct); nf_ct_put(found_ct);
conn_free(list, conn); conn_free(list, conn);
collect++;
continue; continue;
} }
nf_ct_put(found_ct); nf_ct_put(found_ct);
length++;
} }
return length;
} }
EXPORT_SYMBOL_GPL(nf_conncount_lookup); EXPORT_SYMBOL_GPL(nf_conncount_lookup);
...@@ -211,8 +213,8 @@ void nf_conncount_list_init(struct nf_conncount_list *list) ...@@ -211,8 +213,8 @@ void nf_conncount_list_init(struct nf_conncount_list *list)
} }
EXPORT_SYMBOL_GPL(nf_conncount_list_init); EXPORT_SYMBOL_GPL(nf_conncount_list_init);
static void nf_conncount_gc_list(struct net *net, void nf_conncount_gc_list(struct net *net,
struct nf_conncount_list *list) struct nf_conncount_list *list)
{ {
const struct nf_conntrack_tuple_hash *found; const struct nf_conntrack_tuple_hash *found;
struct nf_conncount_tuple *conn, *conn_n; struct nf_conncount_tuple *conn, *conn_n;
...@@ -244,6 +246,7 @@ static void nf_conncount_gc_list(struct net *net, ...@@ -244,6 +246,7 @@ static void nf_conncount_gc_list(struct net *net,
return; return;
} }
} }
EXPORT_SYMBOL_GPL(nf_conncount_gc_list);
static void tree_nodes_free(struct rb_root *root, static void tree_nodes_free(struct rb_root *root,
struct nf_conncount_rb *gc_nodes[], struct nf_conncount_rb *gc_nodes[],
...@@ -291,8 +294,9 @@ count_tree(struct net *net, struct rb_root *root, ...@@ -291,8 +294,9 @@ count_tree(struct net *net, struct rb_root *root,
/* same source network -> be counted! */ /* same source network -> be counted! */
unsigned int count; unsigned int count;
count = nf_conncount_lookup(net, &rbconn->list, tuple, nf_conncount_lookup(net, &rbconn->list, tuple, zone,
zone, &addit); &addit);
count = rbconn->list.count;
tree_nodes_free(root, gc_nodes, gc_count); tree_nodes_free(root, gc_nodes, gc_count);
if (!addit) if (!addit)
......
...@@ -46,8 +46,9 @@ static inline void nft_connlimit_do_eval(struct nft_connlimit *priv, ...@@ -46,8 +46,9 @@ static inline void nft_connlimit_do_eval(struct nft_connlimit *priv,
} }
spin_lock_bh(&priv->lock); spin_lock_bh(&priv->lock);
count = nf_conncount_lookup(nft_net(pkt), &priv->list, tuple_ptr, zone, nf_conncount_lookup(nft_net(pkt), &priv->list, tuple_ptr, zone,
&addit); &addit);
count = priv->list.count;
if (!addit) if (!addit)
goto out; goto out;
...@@ -231,10 +232,10 @@ static void nft_connlimit_destroy_clone(const struct nft_ctx *ctx, ...@@ -231,10 +232,10 @@ static void nft_connlimit_destroy_clone(const struct nft_ctx *ctx,
static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr) static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr)
{ {
struct nft_connlimit *priv = nft_expr_priv(expr); struct nft_connlimit *priv = nft_expr_priv(expr);
bool addit, ret; bool ret;
spin_lock_bh(&priv->lock); spin_lock_bh(&priv->lock);
nf_conncount_lookup(net, &priv->list, NULL, &nf_ct_zone_dflt, &addit); nf_conncount_gc_list(net, &priv->list);
ret = list_empty(&priv->list.head); ret = list_empty(&priv->list.head);
spin_unlock_bh(&priv->lock); spin_unlock_bh(&priv->lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment