Skip to content

Commit 8d738c1

Browse files
committed
netfilter: nf_tables: fix set size with rbtree backend
The existing rbtree implementation uses singleton elements to represent ranges, however, userspace provides a set size according to the number of ranges in the set. Adjust provided userspace set size to the number of singleton elements in the kernel by multiplying the range by two. Check if the no-match all-zero element is already in the set, in such case release one slot in the set size. Fixes: 0ed6389 ("netfilter: nf_tables: rename set implementations") Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 9c7ad35 commit 8d738c1

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,9 @@ struct nft_set_ext;
442442
* @remove: remove element from set
443443
* @walk: iterate over all set elements
444444
* @get: get set elements
445+
* @ksize: kernel set size
446+
* @usize: userspace set size
447+
* @adjust_maxsize: delta to adjust maximum set size
445448
* @commit: commit set elements
446449
* @abort: abort set elements
447450
* @privsize: function to return size of set private data
@@ -495,6 +498,9 @@ struct nft_set_ops {
495498
const struct nft_set *set,
496499
const struct nft_set_elem *elem,
497500
unsigned int flags);
501+
u32 (*ksize)(u32 size);
502+
u32 (*usize)(u32 size);
503+
u32 (*adjust_maxsize)(const struct nft_set *set);
498504
void (*commit)(struct nft_set *set);
499505
void (*abort)(const struct nft_set *set);
500506
u64 (*privsize)(const struct nlattr * const nla[],

net/netfilter/nf_tables_api.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4752,6 +4752,14 @@ static int nf_tables_fill_set_concat(struct sk_buff *skb,
47524752
return 0;
47534753
}
47544754

4755+
static u32 nft_set_userspace_size(const struct nft_set_ops *ops, u32 size)
4756+
{
4757+
if (ops->usize)
4758+
return ops->usize(size);
4759+
4760+
return size;
4761+
}
4762+
47554763
static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
47564764
const struct nft_set *set, u16 event, u16 flags)
47574765
{
@@ -4822,7 +4830,8 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
48224830
if (!nest)
48234831
goto nla_put_failure;
48244832
if (set->size &&
4825-
nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
4833+
nla_put_be32(skb, NFTA_SET_DESC_SIZE,
4834+
htonl(nft_set_userspace_size(set->ops, set->size))))
48264835
goto nla_put_failure;
48274836

48284837
if (set->field_count > 1 &&
@@ -5190,6 +5199,15 @@ static bool nft_set_is_same(const struct nft_set *set,
51905199
return true;
51915200
}
51925201

5202+
static u32 nft_set_kernel_size(const struct nft_set_ops *ops,
5203+
const struct nft_set_desc *desc)
5204+
{
5205+
if (ops->ksize)
5206+
return ops->ksize(desc->size);
5207+
5208+
return desc->size;
5209+
}
5210+
51935211
static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
51945212
const struct nlattr * const nla[])
51955213
{
@@ -5372,6 +5390,9 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
53725390
if (err < 0)
53735391
return err;
53745392

5393+
if (desc.size)
5394+
desc.size = nft_set_kernel_size(set->ops, &desc);
5395+
53755396
err = 0;
53765397
if (!nft_set_is_same(set, &desc, exprs, num_exprs, flags)) {
53775398
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_NAME]);
@@ -5394,6 +5415,9 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
53945415
if (IS_ERR(ops))
53955416
return PTR_ERR(ops);
53965417

5418+
if (desc.size)
5419+
desc.size = nft_set_kernel_size(ops, &desc);
5420+
53975421
udlen = 0;
53985422
if (nla[NFTA_SET_USERDATA])
53995423
udlen = nla_len(nla[NFTA_SET_USERDATA]);
@@ -7050,6 +7074,27 @@ static bool nft_setelem_valid_key_end(const struct nft_set *set,
70507074
return true;
70517075
}
70527076

7077+
static u32 nft_set_maxsize(const struct nft_set *set)
7078+
{
7079+
u32 maxsize, delta;
7080+
7081+
if (!set->size)
7082+
return UINT_MAX;
7083+
7084+
if (set->ops->adjust_maxsize)
7085+
delta = set->ops->adjust_maxsize(set);
7086+
else
7087+
delta = 0;
7088+
7089+
if (check_add_overflow(set->size, set->ndeact, &maxsize))
7090+
return UINT_MAX;
7091+
7092+
if (check_add_overflow(maxsize, delta, &maxsize))
7093+
return UINT_MAX;
7094+
7095+
return maxsize;
7096+
}
7097+
70537098
static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
70547099
const struct nlattr *attr, u32 nlmsg_flags)
70557100
{
@@ -7422,7 +7467,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
74227467
}
74237468

74247469
if (!(flags & NFT_SET_ELEM_CATCHALL)) {
7425-
unsigned int max = set->size ? set->size + set->ndeact : UINT_MAX;
7470+
unsigned int max = nft_set_maxsize(set);
74267471

74277472
if (!atomic_add_unless(&set->nelems, 1, max)) {
74287473
err = -ENFILE;

net/netfilter/nft_set_rbtree.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,46 @@ static void nft_rbtree_gc_init(const struct nft_set *set)
750750
priv->last_gc = jiffies;
751751
}
752752

753+
/* rbtree stores ranges as singleton elements, each range is composed of two
754+
* elements ...
755+
*/
756+
static u32 nft_rbtree_ksize(u32 size)
757+
{
758+
return size * 2;
759+
}
760+
761+
/* ... hide this detail to userspace. */
762+
static u32 nft_rbtree_usize(u32 size)
763+
{
764+
if (!size)
765+
return 0;
766+
767+
return size / 2;
768+
}
769+
770+
static u32 nft_rbtree_adjust_maxsize(const struct nft_set *set)
771+
{
772+
struct nft_rbtree *priv = nft_set_priv(set);
773+
struct nft_rbtree_elem *rbe;
774+
struct rb_node *node;
775+
const void *key;
776+
777+
node = rb_last(&priv->root);
778+
if (!node)
779+
return 0;
780+
781+
rbe = rb_entry(node, struct nft_rbtree_elem, node);
782+
if (!nft_rbtree_interval_end(rbe))
783+
return 0;
784+
785+
key = nft_set_ext_key(&rbe->ext);
786+
if (memchr(key, 1, set->klen))
787+
return 0;
788+
789+
/* this is the all-zero no-match element. */
790+
return 1;
791+
}
792+
753793
const struct nft_set_type nft_set_rbtree_type = {
754794
.features = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT | NFT_SET_TIMEOUT,
755795
.ops = {
@@ -768,5 +808,8 @@ const struct nft_set_type nft_set_rbtree_type = {
768808
.lookup = nft_rbtree_lookup,
769809
.walk = nft_rbtree_walk,
770810
.get = nft_rbtree_get,
811+
.ksize = nft_rbtree_ksize,
812+
.usize = nft_rbtree_usize,
813+
.adjust_maxsize = nft_rbtree_adjust_maxsize,
771814
},
772815
};

0 commit comments

Comments
 (0)