forked from Minki/linux
netfilter: nf_tables: Simplify set backend selection
Drop nft_set_type's ability to act as a container of multiple backend
implementations it chooses from. Instead consolidate the whole selection
logic in nft_select_set_ops() and the actual backend provided estimate()
callback.
This turns nf_tables_set_types into a list containing all available
backends which is traversed when selecting one matching userspace
requested criteria.
Also, this change allows to embed nft_set_ops structure into
nft_set_type and pull flags field into the latter as it's only used
during selection phase.
A crucial part of this change is to make sure the new layout respects
hash backend constraints formerly enforced by nft_hash_select_ops()
function: This is achieved by introduction of a specific estimate()
callback for nft_hash_fast_ops which returns false for key lengths != 4.
In turn, nft_hash_estimate() is changed to return false for key lengths
== 4 so it won't be chosen by accident. Also, both callbacks must return
false for unbounded sets as their size estimate depends on a known
maximum element count.
Note that this patch partially reverts commit 4f2921ca21
("netfilter:
nf_tables: meter: pick a set backend that supports updates") by making
nft_set_ops_candidate() not explicitly look for an update callback but
make NFT_SET_EVAL a regular backend feature flag which is checked along
with the others. This way all feature requirements are checked in one
go.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
36dd1bcc07
commit
71cc0873e0
@ -275,23 +275,6 @@ struct nft_set_estimate {
|
||||
enum nft_set_class space;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nft_set_type - nf_tables set type
|
||||
*
|
||||
* @select_ops: function to select nft_set_ops
|
||||
* @ops: default ops, used when no select_ops functions is present
|
||||
* @list: used internally
|
||||
* @owner: module reference
|
||||
*/
|
||||
struct nft_set_type {
|
||||
const struct nft_set_ops *(*select_ops)(const struct nft_ctx *,
|
||||
const struct nft_set_desc *desc,
|
||||
u32 flags);
|
||||
const struct nft_set_ops *ops;
|
||||
struct list_head list;
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
struct nft_set_ext;
|
||||
struct nft_expr;
|
||||
|
||||
@ -310,7 +293,6 @@ struct nft_expr;
|
||||
* @init: initialize private data of new set instance
|
||||
* @destroy: destroy private data of set instance
|
||||
* @elemsize: element private size
|
||||
* @features: features supported by the implementation
|
||||
*/
|
||||
struct nft_set_ops {
|
||||
bool (*lookup)(const struct net *net,
|
||||
@ -361,10 +343,24 @@ struct nft_set_ops {
|
||||
void (*destroy)(const struct nft_set *set);
|
||||
|
||||
unsigned int elemsize;
|
||||
u32 features;
|
||||
const struct nft_set_type *type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nft_set_type - nf_tables set type
|
||||
*
|
||||
* @ops: set ops for this type
|
||||
* @list: used internally
|
||||
* @owner: module reference
|
||||
* @features: features supported by the implementation
|
||||
*/
|
||||
struct nft_set_type {
|
||||
const struct nft_set_ops ops;
|
||||
struct list_head list;
|
||||
struct module *owner;
|
||||
u32 features;
|
||||
};
|
||||
#define to_set_type(o) container_of(o, struct nft_set_type, ops)
|
||||
|
||||
int nft_register_set(struct nft_set_type *type);
|
||||
void nft_unregister_set(struct nft_set_type *type);
|
||||
|
||||
|
@ -2523,14 +2523,12 @@ void nft_unregister_set(struct nft_set_type *type)
|
||||
EXPORT_SYMBOL_GPL(nft_unregister_set);
|
||||
|
||||
#define NFT_SET_FEATURES (NFT_SET_INTERVAL | NFT_SET_MAP | \
|
||||
NFT_SET_TIMEOUT | NFT_SET_OBJECT)
|
||||
NFT_SET_TIMEOUT | NFT_SET_OBJECT | \
|
||||
NFT_SET_EVAL)
|
||||
|
||||
static bool nft_set_ops_candidate(const struct nft_set_ops *ops, u32 flags)
|
||||
static bool nft_set_ops_candidate(const struct nft_set_type *type, u32 flags)
|
||||
{
|
||||
if ((flags & NFT_SET_EVAL) && !ops->update)
|
||||
return false;
|
||||
|
||||
return (flags & ops->features) == (flags & NFT_SET_FEATURES);
|
||||
return (flags & type->features) == (flags & NFT_SET_FEATURES);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2567,14 +2565,9 @@ nft_select_set_ops(const struct nft_ctx *ctx,
|
||||
best.space = ~0;
|
||||
|
||||
list_for_each_entry(type, &nf_tables_set_types, list) {
|
||||
if (!type->select_ops)
|
||||
ops = type->ops;
|
||||
else
|
||||
ops = type->select_ops(ctx, desc, flags);
|
||||
if (!ops)
|
||||
continue;
|
||||
ops = &type->ops;
|
||||
|
||||
if (!nft_set_ops_candidate(ops, flags))
|
||||
if (!nft_set_ops_candidate(type, flags))
|
||||
continue;
|
||||
if (!ops->estimate(desc, flags, &est))
|
||||
continue;
|
||||
@ -2605,7 +2598,7 @@ nft_select_set_ops(const struct nft_ctx *ctx,
|
||||
if (!try_module_get(type->owner))
|
||||
continue;
|
||||
if (bops != NULL)
|
||||
module_put(bops->type->owner);
|
||||
module_put(to_set_type(bops)->owner);
|
||||
|
||||
bops = ops;
|
||||
best = est;
|
||||
@ -3247,14 +3240,14 @@ err3:
|
||||
err2:
|
||||
kvfree(set);
|
||||
err1:
|
||||
module_put(ops->type->owner);
|
||||
module_put(to_set_type(ops)->owner);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nft_set_destroy(struct nft_set *set)
|
||||
{
|
||||
set->ops->destroy(set);
|
||||
module_put(set->ops->type->owner);
|
||||
module_put(to_set_type(set->ops)->owner);
|
||||
kfree(set->name);
|
||||
kvfree(set);
|
||||
}
|
||||
|
@ -296,9 +296,9 @@ static bool nft_bitmap_estimate(const struct nft_set_desc *desc, u32 features,
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct nft_set_type nft_bitmap_type;
|
||||
static struct nft_set_ops nft_bitmap_ops __read_mostly = {
|
||||
.type = &nft_bitmap_type,
|
||||
static struct nft_set_type nft_bitmap_type __read_mostly = {
|
||||
.owner = THIS_MODULE,
|
||||
.ops = {
|
||||
.privsize = nft_bitmap_privsize,
|
||||
.elemsize = offsetof(struct nft_bitmap_elem, ext),
|
||||
.estimate = nft_bitmap_estimate,
|
||||
@ -312,11 +312,7 @@ static struct nft_set_ops nft_bitmap_ops __read_mostly = {
|
||||
.lookup = nft_bitmap_lookup,
|
||||
.walk = nft_bitmap_walk,
|
||||
.get = nft_bitmap_get,
|
||||
};
|
||||
|
||||
static struct nft_set_type nft_bitmap_type __read_mostly = {
|
||||
.ops = &nft_bitmap_ops,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init nft_bitmap_module_init(void)
|
||||
|
@ -605,6 +605,12 @@ static void nft_hash_destroy(const struct nft_set *set)
|
||||
static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
|
||||
struct nft_set_estimate *est)
|
||||
{
|
||||
if (!desc->size)
|
||||
return false;
|
||||
|
||||
if (desc->klen == 4)
|
||||
return false;
|
||||
|
||||
est->size = sizeof(struct nft_hash) +
|
||||
nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
|
||||
desc->size * sizeof(struct nft_hash_elem);
|
||||
@ -614,9 +620,29 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct nft_set_type nft_hash_type;
|
||||
static struct nft_set_ops nft_rhash_ops __read_mostly = {
|
||||
.type = &nft_hash_type,
|
||||
static bool nft_hash_fast_estimate(const struct nft_set_desc *desc, u32 features,
|
||||
struct nft_set_estimate *est)
|
||||
{
|
||||
if (!desc->size)
|
||||
return false;
|
||||
|
||||
if (desc->klen != 4)
|
||||
return false;
|
||||
|
||||
est->size = sizeof(struct nft_hash) +
|
||||
nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
|
||||
desc->size * sizeof(struct nft_hash_elem);
|
||||
est->lookup = NFT_SET_CLASS_O_1;
|
||||
est->space = NFT_SET_CLASS_O_N;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct nft_set_type nft_rhash_type __read_mostly = {
|
||||
.owner = THIS_MODULE,
|
||||
.features = NFT_SET_MAP | NFT_SET_OBJECT |
|
||||
NFT_SET_TIMEOUT | NFT_SET_EVAL,
|
||||
.ops = {
|
||||
.privsize = nft_rhash_privsize,
|
||||
.elemsize = offsetof(struct nft_rhash_elem, ext),
|
||||
.estimate = nft_rhash_estimate,
|
||||
@ -631,11 +657,13 @@ static struct nft_set_ops nft_rhash_ops __read_mostly = {
|
||||
.update = nft_rhash_update,
|
||||
.walk = nft_rhash_walk,
|
||||
.get = nft_rhash_get,
|
||||
.features = NFT_SET_MAP | NFT_SET_OBJECT | NFT_SET_TIMEOUT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct nft_set_ops nft_hash_ops __read_mostly = {
|
||||
.type = &nft_hash_type,
|
||||
static struct nft_set_type nft_hash_type __read_mostly = {
|
||||
.owner = THIS_MODULE,
|
||||
.features = NFT_SET_MAP | NFT_SET_OBJECT,
|
||||
.ops = {
|
||||
.privsize = nft_hash_privsize,
|
||||
.elemsize = offsetof(struct nft_hash_elem, ext),
|
||||
.estimate = nft_hash_estimate,
|
||||
@ -649,14 +677,16 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
|
||||
.lookup = nft_hash_lookup,
|
||||
.walk = nft_hash_walk,
|
||||
.get = nft_hash_get,
|
||||
.features = NFT_SET_MAP | NFT_SET_OBJECT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct nft_set_ops nft_hash_fast_ops __read_mostly = {
|
||||
.type = &nft_hash_type,
|
||||
static struct nft_set_type nft_hash_fast_type __read_mostly = {
|
||||
.owner = THIS_MODULE,
|
||||
.features = NFT_SET_MAP | NFT_SET_OBJECT,
|
||||
.ops = {
|
||||
.privsize = nft_hash_privsize,
|
||||
.elemsize = offsetof(struct nft_hash_elem, ext),
|
||||
.estimate = nft_hash_estimate,
|
||||
.estimate = nft_hash_fast_estimate,
|
||||
.init = nft_hash_init,
|
||||
.destroy = nft_hash_destroy,
|
||||
.insert = nft_hash_insert,
|
||||
@ -667,38 +697,23 @@ static struct nft_set_ops nft_hash_fast_ops __read_mostly = {
|
||||
.lookup = nft_hash_lookup_fast,
|
||||
.walk = nft_hash_walk,
|
||||
.get = nft_hash_get,
|
||||
.features = NFT_SET_MAP | NFT_SET_OBJECT,
|
||||
};
|
||||
|
||||
static const struct nft_set_ops *
|
||||
nft_hash_select_ops(const struct nft_ctx *ctx, const struct nft_set_desc *desc,
|
||||
u32 flags)
|
||||
{
|
||||
if (desc->size && !(flags & (NFT_SET_EVAL | NFT_SET_TIMEOUT))) {
|
||||
switch (desc->klen) {
|
||||
case 4:
|
||||
return &nft_hash_fast_ops;
|
||||
default:
|
||||
return &nft_hash_ops;
|
||||
}
|
||||
}
|
||||
|
||||
return &nft_rhash_ops;
|
||||
}
|
||||
|
||||
static struct nft_set_type nft_hash_type __read_mostly = {
|
||||
.select_ops = nft_hash_select_ops,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init nft_hash_module_init(void)
|
||||
{
|
||||
return nft_register_set(&nft_hash_type);
|
||||
if (nft_register_set(&nft_hash_fast_type) ||
|
||||
nft_register_set(&nft_hash_type) ||
|
||||
nft_register_set(&nft_rhash_type))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit nft_hash_module_exit(void)
|
||||
{
|
||||
nft_unregister_set(&nft_rhash_type);
|
||||
nft_unregister_set(&nft_hash_type);
|
||||
nft_unregister_set(&nft_hash_fast_type);
|
||||
}
|
||||
|
||||
module_init(nft_hash_module_init);
|
||||
|
@ -393,9 +393,10 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct nft_set_type nft_rbtree_type;
|
||||
static struct nft_set_ops nft_rbtree_ops __read_mostly = {
|
||||
.type = &nft_rbtree_type,
|
||||
static struct nft_set_type nft_rbtree_type __read_mostly = {
|
||||
.owner = THIS_MODULE,
|
||||
.features = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT,
|
||||
.ops = {
|
||||
.privsize = nft_rbtree_privsize,
|
||||
.elemsize = offsetof(struct nft_rbtree_elem, ext),
|
||||
.estimate = nft_rbtree_estimate,
|
||||
@ -409,12 +410,7 @@ static struct nft_set_ops nft_rbtree_ops __read_mostly = {
|
||||
.lookup = nft_rbtree_lookup,
|
||||
.walk = nft_rbtree_walk,
|
||||
.get = nft_rbtree_get,
|
||||
.features = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT,
|
||||
};
|
||||
|
||||
static struct nft_set_type nft_rbtree_type __read_mostly = {
|
||||
.ops = &nft_rbtree_ops,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init nft_rbtree_module_init(void)
|
||||
|
Loading…
Reference in New Issue
Block a user