commit 796b1f40a42b505d0e614fd2fbb6dad9f4e3c2c5 Author: Pablo Neira Ayuso Date: Fri Jul 15 10:38:31 2022 +0200 x diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 87a28d2dca77..176173f770fd 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -220,13 +220,37 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, struct nft_set_ext **ext) { struct nft_rbtree_elem *rbe, *rbe_le = NULL, *rbe_ge = NULL; + struct rb_node *node, *parent, **p, *first = NULL; struct nft_rbtree *priv = nft_set_priv(set); u8 genmask = nft_genmask_next(net); - struct rb_node *node, *parent, **p; int d; + parent = NULL; + p = &priv->root.rb_node; + while (*p != NULL) { + parent = *p; + rbe = rb_entry(parent, struct nft_rbtree_elem, node); + d = nft_rbtree_cmp(set, rbe, new); + + if (d < 0) + p = &parent->rb_left; + else if (d > 0) { + first = &rbe->node; + p = &parent->rb_right; + } else { + first = &rbe->node; + if (nft_rbtree_interval_end(rbe)) + p = &parent->rb_left; + else + p = &parent->rb_right; + } + } + + if (!first) + first = rb_first(&priv->root); + /* Detect overlaps by going through the list of valid tree nodes: */ - for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) { + for (node = first; node != NULL; node = rb_next(node)) { rbe = rb_entry(node, struct nft_rbtree_elem, node); if (!nft_set_elem_active(&rbe->ext, genmask) || @@ -235,9 +259,13 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, d = nft_rbtree_cmp(set, rbe, new); - if (d <= 0 && (!rbe_le || nft_rbtree_cmp(set, rbe, rbe_le) > 0)) + /* annotate element coming before new element. */ + if (d < 0 && (!rbe_le || nft_rbtree_cmp(set, rbe, rbe_le) > 0)) { rbe_le = rbe; + break; + } + /* annotate existing element coming after new element. */ if (d >= 0 && (!rbe_ge || nft_rbtree_cmp(set, rbe, rbe_ge) < 0)) rbe_ge = rbe; } @@ -246,7 +274,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, * matching end element: full overlap reported as -EEXIST, cleared by * caller if NLM_F_EXCL is not given */ - rbe = rbe_le; + rbe = rbe_ge; if (rbe && !nft_rbtree_cmp(set, new, rbe) && nft_rbtree_interval_start(rbe) == nft_rbtree_interval_start(new)) { *ext = &rbe->ext; @@ -257,7 +285,14 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, * being a start element: partial overlap, reported as -ENOTEMPTY */ if (rbe_le && - nft_rbtree_interval_start(rbe_le) && nft_rbtree_interval_start(new)) + nft_rbtree_interval_end(rbe_le) && nft_rbtree_interval_end(new)) + return -ENOTEMPTY; + + /* - new start element before existing closest, less or equal key value + * element: partial overlap, reported as -ENOTEMPTY + */ + if (rbe_ge && + nft_rbtree_interval_start(rbe_ge) && nft_rbtree_interval_start(new)) return -ENOTEMPTY; /* - new end element with existing closest, greater or equal key value