Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nftables: add catch-all set element support

This patch extends the set infrastructure to add a special catch-all set
element. If the lookup fails to find an element (or range) in the set,
then the catch-all element is selected. Users can specify a mapping,
expression(s) and timeout to be attached to the catch-all element.

This patch adds a catchall list to the set, this list might contain more
than one single catch-all element (e.g. in case that the catch-all
element is removed and a new one is added in the same transaction).
However, most of the time, there will be either one element or no
elements at all in this list.

The catch-all element is identified via NFT_SET_ELEM_CATCHALL flag and
such special element has no NFTA_SET_ELEM_KEY attribute. There is a new
nft_set_elem_catchall object that stores a reference to the dummy
catch-all element (catchall->elem) whose layout is the same of the set
element type to reuse the existing set element codebase.

The set size does not apply to the catch-all element, users can define a
catch-all element even if the set is full.

The check for valid set element flags hava been updates to report
EOPNOTSUPP in case userspace requests flags that are not supported when
using new userspace nftables and old kernel.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+465 -63
+5
include/net/netfilter/nf_tables.h
··· 497 497 u8 dlen; 498 498 u8 num_exprs; 499 499 struct nft_expr *exprs[NFT_SET_EXPR_MAX]; 500 + struct list_head catchall_list; 500 501 unsigned char data[] 501 502 __attribute__((aligned(__alignof__(u64)))); 502 503 }; ··· 522 521 const struct nlattr *nla_set_name, 523 522 const struct nlattr *nla_set_id, 524 523 u8 genmask); 524 + 525 + struct nft_set_ext *nft_set_catchall_lookup(const struct net *net, 526 + const struct nft_set *set); 527 + void *nft_set_catchall_gc(const struct nft_set *set); 525 528 526 529 static inline unsigned long nft_set_gc_interval(const struct nft_set *set) 527 530 {
+2
include/uapi/linux/netfilter/nf_tables.h
··· 398 398 * enum nft_set_elem_flags - nf_tables set element flags 399 399 * 400 400 * @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval 401 + * @NFT_SET_ELEM_CATCHALL: special catch-all element 401 402 */ 402 403 enum nft_set_elem_flags { 403 404 NFT_SET_ELEM_INTERVAL_END = 0x1, 405 + NFT_SET_ELEM_CATCHALL = 0x2, 404 406 }; 405 407 406 408 /**
+426 -54
net/netfilter/nf_tables_api.c
··· 4389 4389 } 4390 4390 4391 4391 INIT_LIST_HEAD(&set->bindings); 4392 + INIT_LIST_HEAD(&set->catchall_list); 4392 4393 set->table = table; 4393 4394 write_pnet(&set->net, net); 4394 4395 set->ops = ops; ··· 4435 4434 return err; 4436 4435 } 4437 4436 4437 + struct nft_set_elem_catchall { 4438 + struct list_head list; 4439 + struct rcu_head rcu; 4440 + void *elem; 4441 + }; 4442 + 4443 + static void nft_set_catchall_destroy(const struct nft_ctx *ctx, 4444 + struct nft_set *set) 4445 + { 4446 + struct nft_set_elem_catchall *catchall; 4447 + 4448 + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { 4449 + list_del_rcu(&catchall->list); 4450 + nft_set_elem_destroy(set, catchall->elem, true); 4451 + kfree_rcu(catchall); 4452 + } 4453 + } 4454 + 4438 4455 static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) 4439 4456 { 4440 4457 int i; ··· 4464 4445 nft_expr_destroy(ctx, set->exprs[i]); 4465 4446 4466 4447 set->ops->destroy(set); 4448 + nft_set_catchall_destroy(ctx, set); 4467 4449 kfree(set->name); 4468 4450 kvfree(set); 4469 4451 } ··· 4541 4521 return nft_setelem_data_validate(ctx, set, elem); 4542 4522 } 4543 4523 4524 + static int nft_set_catchall_bind_check(const struct nft_ctx *ctx, 4525 + struct nft_set *set) 4526 + { 4527 + u8 genmask = nft_genmask_next(ctx->net); 4528 + struct nft_set_elem_catchall *catchall; 4529 + struct nft_set_elem elem; 4530 + struct nft_set_ext *ext; 4531 + int ret = 0; 4532 + 4533 + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { 4534 + ext = nft_set_elem_ext(set, catchall->elem); 4535 + if (!nft_set_elem_active(ext, genmask)) 4536 + continue; 4537 + 4538 + elem.priv = catchall->elem; 4539 + ret = nft_setelem_data_validate(ctx, set, &elem); 4540 + if (ret < 0) 4541 + break; 4542 + } 4543 + 4544 + return ret; 4545 + } 4546 + 4544 4547 int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set, 4545 4548 struct nft_set_binding *binding) 4546 4549 { ··· 4593 4550 iter.fn = nf_tables_bind_check_setelem; 4594 4551 4595 4552 set->ops->walk(ctx, set, &iter); 4553 + if (!iter.err) 4554 + iter.err = nft_set_catchall_bind_check(ctx, set); 4555 + 4596 4556 if (iter.err < 0) 4597 4557 return iter.err; 4598 4558 } ··· 4782 4736 if (nest == NULL) 4783 4737 goto nla_put_failure; 4784 4738 4785 - if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext), 4739 + if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) && 4740 + nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext), 4786 4741 NFT_DATA_VALUE, set->klen) < 0) 4787 4742 goto nla_put_failure; 4788 4743 ··· 4872 4825 struct nft_ctx ctx; 4873 4826 }; 4874 4827 4828 + static int nft_set_catchall_dump(struct net *net, struct sk_buff *skb, 4829 + const struct nft_set *set) 4830 + { 4831 + struct nft_set_elem_catchall *catchall; 4832 + u8 genmask = nft_genmask_cur(net); 4833 + struct nft_set_elem elem; 4834 + struct nft_set_ext *ext; 4835 + int ret = 0; 4836 + 4837 + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { 4838 + ext = nft_set_elem_ext(set, catchall->elem); 4839 + if (!nft_set_elem_active(ext, genmask) || 4840 + nft_set_elem_expired(ext)) 4841 + continue; 4842 + 4843 + elem.priv = catchall->elem; 4844 + ret = nf_tables_fill_setelem(skb, set, &elem); 4845 + break; 4846 + } 4847 + 4848 + return ret; 4849 + } 4850 + 4875 4851 static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) 4876 4852 { 4877 4853 struct nft_set_dump_ctx *dump_ctx = cb->data; ··· 4959 4889 args.iter.err = 0; 4960 4890 args.iter.fn = nf_tables_dump_setelem; 4961 4891 set->ops->walk(&dump_ctx->ctx, set, &args.iter); 4892 + 4893 + if (!args.iter.err && args.iter.count == cb->args[0]) 4894 + args.iter.err = nft_set_catchall_dump(net, skb, set); 4962 4895 rcu_read_unlock(); 4963 4896 4964 4897 nla_nest_end(skb, nest); ··· 5041 4968 return 0; 5042 4969 5043 4970 *flags = ntohl(nla_get_be32(attr)); 5044 - if (*flags & ~NFT_SET_ELEM_INTERVAL_END) 5045 - return -EINVAL; 4971 + if (*flags & ~(NFT_SET_ELEM_INTERVAL_END | NFT_SET_ELEM_CATCHALL)) 4972 + return -EOPNOTSUPP; 5046 4973 if (!(set->flags & NFT_SET_INTERVAL) && 5047 4974 *flags & NFT_SET_ELEM_INTERVAL_END) 5048 4975 return -EINVAL; ··· 5087 5014 return 0; 5088 5015 } 5089 5016 5017 + static void *nft_setelem_catchall_get(const struct net *net, 5018 + const struct nft_set *set) 5019 + { 5020 + struct nft_set_elem_catchall *catchall; 5021 + u8 genmask = nft_genmask_cur(net); 5022 + struct nft_set_ext *ext; 5023 + void *priv = NULL; 5024 + 5025 + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { 5026 + ext = nft_set_elem_ext(set, catchall->elem); 5027 + if (!nft_set_elem_active(ext, genmask) || 5028 + nft_set_elem_expired(ext)) 5029 + continue; 5030 + 5031 + priv = catchall->elem; 5032 + break; 5033 + } 5034 + 5035 + return priv; 5036 + } 5037 + 5038 + static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set, 5039 + struct nft_set_elem *elem, u32 flags) 5040 + { 5041 + void *priv; 5042 + 5043 + if (!(flags & NFT_SET_ELEM_CATCHALL)) { 5044 + priv = set->ops->get(ctx->net, set, elem, flags); 5045 + if (IS_ERR(priv)) 5046 + return PTR_ERR(priv); 5047 + } else { 5048 + priv = nft_setelem_catchall_get(ctx->net, set); 5049 + if (!priv) 5050 + return -ENOENT; 5051 + } 5052 + elem->priv = priv; 5053 + 5054 + return 0; 5055 + } 5056 + 5090 5057 static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set, 5091 5058 const struct nlattr *attr) 5092 5059 { ··· 5134 5021 struct nft_set_elem elem; 5135 5022 struct sk_buff *skb; 5136 5023 uint32_t flags = 0; 5137 - void *priv; 5138 5024 int err; 5139 5025 5140 5026 err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr, ··· 5141 5029 if (err < 0) 5142 5030 return err; 5143 5031 5144 - if (!nla[NFTA_SET_ELEM_KEY]) 5145 - return -EINVAL; 5146 - 5147 5032 err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags); 5148 5033 if (err < 0) 5149 5034 return err; 5150 5035 5151 - err = nft_setelem_parse_key(ctx, set, &elem.key.val, 5152 - nla[NFTA_SET_ELEM_KEY]); 5153 - if (err < 0) 5154 - return err; 5036 + if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL)) 5037 + return -EINVAL; 5038 + 5039 + if (nla[NFTA_SET_ELEM_KEY]) { 5040 + err = nft_setelem_parse_key(ctx, set, &elem.key.val, 5041 + nla[NFTA_SET_ELEM_KEY]); 5042 + if (err < 0) 5043 + return err; 5044 + } 5155 5045 5156 5046 if (nla[NFTA_SET_ELEM_KEY_END]) { 5157 5047 err = nft_setelem_parse_key(ctx, set, &elem.key_end.val, ··· 5162 5048 return err; 5163 5049 } 5164 5050 5165 - priv = set->ops->get(ctx->net, set, &elem, flags); 5166 - if (IS_ERR(priv)) 5167 - return PTR_ERR(priv); 5168 - 5169 - elem.priv = priv; 5051 + err = nft_setelem_get(ctx, set, &elem, flags); 5052 + if (err < 0) 5053 + return err; 5170 5054 5171 5055 err = -ENOMEM; 5172 5056 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); ··· 5324 5212 ext = nft_set_elem_ext(set, elem); 5325 5213 nft_set_ext_init(ext, tmpl); 5326 5214 5327 - memcpy(nft_set_ext_key(ext), key, set->klen); 5215 + if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY)) 5216 + memcpy(nft_set_ext_key(ext), key, set->klen); 5328 5217 if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) 5329 5218 memcpy(nft_set_ext_key_end(ext), key_end, set->klen); 5330 5219 if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) ··· 5456 5343 return -ENOMEM; 5457 5344 } 5458 5345 5346 + struct nft_set_ext *nft_set_catchall_lookup(const struct net *net, 5347 + const struct nft_set *set) 5348 + { 5349 + struct nft_set_elem_catchall *catchall; 5350 + u8 genmask = nft_genmask_cur(net); 5351 + struct nft_set_ext *ext; 5352 + 5353 + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { 5354 + ext = nft_set_elem_ext(set, catchall->elem); 5355 + if (nft_set_elem_active(ext, genmask) && 5356 + !nft_set_elem_expired(ext)) 5357 + return ext; 5358 + } 5359 + 5360 + return NULL; 5361 + } 5362 + EXPORT_SYMBOL_GPL(nft_set_catchall_lookup); 5363 + 5364 + void *nft_set_catchall_gc(const struct nft_set *set) 5365 + { 5366 + struct nft_set_elem_catchall *catchall, *next; 5367 + struct nft_set_ext *ext; 5368 + void *elem = NULL; 5369 + 5370 + list_for_each_entry_safe(catchall, next, &set->catchall_list, list) { 5371 + ext = nft_set_elem_ext(set, catchall->elem); 5372 + 5373 + if (!nft_set_elem_expired(ext) || 5374 + nft_set_elem_mark_busy(ext)) 5375 + continue; 5376 + 5377 + elem = catchall->elem; 5378 + list_del_rcu(&catchall->list); 5379 + kfree_rcu(catchall, rcu); 5380 + break; 5381 + } 5382 + 5383 + return elem; 5384 + } 5385 + EXPORT_SYMBOL_GPL(nft_set_catchall_gc); 5386 + 5387 + static int nft_setelem_catchall_insert(const struct net *net, 5388 + struct nft_set *set, 5389 + const struct nft_set_elem *elem, 5390 + struct nft_set_ext **pext) 5391 + { 5392 + struct nft_set_elem_catchall *catchall; 5393 + u8 genmask = nft_genmask_next(net); 5394 + struct nft_set_ext *ext; 5395 + 5396 + list_for_each_entry(catchall, &set->catchall_list, list) { 5397 + ext = nft_set_elem_ext(set, catchall->elem); 5398 + if (nft_set_elem_active(ext, genmask)) { 5399 + *pext = ext; 5400 + return -EEXIST; 5401 + } 5402 + } 5403 + 5404 + catchall = kmalloc(sizeof(*catchall), GFP_KERNEL); 5405 + if (!catchall) 5406 + return -ENOMEM; 5407 + 5408 + catchall->elem = elem->priv; 5409 + list_add_tail_rcu(&catchall->list, &set->catchall_list); 5410 + 5411 + return 0; 5412 + } 5413 + 5414 + static int nft_setelem_insert(const struct net *net, 5415 + struct nft_set *set, 5416 + const struct nft_set_elem *elem, 5417 + struct nft_set_ext **ext, unsigned int flags) 5418 + { 5419 + int ret; 5420 + 5421 + if (flags & NFT_SET_ELEM_CATCHALL) 5422 + ret = nft_setelem_catchall_insert(net, set, elem, ext); 5423 + else 5424 + ret = set->ops->insert(net, set, elem, ext); 5425 + 5426 + return ret; 5427 + } 5428 + 5429 + static bool nft_setelem_is_catchall(const struct nft_set *set, 5430 + const struct nft_set_elem *elem) 5431 + { 5432 + struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); 5433 + 5434 + if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && 5435 + *nft_set_ext_flags(ext) & NFT_SET_ELEM_CATCHALL) 5436 + return true; 5437 + 5438 + return false; 5439 + } 5440 + 5441 + static void nft_setelem_activate(struct net *net, struct nft_set *set, 5442 + struct nft_set_elem *elem) 5443 + { 5444 + struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); 5445 + 5446 + if (nft_setelem_is_catchall(set, elem)) { 5447 + nft_set_elem_change_active(net, set, ext); 5448 + nft_set_elem_clear_busy(ext); 5449 + } else { 5450 + set->ops->activate(net, set, elem); 5451 + } 5452 + } 5453 + 5454 + static int nft_setelem_catchall_deactivate(const struct net *net, 5455 + struct nft_set *set, 5456 + struct nft_set_elem *elem) 5457 + { 5458 + struct nft_set_elem_catchall *catchall; 5459 + struct nft_set_ext *ext; 5460 + 5461 + list_for_each_entry(catchall, &set->catchall_list, list) { 5462 + ext = nft_set_elem_ext(set, catchall->elem); 5463 + if (!nft_is_active(net, ext) || 5464 + nft_set_elem_mark_busy(ext)) 5465 + continue; 5466 + 5467 + kfree(elem->priv); 5468 + elem->priv = catchall->elem; 5469 + nft_set_elem_change_active(net, set, ext); 5470 + return 0; 5471 + } 5472 + 5473 + return -ENOENT; 5474 + } 5475 + 5476 + static int __nft_setelem_deactivate(const struct net *net, 5477 + struct nft_set *set, 5478 + struct nft_set_elem *elem) 5479 + { 5480 + void *priv; 5481 + 5482 + priv = set->ops->deactivate(net, set, elem); 5483 + if (!priv) 5484 + return -ENOENT; 5485 + 5486 + kfree(elem->priv); 5487 + elem->priv = priv; 5488 + set->ndeact++; 5489 + 5490 + return 0; 5491 + } 5492 + 5493 + static int nft_setelem_deactivate(const struct net *net, 5494 + struct nft_set *set, 5495 + struct nft_set_elem *elem, u32 flags) 5496 + { 5497 + int ret; 5498 + 5499 + if (flags & NFT_SET_ELEM_CATCHALL) 5500 + ret = nft_setelem_catchall_deactivate(net, set, elem); 5501 + else 5502 + ret = __nft_setelem_deactivate(net, set, elem); 5503 + 5504 + return ret; 5505 + } 5506 + 5507 + static void nft_setelem_catchall_remove(const struct net *net, 5508 + const struct nft_set *set, 5509 + const struct nft_set_elem *elem) 5510 + { 5511 + struct nft_set_elem_catchall *catchall, *next; 5512 + 5513 + list_for_each_entry_safe(catchall, next, &set->catchall_list, list) { 5514 + if (catchall->elem == elem->priv) { 5515 + list_del_rcu(&catchall->list); 5516 + kfree_rcu(catchall); 5517 + break; 5518 + } 5519 + } 5520 + } 5521 + 5522 + static void nft_setelem_remove(const struct net *net, 5523 + const struct nft_set *set, 5524 + const struct nft_set_elem *elem) 5525 + { 5526 + if (nft_setelem_is_catchall(set, elem)) 5527 + nft_setelem_catchall_remove(net, set, elem); 5528 + else 5529 + set->ops->remove(net, set, elem); 5530 + } 5531 + 5459 5532 static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, 5460 5533 const struct nlattr *attr, u32 nlmsg_flags) 5461 5534 { ··· 5668 5369 if (err < 0) 5669 5370 return err; 5670 5371 5671 - if (nla[NFTA_SET_ELEM_KEY] == NULL) 5672 - return -EINVAL; 5673 - 5674 5372 nft_set_ext_prepare(&tmpl); 5675 5373 5676 5374 err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags); 5677 5375 if (err < 0) 5678 5376 return err; 5377 + 5378 + if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL)) 5379 + return -EINVAL; 5380 + 5679 5381 if (flags != 0) 5680 5382 nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS); 5681 5383 ··· 5781 5481 num_exprs = set->num_exprs; 5782 5482 } 5783 5483 5784 - err = nft_setelem_parse_key(ctx, set, &elem.key.val, 5785 - nla[NFTA_SET_ELEM_KEY]); 5786 - if (err < 0) 5787 - goto err_set_elem_expr; 5484 + if (nla[NFTA_SET_ELEM_KEY]) { 5485 + err = nft_setelem_parse_key(ctx, set, &elem.key.val, 5486 + nla[NFTA_SET_ELEM_KEY]); 5487 + if (err < 0) 5488 + goto err_set_elem_expr; 5788 5489 5789 - nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen); 5490 + nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen); 5491 + } 5790 5492 5791 5493 if (nla[NFTA_SET_ELEM_KEY_END]) { 5792 5494 err = nft_setelem_parse_key(ctx, set, &elem.key_end.val, ··· 5905 5603 } 5906 5604 5907 5605 ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; 5908 - err = set->ops->insert(ctx->net, set, &elem, &ext2); 5606 + 5607 + err = nft_setelem_insert(ctx->net, set, &elem, &ext2, flags); 5909 5608 if (err) { 5910 5609 if (err == -EEXIST) { 5911 5610 if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) ^ ··· 5933 5630 goto err_element_clash; 5934 5631 } 5935 5632 5936 - if (set->size && 5633 + if (!(flags & NFT_SET_ELEM_CATCHALL) && set->size && 5937 5634 !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) { 5938 5635 err = -ENFILE; 5939 5636 goto err_set_full; ··· 5944 5641 return 0; 5945 5642 5946 5643 err_set_full: 5947 - set->ops->remove(ctx->net, set, &elem); 5644 + nft_setelem_remove(ctx->net, set, &elem); 5948 5645 err_element_clash: 5949 5646 kfree(trans); 5950 5647 err_elem_expr: ··· 6076 5773 struct nft_set_ext *ext; 6077 5774 struct nft_trans *trans; 6078 5775 u32 flags = 0; 6079 - void *priv; 6080 5776 int err; 6081 5777 6082 5778 err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr, ··· 6083 5781 if (err < 0) 6084 5782 return err; 6085 5783 6086 - if (nla[NFTA_SET_ELEM_KEY] == NULL) 5784 + err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags); 5785 + if (err < 0) 5786 + return err; 5787 + 5788 + if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL)) 6087 5789 return -EINVAL; 6088 5790 6089 5791 nft_set_ext_prepare(&tmpl); 6090 5792 6091 - err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags); 6092 - if (err < 0) 6093 - return err; 6094 5793 if (flags != 0) 6095 5794 nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS); 6096 5795 6097 - err = nft_setelem_parse_key(ctx, set, &elem.key.val, 6098 - nla[NFTA_SET_ELEM_KEY]); 6099 - if (err < 0) 6100 - return err; 5796 + if (nla[NFTA_SET_ELEM_KEY]) { 5797 + err = nft_setelem_parse_key(ctx, set, &elem.key.val, 5798 + nla[NFTA_SET_ELEM_KEY]); 5799 + if (err < 0) 5800 + return err; 6101 5801 6102 - nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen); 5802 + nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen); 5803 + } 6103 5804 6104 5805 if (nla[NFTA_SET_ELEM_KEY_END]) { 6105 5806 err = nft_setelem_parse_key(ctx, set, &elem.key_end.val, ··· 6128 5823 if (trans == NULL) 6129 5824 goto fail_trans; 6130 5825 6131 - priv = set->ops->deactivate(ctx->net, set, &elem); 6132 - if (priv == NULL) { 6133 - err = -ENOENT; 5826 + err = nft_setelem_deactivate(ctx->net, set, &elem, flags); 5827 + if (err < 0) 6134 5828 goto fail_ops; 6135 - } 6136 - kfree(elem.priv); 6137 - elem.priv = priv; 6138 5829 6139 5830 nft_setelem_data_deactivate(ctx->net, set, &elem); 6140 5831 ··· 6177 5876 return err; 6178 5877 } 6179 5878 5879 + static int __nft_set_catchall_flush(const struct nft_ctx *ctx, 5880 + struct nft_set *set, 5881 + struct nft_set_elem *elem) 5882 + { 5883 + struct nft_trans *trans; 5884 + 5885 + trans = nft_trans_alloc_gfp(ctx, NFT_MSG_DELSETELEM, 5886 + sizeof(struct nft_trans_elem), GFP_KERNEL); 5887 + if (!trans) 5888 + return -ENOMEM; 5889 + 5890 + nft_setelem_data_deactivate(ctx->net, set, elem); 5891 + nft_trans_elem_set(trans) = set; 5892 + nft_trans_elem(trans) = *elem; 5893 + nft_trans_commit_list_add_tail(ctx->net, trans); 5894 + 5895 + return 0; 5896 + } 5897 + 5898 + static int nft_set_catchall_flush(const struct nft_ctx *ctx, 5899 + struct nft_set *set) 5900 + { 5901 + u8 genmask = nft_genmask_next(ctx->net); 5902 + struct nft_set_elem_catchall *catchall; 5903 + struct nft_set_elem elem; 5904 + struct nft_set_ext *ext; 5905 + int ret = 0; 5906 + 5907 + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { 5908 + ext = nft_set_elem_ext(set, catchall->elem); 5909 + if (!nft_set_elem_active(ext, genmask) || 5910 + nft_set_elem_mark_busy(ext)) 5911 + continue; 5912 + 5913 + elem.priv = catchall->elem; 5914 + ret = __nft_set_catchall_flush(ctx, set, &elem); 5915 + if (ret < 0) 5916 + break; 5917 + } 5918 + 5919 + return ret; 5920 + } 5921 + 6180 5922 static int nft_set_flush(struct nft_ctx *ctx, struct nft_set *set, u8 genmask) 6181 5923 { 6182 5924 struct nft_set_iter iter = { ··· 6228 5884 }; 6229 5885 6230 5886 set->ops->walk(ctx, set, &iter); 5887 + if (!iter.err) 5888 + iter.err = nft_set_catchall_flush(ctx, set); 6231 5889 6232 5890 return iter.err; 6233 5891 } ··· 6264 5918 err = nft_del_setelem(&ctx, set, attr); 6265 5919 if (err < 0) 6266 5920 break; 6267 - 6268 - set->ndeact++; 6269 5921 } 6270 5922 return err; 6271 5923 } ··· 8614 8270 case NFT_MSG_NEWSETELEM: 8615 8271 te = (struct nft_trans_elem *)trans->data; 8616 8272 8617 - te->set->ops->activate(net, te->set, &te->elem); 8273 + nft_setelem_activate(net, te->set, &te->elem); 8618 8274 nf_tables_setelem_notify(&trans->ctx, te->set, 8619 8275 &te->elem, 8620 8276 NFT_MSG_NEWSETELEM, 0); ··· 8626 8282 nf_tables_setelem_notify(&trans->ctx, te->set, 8627 8283 &te->elem, 8628 8284 NFT_MSG_DELSETELEM, 0); 8629 - te->set->ops->remove(net, te->set, &te->elem); 8630 - atomic_dec(&te->set->nelems); 8631 - te->set->ndeact--; 8285 + nft_setelem_remove(net, te->set, &te->elem); 8286 + if (!nft_setelem_is_catchall(te->set, &te->elem)) { 8287 + atomic_dec(&te->set->nelems); 8288 + te->set->ndeact--; 8289 + } 8632 8290 break; 8633 8291 case NFT_MSG_NEWOBJ: 8634 8292 if (nft_trans_obj_update(trans)) { ··· 8831 8485 break; 8832 8486 } 8833 8487 te = (struct nft_trans_elem *)trans->data; 8834 - te->set->ops->remove(net, te->set, &te->elem); 8835 - atomic_dec(&te->set->nelems); 8488 + nft_setelem_remove(net, te->set, &te->elem); 8489 + if (!nft_setelem_is_catchall(te->set, &te->elem)) 8490 + atomic_dec(&te->set->nelems); 8836 8491 break; 8837 8492 case NFT_MSG_DELSETELEM: 8838 8493 te = (struct nft_trans_elem *)trans->data; 8839 8494 8840 8495 nft_setelem_data_activate(net, te->set, &te->elem); 8841 - te->set->ops->activate(net, te->set, &te->elem); 8842 - te->set->ndeact--; 8496 + nft_setelem_activate(net, te->set, &te->elem); 8497 + if (!nft_setelem_is_catchall(te->set, &te->elem)) 8498 + te->set->ndeact--; 8843 8499 8844 8500 nft_trans_destroy(trans); 8845 8501 break; ··· 9020 8672 return nft_check_loops(ctx, ext); 9021 8673 } 9022 8674 8675 + static int nft_set_catchall_loops(const struct nft_ctx *ctx, 8676 + struct nft_set *set) 8677 + { 8678 + u8 genmask = nft_genmask_next(ctx->net); 8679 + struct nft_set_elem_catchall *catchall; 8680 + struct nft_set_ext *ext; 8681 + int ret = 0; 8682 + 8683 + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { 8684 + ext = nft_set_elem_ext(set, catchall->elem); 8685 + if (!nft_set_elem_active(ext, genmask)) 8686 + continue; 8687 + 8688 + ret = nft_check_loops(ctx, ext); 8689 + if (ret < 0) 8690 + return ret; 8691 + } 8692 + 8693 + return ret; 8694 + } 8695 + 9023 8696 static int nf_tables_check_loops(const struct nft_ctx *ctx, 9024 8697 const struct nft_chain *chain) 9025 8698 { ··· 9100 8731 iter.fn = nf_tables_loop_check_setelem; 9101 8732 9102 8733 set->ops->walk(ctx, set, &iter); 8734 + if (!iter.err) 8735 + iter.err = nft_set_catchall_loops(ctx, set); 8736 + 9103 8737 if (iter.err < 0) 9104 8738 return iter.err; 9105 8739 }
+8 -4
net/netfilter/nft_lookup.c
··· 30 30 const struct nft_lookup *priv = nft_expr_priv(expr); 31 31 const struct nft_set *set = priv->set; 32 32 const struct nft_set_ext *ext = NULL; 33 + const struct net *net = nft_net(pkt); 33 34 bool found; 34 35 35 - found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg], 36 - &ext) ^ priv->invert; 36 + found = set->ops->lookup(net, set, &regs->data[priv->sreg], &ext) ^ 37 + priv->invert; 37 38 if (!found) { 38 - regs->verdict.code = NFT_BREAK; 39 - return; 39 + ext = nft_set_catchall_lookup(net, set); 40 + if (!ext) { 41 + regs->verdict.code = NFT_BREAK; 42 + return; 43 + } 40 44 } 41 45 42 46 if (ext) {
+7 -4
net/netfilter/nft_objref.c
··· 105 105 { 106 106 struct nft_objref_map *priv = nft_expr_priv(expr); 107 107 const struct nft_set *set = priv->set; 108 + struct net *net = nft_net(pkt); 108 109 const struct nft_set_ext *ext; 109 110 struct nft_object *obj; 110 111 bool found; 111 112 112 - found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg], 113 - &ext); 113 + found = set->ops->lookup(net, set, &regs->data[priv->sreg], &ext); 114 114 if (!found) { 115 - regs->verdict.code = NFT_BREAK; 116 - return; 115 + ext = nft_set_catchall_lookup(net, set); 116 + if (!ext) { 117 + regs->verdict.code = NFT_BREAK; 118 + return; 119 + } 117 120 } 118 121 obj = *nft_set_ext_obj(ext); 119 122 obj->ops->eval(obj, regs, pkt);
+6
net/netfilter/nft_set_hash.c
··· 350 350 rhashtable_walk_stop(&hti); 351 351 rhashtable_walk_exit(&hti); 352 352 353 + he = nft_set_catchall_gc(set); 354 + if (he) { 355 + gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); 356 + if (gcb) 357 + nft_set_gc_batch_add(gcb, he); 358 + } 353 359 nft_set_gc_batch_complete(gcb); 354 360 queue_delayed_work(system_power_efficient_wq, &priv->gc_work, 355 361 nft_set_gc_interval(set));
+5 -1
net/netfilter/nft_set_pipapo.c
··· 1529 1529 { 1530 1530 struct nft_pipapo *priv = nft_set_priv(set); 1531 1531 int rules_f0, first_rule = 0; 1532 + struct nft_pipapo_elem *e; 1532 1533 1533 1534 while ((rules_f0 = pipapo_rules_same_key(m->f, first_rule))) { 1534 1535 union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS]; 1535 1536 struct nft_pipapo_field *f; 1536 - struct nft_pipapo_elem *e; 1537 1537 int i, start, rules_fx; 1538 1538 1539 1539 start = first_rule; ··· 1568 1568 first_rule += rules_f0; 1569 1569 } 1570 1570 } 1571 + 1572 + e = nft_set_catchall_gc(set); 1573 + if (e) 1574 + nft_set_elem_destroy(set, e, true); 1571 1575 1572 1576 priv->last_gc = jiffies; 1573 1577 }
+6
net/netfilter/nft_set_rbtree.c
··· 541 541 write_seqcount_end(&priv->count); 542 542 write_unlock_bh(&priv->lock); 543 543 544 + rbe = nft_set_catchall_gc(set); 545 + if (rbe) { 546 + gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); 547 + if (gcb) 548 + nft_set_gc_batch_add(gcb, rbe); 549 + } 544 550 nft_set_gc_batch_complete(gcb); 545 551 546 552 queue_delayed_work(system_power_efficient_wq, &priv->gc_work,