.. | .. |
---|
17 | 17 | #include <linux/netfilter.h> |
---|
18 | 18 | #include <linux/netfilter/nf_tables.h> |
---|
19 | 19 | #include <net/netfilter/nf_tables_core.h> |
---|
| 20 | +#include <net/netns/generic.h> |
---|
| 21 | + |
---|
| 22 | +extern unsigned int nf_tables_net_id; |
---|
20 | 23 | |
---|
21 | 24 | /* We target a hash table size of 4, element hint is 75% of final size */ |
---|
22 | 25 | #define NFT_RHASH_ELEMENT_HINT 3 |
---|
.. | .. |
---|
58 | 61 | const struct nft_rhash_elem *he = ptr; |
---|
59 | 62 | |
---|
60 | 63 | if (memcmp(nft_set_ext_key(&he->ext), x->key, x->set->klen)) |
---|
| 64 | + return 1; |
---|
| 65 | + if (nft_set_elem_is_dead(&he->ext)) |
---|
61 | 66 | return 1; |
---|
62 | 67 | if (nft_set_elem_expired(&he->ext)) |
---|
63 | 68 | return 1; |
---|
.. | .. |
---|
187 | 192 | struct nft_rhash_elem *he = elem->priv; |
---|
188 | 193 | |
---|
189 | 194 | nft_set_elem_change_active(net, set, &he->ext); |
---|
190 | | - nft_set_elem_clear_busy(&he->ext); |
---|
191 | 195 | } |
---|
192 | 196 | |
---|
193 | 197 | static bool nft_rhash_flush(const struct net *net, |
---|
.. | .. |
---|
195 | 199 | { |
---|
196 | 200 | struct nft_rhash_elem *he = priv; |
---|
197 | 201 | |
---|
198 | | - if (!nft_set_elem_mark_busy(&he->ext) || |
---|
199 | | - !nft_is_active(net, &he->ext)) { |
---|
200 | | - nft_set_elem_change_active(net, set, &he->ext); |
---|
201 | | - return true; |
---|
202 | | - } |
---|
203 | | - return false; |
---|
| 202 | + nft_set_elem_change_active(net, set, &he->ext); |
---|
| 203 | + |
---|
| 204 | + return true; |
---|
204 | 205 | } |
---|
205 | 206 | |
---|
206 | 207 | static void *nft_rhash_deactivate(const struct net *net, |
---|
.. | .. |
---|
217 | 218 | |
---|
218 | 219 | rcu_read_lock(); |
---|
219 | 220 | he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params); |
---|
220 | | - if (he != NULL && |
---|
221 | | - !nft_rhash_flush(net, set, he)) |
---|
222 | | - he = NULL; |
---|
| 221 | + if (he) |
---|
| 222 | + nft_set_elem_change_active(net, set, &he->ext); |
---|
223 | 223 | |
---|
224 | 224 | rcu_read_unlock(); |
---|
225 | 225 | |
---|
.. | .. |
---|
251 | 251 | if (he == NULL) |
---|
252 | 252 | return false; |
---|
253 | 253 | |
---|
254 | | - return rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params) == 0; |
---|
| 254 | + nft_set_elem_dead(&he->ext); |
---|
| 255 | + |
---|
| 256 | + return true; |
---|
255 | 257 | } |
---|
256 | 258 | |
---|
257 | 259 | static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set, |
---|
.. | .. |
---|
277 | 279 | |
---|
278 | 280 | if (iter->count < iter->skip) |
---|
279 | 281 | goto cont; |
---|
280 | | - if (nft_set_elem_expired(&he->ext)) |
---|
281 | | - goto cont; |
---|
282 | 282 | if (!nft_set_elem_active(&he->ext, iter->genmask)) |
---|
283 | 283 | goto cont; |
---|
284 | 284 | |
---|
.. | .. |
---|
297 | 297 | |
---|
298 | 298 | static void nft_rhash_gc(struct work_struct *work) |
---|
299 | 299 | { |
---|
| 300 | + struct nftables_pernet *nft_net; |
---|
300 | 301 | struct nft_set *set; |
---|
301 | 302 | struct nft_rhash_elem *he; |
---|
302 | 303 | struct nft_rhash *priv; |
---|
303 | | - struct nft_set_gc_batch *gcb = NULL; |
---|
304 | 304 | struct rhashtable_iter hti; |
---|
| 305 | + struct nft_trans_gc *gc; |
---|
| 306 | + struct net *net; |
---|
| 307 | + u32 gc_seq; |
---|
305 | 308 | |
---|
306 | 309 | priv = container_of(work, struct nft_rhash, gc_work.work); |
---|
307 | 310 | set = nft_set_container_of(priv); |
---|
| 311 | + net = read_pnet(&set->net); |
---|
| 312 | + nft_net = net_generic(net, nf_tables_net_id); |
---|
| 313 | + gc_seq = READ_ONCE(nft_net->gc_seq); |
---|
| 314 | + |
---|
| 315 | + if (nft_set_gc_is_pending(set)) |
---|
| 316 | + goto done; |
---|
| 317 | + |
---|
| 318 | + gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL); |
---|
| 319 | + if (!gc) |
---|
| 320 | + goto done; |
---|
308 | 321 | |
---|
309 | 322 | rhashtable_walk_enter(&priv->ht, &hti); |
---|
310 | 323 | rhashtable_walk_start(&hti); |
---|
311 | 324 | |
---|
312 | 325 | while ((he = rhashtable_walk_next(&hti))) { |
---|
313 | 326 | if (IS_ERR(he)) { |
---|
314 | | - if (PTR_ERR(he) != -EAGAIN) |
---|
315 | | - break; |
---|
316 | | - continue; |
---|
| 327 | + nft_trans_gc_destroy(gc); |
---|
| 328 | + gc = NULL; |
---|
| 329 | + goto try_later; |
---|
317 | 330 | } |
---|
| 331 | + |
---|
| 332 | + /* Ruleset has been updated, try later. */ |
---|
| 333 | + if (READ_ONCE(nft_net->gc_seq) != gc_seq) { |
---|
| 334 | + nft_trans_gc_destroy(gc); |
---|
| 335 | + gc = NULL; |
---|
| 336 | + goto try_later; |
---|
| 337 | + } |
---|
| 338 | + |
---|
| 339 | + if (nft_set_elem_is_dead(&he->ext)) |
---|
| 340 | + goto dead_elem; |
---|
318 | 341 | |
---|
319 | 342 | if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPR)) { |
---|
320 | 343 | struct nft_expr *expr = nft_set_ext_expr(&he->ext); |
---|
321 | 344 | |
---|
322 | 345 | if (expr->ops->gc && |
---|
323 | 346 | expr->ops->gc(read_pnet(&set->net), expr)) |
---|
324 | | - goto gc; |
---|
| 347 | + goto needs_gc_run; |
---|
325 | 348 | } |
---|
| 349 | + |
---|
326 | 350 | if (!nft_set_elem_expired(&he->ext)) |
---|
327 | 351 | continue; |
---|
328 | | -gc: |
---|
329 | | - if (nft_set_elem_mark_busy(&he->ext)) |
---|
330 | | - continue; |
---|
| 352 | +needs_gc_run: |
---|
| 353 | + nft_set_elem_dead(&he->ext); |
---|
| 354 | +dead_elem: |
---|
| 355 | + gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); |
---|
| 356 | + if (!gc) |
---|
| 357 | + goto try_later; |
---|
331 | 358 | |
---|
332 | | - gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); |
---|
333 | | - if (gcb == NULL) |
---|
334 | | - break; |
---|
335 | | - rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params); |
---|
336 | | - atomic_dec(&set->nelems); |
---|
337 | | - nft_set_gc_batch_add(gcb, he); |
---|
| 359 | + nft_trans_gc_elem_add(gc, he); |
---|
338 | 360 | } |
---|
| 361 | + |
---|
| 362 | +try_later: |
---|
339 | 363 | rhashtable_walk_stop(&hti); |
---|
340 | 364 | rhashtable_walk_exit(&hti); |
---|
341 | 365 | |
---|
342 | | - nft_set_gc_batch_complete(gcb); |
---|
| 366 | + if (gc) |
---|
| 367 | + nft_trans_gc_queue_async_done(gc); |
---|
| 368 | +done: |
---|
343 | 369 | queue_delayed_work(system_power_efficient_wq, &priv->gc_work, |
---|
344 | 370 | nft_set_gc_interval(set)); |
---|
345 | 371 | } |
---|
.. | .. |
---|
374 | 400 | return err; |
---|
375 | 401 | |
---|
376 | 402 | INIT_DEFERRABLE_WORK(&priv->gc_work, nft_rhash_gc); |
---|
377 | | - if (set->flags & NFT_SET_TIMEOUT) |
---|
| 403 | + if (set->flags & (NFT_SET_TIMEOUT | NFT_SET_EVAL)) |
---|
378 | 404 | nft_rhash_gc_init(set); |
---|
379 | 405 | |
---|
380 | 406 | return 0; |
---|
381 | 407 | } |
---|
382 | 408 | |
---|
| 409 | +struct nft_rhash_ctx { |
---|
| 410 | + const struct nft_ctx ctx; |
---|
| 411 | + const struct nft_set *set; |
---|
| 412 | +}; |
---|
| 413 | + |
---|
383 | 414 | static void nft_rhash_elem_destroy(void *ptr, void *arg) |
---|
384 | 415 | { |
---|
385 | | - nft_set_elem_destroy(arg, ptr, true); |
---|
| 416 | + struct nft_rhash_ctx *rhash_ctx = arg; |
---|
| 417 | + |
---|
| 418 | + nf_tables_set_elem_destroy(&rhash_ctx->ctx, rhash_ctx->set, ptr); |
---|
386 | 419 | } |
---|
387 | 420 | |
---|
388 | | -static void nft_rhash_destroy(const struct nft_set *set) |
---|
| 421 | +static void nft_rhash_destroy(const struct nft_ctx *ctx, |
---|
| 422 | + const struct nft_set *set) |
---|
389 | 423 | { |
---|
390 | 424 | struct nft_rhash *priv = nft_set_priv(set); |
---|
| 425 | + struct nft_rhash_ctx rhash_ctx = { |
---|
| 426 | + .ctx = *ctx, |
---|
| 427 | + .set = set, |
---|
| 428 | + }; |
---|
391 | 429 | |
---|
392 | 430 | cancel_delayed_work_sync(&priv->gc_work); |
---|
393 | | - rcu_barrier(); |
---|
394 | 431 | rhashtable_free_and_destroy(&priv->ht, nft_rhash_elem_destroy, |
---|
395 | | - (void *)set); |
---|
| 432 | + (void *)&rhash_ctx); |
---|
396 | 433 | } |
---|
397 | 434 | |
---|
398 | 435 | /* Number of buckets is stored in u32, so cap our result to 1U<<31 */ |
---|
.. | .. |
---|
621 | 658 | return 0; |
---|
622 | 659 | } |
---|
623 | 660 | |
---|
624 | | -static void nft_hash_destroy(const struct nft_set *set) |
---|
| 661 | +static void nft_hash_destroy(const struct nft_ctx *ctx, |
---|
| 662 | + const struct nft_set *set) |
---|
625 | 663 | { |
---|
626 | 664 | struct nft_hash *priv = nft_set_priv(set); |
---|
627 | 665 | struct nft_hash_elem *he; |
---|
.. | .. |
---|
631 | 669 | for (i = 0; i < priv->buckets; i++) { |
---|
632 | 670 | hlist_for_each_entry_safe(he, next, &priv->table[i], node) { |
---|
633 | 671 | hlist_del_rcu(&he->node); |
---|
634 | | - nft_set_elem_destroy(set, he, true); |
---|
| 672 | + nf_tables_set_elem_destroy(ctx, set, he); |
---|
635 | 673 | } |
---|
636 | 674 | } |
---|
637 | 675 | } |
---|