[PATCH RFC nf-next 12/12] netfilter: ipset: re-add forceadd support for rhashtable
Florian Westphal <[email protected]> Tue, 14 Jul 2026 15:18:28 +0200
| Newsgroups | gmane.comp.security.firewalls.netfilter.devel |
|---|---|
| Message-ID | <[email protected]> |
The rhashtable conversion removed the SET_WITH_FORCEADD eviction logic. Sets created with the forceadd flag got IPSET_ERR_HASH_FULL instead of evicting an existing element to make room. Add mtype_remove_random() helper that walks the rhashtable to pick an element to evict, and call it from mtype_add() when the set is full and forceadd is enabled. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Florian Westphal <[email protected]> --- net/netfilter/ipset/ip_set_hash_gen.h | 43 +++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 12d3bc5acc81..df73c1ebb3f0 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -116,6 +116,7 @@ static const union nf_inet_addr zeromask = {}; #undef mtype_bucket_size #undef mtype_hash_size +#undef mtype_remove_random #undef mtype_add #undef mtype_del #undef mtype_test_cidrs @@ -165,6 +166,7 @@ static const union nf_inet_addr zeromask = {}; #define mtype_bucket_size IPSET_TOKEN(MTYPE, _bucket_size) #define mtype_hash_size IPSET_TOKEN(MTYPE, _hash_size) +#define mtype_remove_random IPSET_TOKEN(MTYPE, _remove_random) #define mtype_add IPSET_TOKEN(MTYPE, _add) #define mtype_del IPSET_TOKEN(MTYPE, _del) #define mtype_test_cidrs IPSET_TOKEN(MTYPE, _test_cidrs) @@ -511,6 +513,33 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size) (offsetof(struct mtype_rht_elem, elem) + set->dsize); } +/* Evict one element from the set to make room for a new one (forceadd) */ +static void __maybe_unused +mtype_remove_random(struct ip_set *set, struct htype *h) +{ + struct rhashtable_iter hti; + struct mtype_rht_elem *e; + bool removed = false; + + rhashtable_walk_enter(&h->ht, &hti); + rhashtable_walk_start(&hti); + e = rhashtable_walk_next(&hti); + if (IS_ERR(e)) + e = NULL; + + if (e && !rhashtable_remove_fast(&h->ht, &e->node, mtype_rht_params)) + removed = true; + + rhashtable_walk_stop(&hti); + rhashtable_walk_exit(&hti); + + if (removed) { + mtype_del_cidr_all(set, h, &e->elem); + ip_set_ext_destroy_slow(set, &e->elem); + kfree_rcu(e, rcu); + } +} + /* Add an element to a hash and update the internal counters when succeeded, * otherwise report the proper error code. */ @@ -573,11 +602,15 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, rcu_read_unlock(); if (atomic_read(&h->ht.nelems) >= h->maxelem) { - if (net_ratelimit()) - pr_warn("Set %s is full, maxelem %u reached\n", - set->name, h->maxelem); - mtype_data_next(&h->next, d); - return -IPSET_ERR_HASH_FULL; + if (SET_WITH_FORCEADD(set)) { + mtype_remove_random(set, h); + } else { + if (net_ratelimit()) + pr_warn("Set %s is full, maxelem %u reached\n", + set->name, h->maxelem); + mtype_data_next(&h->next, d); + return -IPSET_ERR_HASH_FULL; + } } e = kzalloc(offsetof(struct mtype_rht_elem, elem) + set->dsize, -- 2.54.0