Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
96518518 | 2 | /* |
ce6eb0d7 | 3 | * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net> |
96518518 | 4 | * |
96518518 PM |
5 | * Development of this code funded by Astaro AG (http://www.astaro.com/) |
6 | */ | |
7 | ||
8 | #include <linux/kernel.h> | |
9 | #include <linux/init.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/list.h> | |
c50b960c | 12 | #include <linux/log2.h> |
96518518 PM |
13 | #include <linux/jhash.h> |
14 | #include <linux/netlink.h> | |
9d098292 | 15 | #include <linux/workqueue.h> |
cfe4a9dd | 16 | #include <linux/rhashtable.h> |
96518518 PM |
17 | #include <linux/netfilter.h> |
18 | #include <linux/netfilter/nf_tables.h> | |
5785cf15 | 19 | #include <net/netfilter/nf_tables_core.h> |
96518518 | 20 | |
cfe4a9dd | 21 | /* We target a hash table size of 4, element hint is 75% of final size */ |
5fc6ced9 | 22 | #define NFT_RHASH_ELEMENT_HINT 3 |
96518518 | 23 | |
5fc6ced9 | 24 | struct nft_rhash { |
745f5450 | 25 | struct rhashtable ht; |
9d098292 | 26 | struct delayed_work gc_work; |
745f5450 PM |
27 | }; |
28 | ||
5fc6ced9 | 29 | struct nft_rhash_elem { |
cfe4a9dd | 30 | struct rhash_head node; |
fe2811eb | 31 | struct nft_set_ext ext; |
96518518 PM |
32 | }; |
33 | ||
5fc6ced9 | 34 | struct nft_rhash_cmp_arg { |
bfd6e327 | 35 | const struct nft_set *set; |
8cd8937a | 36 | const u32 *key; |
cc02e457 | 37 | u8 genmask; |
bfd6e327 PM |
38 | }; |
39 | ||
5fc6ced9 | 40 | static inline u32 nft_rhash_key(const void *data, u32 len, u32 seed) |
bfd6e327 | 41 | { |
5fc6ced9 | 42 | const struct nft_rhash_cmp_arg *arg = data; |
bfd6e327 PM |
43 | |
44 | return jhash(arg->key, len, seed); | |
45 | } | |
46 | ||
5fc6ced9 | 47 | static inline u32 nft_rhash_obj(const void *data, u32 len, u32 seed) |
bfd6e327 | 48 | { |
5fc6ced9 | 49 | const struct nft_rhash_elem *he = data; |
bfd6e327 | 50 | |
fe2811eb | 51 | return jhash(nft_set_ext_key(&he->ext), len, seed); |
bfd6e327 PM |
52 | } |
53 | ||
5fc6ced9 PNA |
54 | static inline int nft_rhash_cmp(struct rhashtable_compare_arg *arg, |
55 | const void *ptr) | |
bfd6e327 | 56 | { |
5fc6ced9 PNA |
57 | const struct nft_rhash_cmp_arg *x = arg->key; |
58 | const struct nft_rhash_elem *he = ptr; | |
bfd6e327 | 59 | |
e562d860 | 60 | if (memcmp(nft_set_ext_key(&he->ext), x->key, x->set->klen)) |
bfd6e327 | 61 | return 1; |
9d098292 PM |
62 | if (nft_set_elem_expired(&he->ext)) |
63 | return 1; | |
cc02e457 PM |
64 | if (!nft_set_elem_active(&he->ext, x->genmask)) |
65 | return 1; | |
bfd6e327 PM |
66 | return 0; |
67 | } | |
68 | ||
5fc6ced9 PNA |
69 | static const struct rhashtable_params nft_rhash_params = { |
70 | .head_offset = offsetof(struct nft_rhash_elem, node), | |
71 | .hashfn = nft_rhash_key, | |
72 | .obj_hashfn = nft_rhash_obj, | |
73 | .obj_cmpfn = nft_rhash_cmp, | |
187388bc PNA |
74 | .automatic_shrinking = true, |
75 | }; | |
76 | ||
f227925e FW |
77 | INDIRECT_CALLABLE_SCOPE |
78 | bool nft_rhash_lookup(const struct net *net, const struct nft_set *set, | |
79 | const u32 *key, const struct nft_set_ext **ext) | |
96518518 | 80 | { |
5fc6ced9 PNA |
81 | struct nft_rhash *priv = nft_set_priv(set); |
82 | const struct nft_rhash_elem *he; | |
83 | struct nft_rhash_cmp_arg arg = { | |
42a55769 | 84 | .genmask = nft_genmask_cur(net), |
bfd6e327 PM |
85 | .set = set, |
86 | .key = key, | |
87 | }; | |
ce6eb0d7 | 88 | |
a2d88182 | 89 | he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params); |
b2832dd6 PM |
90 | if (he != NULL) |
91 | *ext = &he->ext; | |
ce6eb0d7 | 92 | |
cfe4a9dd | 93 | return !!he; |
96518518 PM |
94 | } |
95 | ||
ba0e4d99 PNA |
96 | static void *nft_rhash_get(const struct net *net, const struct nft_set *set, |
97 | const struct nft_set_elem *elem, unsigned int flags) | |
98 | { | |
99 | struct nft_rhash *priv = nft_set_priv(set); | |
100 | struct nft_rhash_elem *he; | |
101 | struct nft_rhash_cmp_arg arg = { | |
102 | .genmask = nft_genmask_cur(net), | |
103 | .set = set, | |
104 | .key = elem->key.val.data, | |
105 | }; | |
106 | ||
a2d88182 | 107 | he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params); |
ba0e4d99 PNA |
108 | if (he != NULL) |
109 | return he; | |
110 | ||
111 | return ERR_PTR(-ENOENT); | |
112 | } | |
113 | ||
5fc6ced9 PNA |
114 | static bool nft_rhash_update(struct nft_set *set, const u32 *key, |
115 | void *(*new)(struct nft_set *, | |
116 | const struct nft_expr *, | |
117 | struct nft_regs *regs), | |
118 | const struct nft_expr *expr, | |
119 | struct nft_regs *regs, | |
120 | const struct nft_set_ext **ext) | |
22fe54d5 | 121 | { |
5fc6ced9 PNA |
122 | struct nft_rhash *priv = nft_set_priv(set); |
123 | struct nft_rhash_elem *he, *prev; | |
124 | struct nft_rhash_cmp_arg arg = { | |
22fe54d5 PM |
125 | .genmask = NFT_GENMASK_ANY, |
126 | .set = set, | |
127 | .key = key, | |
128 | }; | |
129 | ||
a2d88182 | 130 | he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params); |
22fe54d5 PM |
131 | if (he != NULL) |
132 | goto out; | |
133 | ||
a55e22e9 | 134 | he = new(set, expr, regs); |
22fe54d5 PM |
135 | if (he == NULL) |
136 | goto err1; | |
dab45060 LZ |
137 | |
138 | prev = rhashtable_lookup_get_insert_key(&priv->ht, &arg, &he->node, | |
5fc6ced9 | 139 | nft_rhash_params); |
dab45060 | 140 | if (IS_ERR(prev)) |
22fe54d5 | 141 | goto err2; |
dab45060 LZ |
142 | |
143 | /* Another cpu may race to insert the element with the same key */ | |
144 | if (prev) { | |
145 | nft_set_elem_destroy(set, he, true); | |
05907f10 | 146 | atomic_dec(&set->nelems); |
dab45060 LZ |
147 | he = prev; |
148 | } | |
149 | ||
22fe54d5 PM |
150 | out: |
151 | *ext = &he->ext; | |
152 | return true; | |
153 | ||
154 | err2: | |
61f9e292 | 155 | nft_set_elem_destroy(set, he, true); |
05907f10 | 156 | atomic_dec(&set->nelems); |
22fe54d5 PM |
157 | err1: |
158 | return false; | |
159 | } | |
160 | ||
5fc6ced9 PNA |
161 | static int nft_rhash_insert(const struct net *net, const struct nft_set *set, |
162 | const struct nft_set_elem *elem, | |
163 | struct nft_set_ext **ext) | |
96518518 | 164 | { |
5fc6ced9 PNA |
165 | struct nft_rhash *priv = nft_set_priv(set); |
166 | struct nft_rhash_elem *he = elem->priv; | |
167 | struct nft_rhash_cmp_arg arg = { | |
42a55769 | 168 | .genmask = nft_genmask_next(net), |
bfd6e327 | 169 | .set = set, |
7d740264 | 170 | .key = elem->key.val.data, |
bfd6e327 | 171 | }; |
5fc6ced9 | 172 | struct nft_rhash_elem *prev; |
c016c7e4 PNA |
173 | |
174 | prev = rhashtable_lookup_get_insert_key(&priv->ht, &arg, &he->node, | |
5fc6ced9 | 175 | nft_rhash_params); |
c016c7e4 PNA |
176 | if (IS_ERR(prev)) |
177 | return PTR_ERR(prev); | |
178 | if (prev) { | |
179 | *ext = &prev->ext; | |
180 | return -EEXIST; | |
181 | } | |
182 | return 0; | |
96518518 PM |
183 | } |
184 | ||
5fc6ced9 PNA |
185 | static void nft_rhash_activate(const struct net *net, const struct nft_set *set, |
186 | const struct nft_set_elem *elem) | |
96518518 | 187 | { |
5fc6ced9 | 188 | struct nft_rhash_elem *he = elem->priv; |
ce6eb0d7 | 189 | |
42a55769 | 190 | nft_set_elem_change_active(net, set, &he->ext); |
9d098292 | 191 | nft_set_elem_clear_busy(&he->ext); |
20a69341 | 192 | } |
96518518 | 193 | |
5fc6ced9 PNA |
194 | static bool nft_rhash_flush(const struct net *net, |
195 | const struct nft_set *set, void *priv) | |
37df5301 | 196 | { |
5fc6ced9 | 197 | struct nft_rhash_elem *he = priv; |
37df5301 PNA |
198 | |
199 | if (!nft_set_elem_mark_busy(&he->ext) || | |
200 | !nft_is_active(net, &he->ext)) { | |
201 | nft_set_elem_change_active(net, set, &he->ext); | |
202 | return true; | |
203 | } | |
204 | return false; | |
205 | } | |
206 | ||
5fc6ced9 PNA |
207 | static void *nft_rhash_deactivate(const struct net *net, |
208 | const struct nft_set *set, | |
209 | const struct nft_set_elem *elem) | |
20a69341 | 210 | { |
5fc6ced9 PNA |
211 | struct nft_rhash *priv = nft_set_priv(set); |
212 | struct nft_rhash_elem *he; | |
213 | struct nft_rhash_cmp_arg arg = { | |
8eee54be | 214 | .genmask = nft_genmask_next(net), |
bfd6e327 | 215 | .set = set, |
7d740264 | 216 | .key = elem->key.val.data, |
bfd6e327 | 217 | }; |
fa377321 | 218 | |
9d098292 | 219 | rcu_read_lock(); |
a2d88182 | 220 | he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params); |
37df5301 | 221 | if (he != NULL && |
5fc6ced9 | 222 | !nft_rhash_flush(net, set, he)) |
37df5301 PNA |
223 | he = NULL; |
224 | ||
9d098292 | 225 | rcu_read_unlock(); |
8d24c0b4 | 226 | |
cc02e457 PM |
227 | return he; |
228 | } | |
8d24c0b4 | 229 | |
5fc6ced9 PNA |
230 | static void nft_rhash_remove(const struct net *net, |
231 | const struct nft_set *set, | |
232 | const struct nft_set_elem *elem) | |
cc02e457 | 233 | { |
5fc6ced9 PNA |
234 | struct nft_rhash *priv = nft_set_priv(set); |
235 | struct nft_rhash_elem *he = elem->priv; | |
cc02e457 | 236 | |
5fc6ced9 | 237 | rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params); |
96518518 PM |
238 | } |
239 | ||
d0a8d877 AJ |
240 | static bool nft_rhash_delete(const struct nft_set *set, |
241 | const u32 *key) | |
242 | { | |
243 | struct nft_rhash *priv = nft_set_priv(set); | |
244 | struct nft_rhash_cmp_arg arg = { | |
245 | .genmask = NFT_GENMASK_ANY, | |
246 | .set = set, | |
247 | .key = key, | |
248 | }; | |
249 | struct nft_rhash_elem *he; | |
250 | ||
251 | he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params); | |
252 | if (he == NULL) | |
253 | return false; | |
254 | ||
255 | return rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params) == 0; | |
256 | } | |
257 | ||
5fc6ced9 PNA |
258 | static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set, |
259 | struct nft_set_iter *iter) | |
96518518 | 260 | { |
5fc6ced9 PNA |
261 | struct nft_rhash *priv = nft_set_priv(set); |
262 | struct nft_rhash_elem *he; | |
9a776628 | 263 | struct rhashtable_iter hti; |
20a69341 | 264 | struct nft_set_elem elem; |
88d6ed15 | 265 | |
0de22baa | 266 | rhashtable_walk_enter(&priv->ht, &hti); |
97a6ec4a | 267 | rhashtable_walk_start(&hti); |
9a776628 HX |
268 | |
269 | while ((he = rhashtable_walk_next(&hti))) { | |
270 | if (IS_ERR(he)) { | |
0de22baa TY |
271 | if (PTR_ERR(he) != -EAGAIN) { |
272 | iter->err = PTR_ERR(he); | |
273 | break; | |
9a776628 | 274 | } |
d8bdff59 HX |
275 | |
276 | continue; | |
9a776628 HX |
277 | } |
278 | ||
279 | if (iter->count < iter->skip) | |
280 | goto cont; | |
9d098292 PM |
281 | if (nft_set_elem_expired(&he->ext)) |
282 | goto cont; | |
8588ac09 | 283 | if (!nft_set_elem_active(&he->ext, iter->genmask)) |
cc02e457 | 284 | goto cont; |
20a69341 | 285 | |
fe2811eb | 286 | elem.priv = he; |
9a776628 HX |
287 | |
288 | iter->err = iter->fn(ctx, set, iter, &elem); | |
289 | if (iter->err < 0) | |
0de22baa | 290 | break; |
20a69341 | 291 | |
20a69341 | 292 | cont: |
9a776628 | 293 | iter->count++; |
96518518 | 294 | } |
9a776628 HX |
295 | rhashtable_walk_stop(&hti); |
296 | rhashtable_walk_exit(&hti); | |
96518518 PM |
297 | } |
298 | ||
563125a7 PNA |
299 | static bool nft_rhash_expr_needs_gc_run(const struct nft_set *set, |
300 | struct nft_set_ext *ext) | |
301 | { | |
302 | struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); | |
303 | struct nft_expr *expr; | |
304 | u32 size; | |
305 | ||
306 | nft_setelem_expr_foreach(expr, elem_expr, size) { | |
307 | if (expr->ops->gc && | |
308 | expr->ops->gc(read_pnet(&set->net), expr)) | |
309 | return true; | |
310 | } | |
311 | ||
312 | return false; | |
313 | } | |
314 | ||
5fc6ced9 | 315 | static void nft_rhash_gc(struct work_struct *work) |
9d098292 | 316 | { |
3dd0673a | 317 | struct nft_set *set; |
5fc6ced9 PNA |
318 | struct nft_rhash_elem *he; |
319 | struct nft_rhash *priv; | |
9d098292 PM |
320 | struct nft_set_gc_batch *gcb = NULL; |
321 | struct rhashtable_iter hti; | |
9d098292 | 322 | |
5fc6ced9 | 323 | priv = container_of(work, struct nft_rhash, gc_work.work); |
9d098292 PM |
324 | set = nft_set_container_of(priv); |
325 | ||
0de22baa | 326 | rhashtable_walk_enter(&priv->ht, &hti); |
97a6ec4a | 327 | rhashtable_walk_start(&hti); |
9d098292 PM |
328 | |
329 | while ((he = rhashtable_walk_next(&hti))) { | |
330 | if (IS_ERR(he)) { | |
331 | if (PTR_ERR(he) != -EAGAIN) | |
0de22baa | 332 | break; |
9d098292 PM |
333 | continue; |
334 | } | |
335 | ||
563125a7 PNA |
336 | if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPRESSIONS) && |
337 | nft_rhash_expr_needs_gc_run(set, &he->ext)) | |
338 | goto needs_gc_run; | |
79b174ad | 339 | |
9d098292 PM |
340 | if (!nft_set_elem_expired(&he->ext)) |
341 | continue; | |
563125a7 | 342 | needs_gc_run: |
9d098292 PM |
343 | if (nft_set_elem_mark_busy(&he->ext)) |
344 | continue; | |
345 | ||
346 | gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); | |
347 | if (gcb == NULL) | |
0de22baa | 348 | break; |
5fc6ced9 | 349 | rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params); |
3dd0673a | 350 | atomic_dec(&set->nelems); |
9d098292 PM |
351 | nft_set_gc_batch_add(gcb, he); |
352 | } | |
9d098292 PM |
353 | rhashtable_walk_stop(&hti); |
354 | rhashtable_walk_exit(&hti); | |
355 | ||
aaa31047 PNA |
356 | he = nft_set_catchall_gc(set); |
357 | if (he) { | |
358 | gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); | |
359 | if (gcb) | |
360 | nft_set_gc_batch_add(gcb, he); | |
361 | } | |
9d098292 | 362 | nft_set_gc_batch_complete(gcb); |
9d098292 PM |
363 | queue_delayed_work(system_power_efficient_wq, &priv->gc_work, |
364 | nft_set_gc_interval(set)); | |
365 | } | |
366 | ||
4ef360dd TY |
367 | static u64 nft_rhash_privsize(const struct nlattr * const nla[], |
368 | const struct nft_set_desc *desc) | |
20a69341 | 369 | { |
5fc6ced9 | 370 | return sizeof(struct nft_rhash); |
cfe4a9dd TG |
371 | } |
372 | ||
79b174ad PNA |
373 | static void nft_rhash_gc_init(const struct nft_set *set) |
374 | { | |
375 | struct nft_rhash *priv = nft_set_priv(set); | |
376 | ||
377 | queue_delayed_work(system_power_efficient_wq, &priv->gc_work, | |
378 | nft_set_gc_interval(set)); | |
379 | } | |
380 | ||
5fc6ced9 PNA |
381 | static int nft_rhash_init(const struct nft_set *set, |
382 | const struct nft_set_desc *desc, | |
383 | const struct nlattr * const tb[]) | |
96518518 | 384 | { |
5fc6ced9 PNA |
385 | struct nft_rhash *priv = nft_set_priv(set); |
386 | struct rhashtable_params params = nft_rhash_params; | |
9d098292 | 387 | int err; |
fa377321 | 388 | |
5fc6ced9 | 389 | params.nelem_hint = desc->size ?: NFT_RHASH_ELEMENT_HINT; |
45d84751 | 390 | params.key_len = set->klen; |
96518518 | 391 | |
9d098292 PM |
392 | err = rhashtable_init(&priv->ht, ¶ms); |
393 | if (err < 0) | |
394 | return err; | |
395 | ||
5fc6ced9 | 396 | INIT_DEFERRABLE_WORK(&priv->gc_work, nft_rhash_gc); |
9d098292 | 397 | if (set->flags & NFT_SET_TIMEOUT) |
79b174ad PNA |
398 | nft_rhash_gc_init(set); |
399 | ||
9d098292 | 400 | return 0; |
96518518 PM |
401 | } |
402 | ||
5fc6ced9 | 403 | static void nft_rhash_elem_destroy(void *ptr, void *arg) |
96518518 | 404 | { |
68ad546a | 405 | nft_set_elem_destroy(arg, ptr, true); |
6b6f302c | 406 | } |
97defe1e | 407 | |
5fc6ced9 | 408 | static void nft_rhash_destroy(const struct nft_set *set) |
6b6f302c | 409 | { |
5fc6ced9 | 410 | struct nft_rhash *priv = nft_set_priv(set); |
745f5450 | 411 | |
9d098292 | 412 | cancel_delayed_work_sync(&priv->gc_work); |
9970a8e4 | 413 | rcu_barrier(); |
5fc6ced9 | 414 | rhashtable_free_and_destroy(&priv->ht, nft_rhash_elem_destroy, |
61edafbb | 415 | (void *)set); |
96518518 PM |
416 | } |
417 | ||
a54754ec ED |
418 | /* Number of buckets is stored in u32, so cap our result to 1U<<31 */ |
419 | #define NFT_MAX_BUCKETS (1U << 31) | |
420 | ||
2111515a PNA |
421 | static u32 nft_hash_buckets(u32 size) |
422 | { | |
a54754ec ED |
423 | u64 val = div_u64((u64)size * 4, 3); |
424 | ||
425 | if (val >= NFT_MAX_BUCKETS) | |
426 | return NFT_MAX_BUCKETS; | |
427 | ||
428 | return roundup_pow_of_two(val); | |
2111515a PNA |
429 | } |
430 | ||
5fc6ced9 PNA |
431 | static bool nft_rhash_estimate(const struct nft_set_desc *desc, u32 features, |
432 | struct nft_set_estimate *est) | |
c50b960c | 433 | { |
6c03ae21 PNA |
434 | est->size = ~0; |
435 | est->lookup = NFT_SET_CLASS_O_1; | |
436 | est->space = NFT_SET_CLASS_O_N; | |
437 | ||
438 | return true; | |
439 | } | |
440 | ||
441 | struct nft_hash { | |
442 | u32 seed; | |
443 | u32 buckets; | |
444 | struct hlist_head table[]; | |
445 | }; | |
446 | ||
447 | struct nft_hash_elem { | |
448 | struct hlist_node node; | |
449 | struct nft_set_ext ext; | |
450 | }; | |
451 | ||
f227925e FW |
452 | INDIRECT_CALLABLE_SCOPE |
453 | bool nft_hash_lookup(const struct net *net, const struct nft_set *set, | |
454 | const u32 *key, const struct nft_set_ext **ext) | |
6c03ae21 PNA |
455 | { |
456 | struct nft_hash *priv = nft_set_priv(set); | |
457 | u8 genmask = nft_genmask_cur(net); | |
458 | const struct nft_hash_elem *he; | |
459 | u32 hash; | |
460 | ||
461 | hash = jhash(key, set->klen, priv->seed); | |
462 | hash = reciprocal_scale(hash, priv->buckets); | |
463 | hlist_for_each_entry_rcu(he, &priv->table[hash], node) { | |
464 | if (!memcmp(nft_set_ext_key(&he->ext), key, set->klen) && | |
465 | nft_set_elem_active(&he->ext, genmask)) { | |
466 | *ext = &he->ext; | |
467 | return true; | |
468 | } | |
469 | } | |
470 | return false; | |
471 | } | |
472 | ||
ba0e4d99 PNA |
473 | static void *nft_hash_get(const struct net *net, const struct nft_set *set, |
474 | const struct nft_set_elem *elem, unsigned int flags) | |
475 | { | |
476 | struct nft_hash *priv = nft_set_priv(set); | |
477 | u8 genmask = nft_genmask_cur(net); | |
478 | struct nft_hash_elem *he; | |
479 | u32 hash; | |
480 | ||
481 | hash = jhash(elem->key.val.data, set->klen, priv->seed); | |
482 | hash = reciprocal_scale(hash, priv->buckets); | |
483 | hlist_for_each_entry_rcu(he, &priv->table[hash], node) { | |
484 | if (!memcmp(nft_set_ext_key(&he->ext), elem->key.val.data, set->klen) && | |
485 | nft_set_elem_active(&he->ext, genmask)) | |
486 | return he; | |
487 | } | |
488 | return ERR_PTR(-ENOENT); | |
489 | } | |
490 | ||
f227925e FW |
491 | INDIRECT_CALLABLE_SCOPE |
492 | bool nft_hash_lookup_fast(const struct net *net, | |
493 | const struct nft_set *set, | |
494 | const u32 *key, const struct nft_set_ext **ext) | |
446a8268 PNA |
495 | { |
496 | struct nft_hash *priv = nft_set_priv(set); | |
497 | u8 genmask = nft_genmask_cur(net); | |
498 | const struct nft_hash_elem *he; | |
499 | u32 hash, k1, k2; | |
500 | ||
123f89c8 | 501 | k1 = *key; |
446a8268 PNA |
502 | hash = jhash_1word(k1, priv->seed); |
503 | hash = reciprocal_scale(hash, priv->buckets); | |
504 | hlist_for_each_entry_rcu(he, &priv->table[hash], node) { | |
123f89c8 | 505 | k2 = *(u32 *)nft_set_ext_key(&he->ext)->data; |
446a8268 PNA |
506 | if (k1 == k2 && |
507 | nft_set_elem_active(&he->ext, genmask)) { | |
508 | *ext = &he->ext; | |
509 | return true; | |
510 | } | |
511 | } | |
512 | return false; | |
513 | } | |
514 | ||
3b02b0ad PNA |
515 | static u32 nft_jhash(const struct nft_set *set, const struct nft_hash *priv, |
516 | const struct nft_set_ext *ext) | |
517 | { | |
518 | const struct nft_data *key = nft_set_ext_key(ext); | |
519 | u32 hash, k1; | |
520 | ||
521 | if (set->klen == 4) { | |
522 | k1 = *(u32 *)key; | |
523 | hash = jhash_1word(k1, priv->seed); | |
524 | } else { | |
525 | hash = jhash(key, set->klen, priv->seed); | |
526 | } | |
527 | hash = reciprocal_scale(hash, priv->buckets); | |
528 | ||
529 | return hash; | |
530 | } | |
531 | ||
6c03ae21 PNA |
532 | static int nft_hash_insert(const struct net *net, const struct nft_set *set, |
533 | const struct nft_set_elem *elem, | |
534 | struct nft_set_ext **ext) | |
535 | { | |
536 | struct nft_hash_elem *this = elem->priv, *he; | |
537 | struct nft_hash *priv = nft_set_priv(set); | |
538 | u8 genmask = nft_genmask_next(net); | |
539 | u32 hash; | |
540 | ||
3b02b0ad | 541 | hash = nft_jhash(set, priv, &this->ext); |
6c03ae21 PNA |
542 | hlist_for_each_entry(he, &priv->table[hash], node) { |
543 | if (!memcmp(nft_set_ext_key(&this->ext), | |
544 | nft_set_ext_key(&he->ext), set->klen) && | |
545 | nft_set_elem_active(&he->ext, genmask)) { | |
546 | *ext = &he->ext; | |
547 | return -EEXIST; | |
548 | } | |
549 | } | |
550 | hlist_add_head_rcu(&this->node, &priv->table[hash]); | |
551 | return 0; | |
552 | } | |
553 | ||
554 | static void nft_hash_activate(const struct net *net, const struct nft_set *set, | |
555 | const struct nft_set_elem *elem) | |
556 | { | |
557 | struct nft_hash_elem *he = elem->priv; | |
558 | ||
559 | nft_set_elem_change_active(net, set, &he->ext); | |
560 | } | |
561 | ||
562 | static bool nft_hash_flush(const struct net *net, | |
563 | const struct nft_set *set, void *priv) | |
564 | { | |
565 | struct nft_hash_elem *he = priv; | |
566 | ||
567 | nft_set_elem_change_active(net, set, &he->ext); | |
568 | return true; | |
569 | } | |
570 | ||
571 | static void *nft_hash_deactivate(const struct net *net, | |
572 | const struct nft_set *set, | |
573 | const struct nft_set_elem *elem) | |
574 | { | |
575 | struct nft_hash *priv = nft_set_priv(set); | |
576 | struct nft_hash_elem *this = elem->priv, *he; | |
577 | u8 genmask = nft_genmask_next(net); | |
578 | u32 hash; | |
579 | ||
3b02b0ad | 580 | hash = nft_jhash(set, priv, &this->ext); |
6c03ae21 | 581 | hlist_for_each_entry(he, &priv->table[hash], node) { |
a01cbae5 | 582 | if (!memcmp(nft_set_ext_key(&he->ext), &elem->key.val, |
7f4dae2d | 583 | set->klen) && |
6c03ae21 PNA |
584 | nft_set_elem_active(&he->ext, genmask)) { |
585 | nft_set_elem_change_active(net, set, &he->ext); | |
586 | return he; | |
587 | } | |
588 | } | |
589 | return NULL; | |
590 | } | |
591 | ||
592 | static void nft_hash_remove(const struct net *net, | |
593 | const struct nft_set *set, | |
594 | const struct nft_set_elem *elem) | |
595 | { | |
596 | struct nft_hash_elem *he = elem->priv; | |
597 | ||
598 | hlist_del_rcu(&he->node); | |
599 | } | |
600 | ||
601 | static void nft_hash_walk(const struct nft_ctx *ctx, struct nft_set *set, | |
602 | struct nft_set_iter *iter) | |
603 | { | |
604 | struct nft_hash *priv = nft_set_priv(set); | |
605 | struct nft_hash_elem *he; | |
606 | struct nft_set_elem elem; | |
607 | int i; | |
608 | ||
609 | for (i = 0; i < priv->buckets; i++) { | |
610 | hlist_for_each_entry_rcu(he, &priv->table[i], node) { | |
611 | if (iter->count < iter->skip) | |
612 | goto cont; | |
613 | if (!nft_set_elem_active(&he->ext, iter->genmask)) | |
614 | goto cont; | |
615 | ||
616 | elem.priv = he; | |
617 | ||
618 | iter->err = iter->fn(ctx, set, iter, &elem); | |
619 | if (iter->err < 0) | |
620 | return; | |
621 | cont: | |
622 | iter->count++; | |
623 | } | |
624 | } | |
625 | } | |
626 | ||
4ef360dd TY |
627 | static u64 nft_hash_privsize(const struct nlattr * const nla[], |
628 | const struct nft_set_desc *desc) | |
6c03ae21 PNA |
629 | { |
630 | return sizeof(struct nft_hash) + | |
6c8774a9 | 631 | (u64)nft_hash_buckets(desc->size) * sizeof(struct hlist_head); |
6c03ae21 PNA |
632 | } |
633 | ||
634 | static int nft_hash_init(const struct nft_set *set, | |
635 | const struct nft_set_desc *desc, | |
636 | const struct nlattr * const tb[]) | |
637 | { | |
638 | struct nft_hash *priv = nft_set_priv(set); | |
639 | ||
640 | priv->buckets = nft_hash_buckets(desc->size); | |
641 | get_random_bytes(&priv->seed, sizeof(priv->seed)); | |
642 | ||
643 | return 0; | |
644 | } | |
645 | ||
646 | static void nft_hash_destroy(const struct nft_set *set) | |
647 | { | |
648 | struct nft_hash *priv = nft_set_priv(set); | |
649 | struct nft_hash_elem *he; | |
650 | struct hlist_node *next; | |
651 | int i; | |
652 | ||
653 | for (i = 0; i < priv->buckets; i++) { | |
654 | hlist_for_each_entry_safe(he, next, &priv->table[i], node) { | |
655 | hlist_del_rcu(&he->node); | |
656 | nft_set_elem_destroy(set, he, true); | |
657 | } | |
658 | } | |
659 | } | |
c50b960c | 660 | |
6c03ae21 PNA |
661 | static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, |
662 | struct nft_set_estimate *est) | |
663 | { | |
71cc0873 PS |
664 | if (!desc->size) |
665 | return false; | |
666 | ||
667 | if (desc->klen == 4) | |
668 | return false; | |
669 | ||
6c03ae21 | 670 | est->size = sizeof(struct nft_hash) + |
6c8774a9 ED |
671 | (u64)nft_hash_buckets(desc->size) * sizeof(struct hlist_head) + |
672 | (u64)desc->size * sizeof(struct nft_hash_elem); | |
55af753c | 673 | est->lookup = NFT_SET_CLASS_O_1; |
0b5a7874 | 674 | est->space = NFT_SET_CLASS_O_N; |
c50b960c PM |
675 | |
676 | return true; | |
677 | } | |
678 | ||
71cc0873 | 679 | static bool nft_hash_fast_estimate(const struct nft_set_desc *desc, u32 features, |
fbf19ddf | 680 | struct nft_set_estimate *est) |
71cc0873 PS |
681 | { |
682 | if (!desc->size) | |
683 | return false; | |
2b664957 | 684 | |
71cc0873 PS |
685 | if (desc->klen != 4) |
686 | return false; | |
6c03ae21 | 687 | |
71cc0873 | 688 | est->size = sizeof(struct nft_hash) + |
6c8774a9 ED |
689 | (u64)nft_hash_buckets(desc->size) * sizeof(struct hlist_head) + |
690 | (u64)desc->size * sizeof(struct nft_hash_elem); | |
71cc0873 PS |
691 | est->lookup = NFT_SET_CLASS_O_1; |
692 | est->space = NFT_SET_CLASS_O_N; | |
6c03ae21 | 693 | |
71cc0873 | 694 | return true; |
6c03ae21 PNA |
695 | } |
696 | ||
24d19826 | 697 | const struct nft_set_type nft_set_rhash_type = { |
71cc0873 PS |
698 | .features = NFT_SET_MAP | NFT_SET_OBJECT | |
699 | NFT_SET_TIMEOUT | NFT_SET_EVAL, | |
700 | .ops = { | |
701 | .privsize = nft_rhash_privsize, | |
702 | .elemsize = offsetof(struct nft_rhash_elem, ext), | |
703 | .estimate = nft_rhash_estimate, | |
704 | .init = nft_rhash_init, | |
79b174ad | 705 | .gc_init = nft_rhash_gc_init, |
71cc0873 PS |
706 | .destroy = nft_rhash_destroy, |
707 | .insert = nft_rhash_insert, | |
708 | .activate = nft_rhash_activate, | |
709 | .deactivate = nft_rhash_deactivate, | |
710 | .flush = nft_rhash_flush, | |
711 | .remove = nft_rhash_remove, | |
712 | .lookup = nft_rhash_lookup, | |
713 | .update = nft_rhash_update, | |
d0a8d877 | 714 | .delete = nft_rhash_delete, |
71cc0873 PS |
715 | .walk = nft_rhash_walk, |
716 | .get = nft_rhash_get, | |
717 | }, | |
718 | }; | |
719 | ||
24d19826 | 720 | const struct nft_set_type nft_set_hash_type = { |
71cc0873 PS |
721 | .features = NFT_SET_MAP | NFT_SET_OBJECT, |
722 | .ops = { | |
723 | .privsize = nft_hash_privsize, | |
724 | .elemsize = offsetof(struct nft_hash_elem, ext), | |
725 | .estimate = nft_hash_estimate, | |
726 | .init = nft_hash_init, | |
727 | .destroy = nft_hash_destroy, | |
728 | .insert = nft_hash_insert, | |
729 | .activate = nft_hash_activate, | |
730 | .deactivate = nft_hash_deactivate, | |
731 | .flush = nft_hash_flush, | |
732 | .remove = nft_hash_remove, | |
733 | .lookup = nft_hash_lookup, | |
734 | .walk = nft_hash_walk, | |
735 | .get = nft_hash_get, | |
736 | }, | |
737 | }; | |
738 | ||
24d19826 | 739 | const struct nft_set_type nft_set_hash_fast_type = { |
71cc0873 PS |
740 | .features = NFT_SET_MAP | NFT_SET_OBJECT, |
741 | .ops = { | |
742 | .privsize = nft_hash_privsize, | |
743 | .elemsize = offsetof(struct nft_hash_elem, ext), | |
744 | .estimate = nft_hash_fast_estimate, | |
745 | .init = nft_hash_init, | |
746 | .destroy = nft_hash_destroy, | |
747 | .insert = nft_hash_insert, | |
748 | .activate = nft_hash_activate, | |
749 | .deactivate = nft_hash_deactivate, | |
750 | .flush = nft_hash_flush, | |
751 | .remove = nft_hash_remove, | |
752 | .lookup = nft_hash_lookup_fast, | |
753 | .walk = nft_hash_walk, | |
754 | .get = nft_hash_get, | |
755 | }, | |
96518518 | 756 | }; |