From 4f2921ca21b71a9faaecd84a9fc74401d3a8d275 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 14 Mar 2018 13:37:58 +0100 Subject: [PATCH] netfilter: nf_tables: meter: pick a set backend that supports updates in nftables, 'meter' can be used to instantiate a hash-table at run time: rule add filter forward iif "internal" meter hostacct { ip saddr counter} nft list meter ip filter hostacct table ip filter { meter hostacct { type ipv4_addr elements = { 192.168.0.1 : counter packets 8 bytes 2672, .. because elemets get added on the fly, the kernel must chose a set backend type that implements the ->update() function, otherwise rule insertion fails with EOPNOTSUPP. Therefore, skip set types that lack ->update, and also make sure we do not discard a (bad) candidate when we did yet find any candidate at all. This could happen when userspace prefers low memory footprint -- the set implementation currently checked might not be a fit at all. Make sure we pick it anyway (!bops). In case next candidate is a better fix, it will be chosen instead. But in case nothing else is found we at least have a non-ideal match rather than no match at all. Fixes: 6c03ae210ce3 ("netfilter: nft_set_hash: add non-resizable hashtable implementation") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 5 ++++- net/netfilter/nft_set_hash.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index c4acc7340eb1..36f69acaf51f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2446,6 +2446,9 @@ EXPORT_SYMBOL_GPL(nft_unregister_set); static bool nft_set_ops_candidate(const struct nft_set_ops *ops, u32 flags) { + if ((flags & NFT_SET_EVAL) && !ops->update) + return false; + return (flags & ops->features) == (flags & NFT_SET_FEATURES); } @@ -2510,7 +2513,7 @@ nft_select_set_ops(const struct nft_ctx *ctx, if (est.space == best.space && est.lookup < best.lookup) break; - } else if (est.size < best.size) { + } else if (est.size < best.size || !bops) { break; } continue; diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index d40591fe1b2f..fc9c6d5d64cd 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -674,7 +674,7 @@ 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_TIMEOUT)) { + if (desc->size && !(flags & (NFT_SET_EVAL | NFT_SET_TIMEOUT))) { switch (desc->klen) { case 4: return &nft_hash_fast_ops;