Skip to content

Commit 9d09829

Browse files
kaberummakynes
authored andcommitted
netfilter: nft_hash: add support for timeouts
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 <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 6908665 commit 9d09829

File tree

2 files changed

+80
-4
lines changed

2 files changed

+80
-4
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ static inline void *nft_set_priv(const struct nft_set *set)
294294
return (void *)set->data;
295295
}
296296

297+
static inline struct nft_set *nft_set_container_of(const void *priv)
298+
{
299+
return (void *)priv - offsetof(struct nft_set, data);
300+
}
301+
297302
struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
298303
const struct nlattr *nla);
299304
struct nft_set *nf_tables_set_lookup_byid(const struct net *net,

net/netfilter/nft_hash.c

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/log2.h>
1616
#include <linux/jhash.h>
1717
#include <linux/netlink.h>
18+
#include <linux/workqueue.h>
1819
#include <linux/rhashtable.h>
1920
#include <linux/netfilter.h>
2021
#include <linux/netfilter/nf_tables.h>
@@ -25,6 +26,7 @@
2526

2627
struct nft_hash {
2728
struct rhashtable ht;
29+
struct delayed_work gc_work;
2830
};
2931

3032
struct nft_hash_elem {
@@ -62,6 +64,8 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg,
6264

6365
if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen))
6466
return 1;
67+
if (nft_set_elem_expired(&he->ext))
68+
return 1;
6569
if (!nft_set_elem_active(&he->ext, x->genmask))
6670
return 1;
6771
return 0;
@@ -107,6 +111,7 @@ static void nft_hash_activate(const struct nft_set *set,
107111
struct nft_hash_elem *he = elem->priv;
108112

109113
nft_set_elem_change_active(set, &he->ext);
114+
nft_set_elem_clear_busy(&he->ext);
110115
}
111116

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

128+
rcu_read_lock();
123129
he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
124-
if (he != NULL)
125-
nft_set_elem_change_active(set, &he->ext);
130+
if (he != NULL) {
131+
if (!nft_set_elem_mark_busy(&he->ext))
132+
nft_set_elem_change_active(set, &he->ext);
133+
else
134+
he = NULL;
135+
}
136+
rcu_read_unlock();
126137

127138
return he;
128139
}
@@ -170,6 +181,8 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
170181

171182
if (iter->count < iter->skip)
172183
goto cont;
184+
if (nft_set_elem_expired(&he->ext))
185+
goto cont;
173186
if (!nft_set_elem_active(&he->ext, genmask))
174187
goto cont;
175188

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

204+
static void nft_hash_gc(struct work_struct *work)
205+
{
206+
const struct nft_set *set;
207+
struct nft_hash_elem *he;
208+
struct nft_hash *priv;
209+
struct nft_set_gc_batch *gcb = NULL;
210+
struct rhashtable_iter hti;
211+
int err;
212+
213+
priv = container_of(work, struct nft_hash, gc_work.work);
214+
set = nft_set_container_of(priv);
215+
216+
err = rhashtable_walk_init(&priv->ht, &hti);
217+
if (err)
218+
goto schedule;
219+
220+
err = rhashtable_walk_start(&hti);
221+
if (err && err != -EAGAIN)
222+
goto out;
223+
224+
while ((he = rhashtable_walk_next(&hti))) {
225+
if (IS_ERR(he)) {
226+
if (PTR_ERR(he) != -EAGAIN)
227+
goto out;
228+
continue;
229+
}
230+
231+
if (!nft_set_elem_expired(&he->ext))
232+
continue;
233+
if (nft_set_elem_mark_busy(&he->ext))
234+
continue;
235+
236+
gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
237+
if (gcb == NULL)
238+
goto out;
239+
rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params);
240+
nft_set_gc_batch_add(gcb, he);
241+
}
242+
out:
243+
rhashtable_walk_stop(&hti);
244+
rhashtable_walk_exit(&hti);
245+
246+
nft_set_gc_batch_complete(gcb);
247+
schedule:
248+
queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
249+
nft_set_gc_interval(set));
250+
}
251+
191252
static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
192253
{
193254
return sizeof(struct nft_hash);
@@ -207,11 +268,20 @@ static int nft_hash_init(const struct nft_set *set,
207268
{
208269
struct nft_hash *priv = nft_set_priv(set);
209270
struct rhashtable_params params = nft_hash_params;
271+
int err;
210272

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

214-
return rhashtable_init(&priv->ht, &params);
276+
err = rhashtable_init(&priv->ht, &params);
277+
if (err < 0)
278+
return err;
279+
280+
INIT_DEFERRABLE_WORK(&priv->gc_work, nft_hash_gc);
281+
if (set->flags & NFT_SET_TIMEOUT)
282+
queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
283+
nft_set_gc_interval(set));
284+
return 0;
215285
}
216286

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

296+
cancel_delayed_work_sync(&priv->gc_work);
226297
rhashtable_free_and_destroy(&priv->ht, nft_hash_elem_destroy,
227298
(void *)set);
228299
}
@@ -264,7 +335,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
264335
.remove = nft_hash_remove,
265336
.lookup = nft_hash_lookup,
266337
.walk = nft_hash_walk,
267-
.features = NFT_SET_MAP,
338+
.features = NFT_SET_MAP | NFT_SET_TIMEOUT,
268339
.owner = THIS_MODULE,
269340
};
270341

0 commit comments

Comments
 (0)