Skip to content

Commit

Permalink
netfilter: nft_hash: add support for timeouts
Browse files Browse the repository at this point in the history
Add support for element timeouts to nft_hash. The lookup and walking
functions are changed to ignore timed out elements, a periodic garbage
collection task cleans out expired entries.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
kaber authored and ummakynes committed Apr 1, 2015
1 parent 6908665 commit 9d09829
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 4 deletions.
5 changes: 5 additions & 0 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,11 @@ static inline void *nft_set_priv(const struct nft_set *set)
return (void *)set->data;
}

static inline struct nft_set *nft_set_container_of(const void *priv)
{
return (void *)priv - offsetof(struct nft_set, data);
}

struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
const struct nlattr *nla);
struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
Expand Down
79 changes: 75 additions & 4 deletions net/netfilter/nft_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/log2.h>
#include <linux/jhash.h>
#include <linux/netlink.h>
#include <linux/workqueue.h>
#include <linux/rhashtable.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
Expand All @@ -25,6 +26,7 @@

struct nft_hash {
struct rhashtable ht;
struct delayed_work gc_work;
};

struct nft_hash_elem {
Expand Down Expand Up @@ -62,6 +64,8 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg,

if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen))
return 1;
if (nft_set_elem_expired(&he->ext))
return 1;
if (!nft_set_elem_active(&he->ext, x->genmask))
return 1;
return 0;
Expand Down Expand Up @@ -107,6 +111,7 @@ static void nft_hash_activate(const struct nft_set *set,
struct nft_hash_elem *he = elem->priv;

nft_set_elem_change_active(set, &he->ext);
nft_set_elem_clear_busy(&he->ext);
}

static void *nft_hash_deactivate(const struct nft_set *set,
Expand All @@ -120,9 +125,15 @@ static void *nft_hash_deactivate(const struct nft_set *set,
.key = &elem->key,
};

rcu_read_lock();
he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
if (he != NULL)
nft_set_elem_change_active(set, &he->ext);
if (he != NULL) {
if (!nft_set_elem_mark_busy(&he->ext))
nft_set_elem_change_active(set, &he->ext);
else
he = NULL;
}
rcu_read_unlock();

return he;
}
Expand Down Expand Up @@ -170,6 +181,8 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,

if (iter->count < iter->skip)
goto cont;
if (nft_set_elem_expired(&he->ext))
goto cont;
if (!nft_set_elem_active(&he->ext, genmask))
goto cont;

Expand All @@ -188,6 +201,54 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
rhashtable_walk_exit(&hti);
}

static void nft_hash_gc(struct work_struct *work)
{
const struct nft_set *set;
struct nft_hash_elem *he;
struct nft_hash *priv;
struct nft_set_gc_batch *gcb = NULL;
struct rhashtable_iter hti;
int err;

priv = container_of(work, struct nft_hash, gc_work.work);
set = nft_set_container_of(priv);

err = rhashtable_walk_init(&priv->ht, &hti);
if (err)
goto schedule;

err = rhashtable_walk_start(&hti);
if (err && err != -EAGAIN)
goto out;

while ((he = rhashtable_walk_next(&hti))) {
if (IS_ERR(he)) {
if (PTR_ERR(he) != -EAGAIN)
goto out;
continue;
}

if (!nft_set_elem_expired(&he->ext))
continue;
if (nft_set_elem_mark_busy(&he->ext))
continue;

gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
if (gcb == NULL)
goto out;
rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params);
nft_set_gc_batch_add(gcb, he);
}
out:
rhashtable_walk_stop(&hti);
rhashtable_walk_exit(&hti);

nft_set_gc_batch_complete(gcb);
schedule:
queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
nft_set_gc_interval(set));
}

static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
{
return sizeof(struct nft_hash);
Expand All @@ -207,11 +268,20 @@ static int nft_hash_init(const struct nft_set *set,
{
struct nft_hash *priv = nft_set_priv(set);
struct rhashtable_params params = nft_hash_params;
int err;

params.nelem_hint = desc->size ?: NFT_HASH_ELEMENT_HINT;
params.key_len = set->klen;

return rhashtable_init(&priv->ht, &params);
err = rhashtable_init(&priv->ht, &params);
if (err < 0)
return err;

INIT_DEFERRABLE_WORK(&priv->gc_work, nft_hash_gc);
if (set->flags & NFT_SET_TIMEOUT)
queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
nft_set_gc_interval(set));
return 0;
}

static void nft_hash_elem_destroy(void *ptr, void *arg)
Expand All @@ -223,6 +293,7 @@ static void nft_hash_destroy(const struct nft_set *set)
{
struct nft_hash *priv = nft_set_priv(set);

cancel_delayed_work_sync(&priv->gc_work);
rhashtable_free_and_destroy(&priv->ht, nft_hash_elem_destroy,
(void *)set);
}
Expand Down Expand Up @@ -264,7 +335,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
.remove = nft_hash_remove,
.lookup = nft_hash_lookup,
.walk = nft_hash_walk,
.features = NFT_SET_MAP,
.features = NFT_SET_MAP | NFT_SET_TIMEOUT,
.owner = THIS_MODULE,
};

Expand Down

0 comments on commit 9d09829

Please sign in to comment.