Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nft_rbtree: allow adjacent intervals with dynamic updates

This patch fixes dynamic element updates for adjacent intervals in the
rb-tree representation.

Since elements are sorted in the rb-tree, in case of adjacent nodes with
the same key, the assumption is that an interval end node must be placed
before an interval opening.

In tree lookup operations, the idea is to search for the closer element
that is smaller than the one we're searching for. Given that we'll have
two possible matchings, we have to take the opening interval in case of
adjacent nodes.

Range merges are not trivial with the current representation,
specifically we have to check if node extensions are equal and make sure
we keep the existing internal states around.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+35 -5
+35 -5
net/netfilter/nft_rbtree.c
··· 35 35 (*nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END); 36 36 } 37 37 38 + static bool nft_rbtree_equal(const struct nft_set *set, const void *this, 39 + const struct nft_rbtree_elem *interval) 40 + { 41 + return memcmp(this, nft_set_ext_key(&interval->ext), set->klen) == 0; 42 + } 43 + 38 44 static bool nft_rbtree_lookup(const struct nft_set *set, const u32 *key, 39 45 const struct nft_set_ext **ext) 40 46 { ··· 48 42 const struct nft_rbtree_elem *rbe, *interval = NULL; 49 43 const struct rb_node *parent; 50 44 u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); 45 + const void *this; 51 46 int d; 52 47 53 48 spin_lock_bh(&nft_rbtree_lock); ··· 56 49 while (parent != NULL) { 57 50 rbe = rb_entry(parent, struct nft_rbtree_elem, node); 58 51 59 - d = memcmp(nft_set_ext_key(&rbe->ext), key, set->klen); 52 + this = nft_set_ext_key(&rbe->ext); 53 + d = memcmp(this, key, set->klen); 60 54 if (d < 0) { 61 55 parent = parent->rb_left; 56 + /* In case of adjacent ranges, we always see the high 57 + * part of the range in first place, before the low one. 58 + * So don't update interval if the keys are equal. 59 + */ 60 + if (interval && nft_rbtree_equal(set, this, interval)) 61 + continue; 62 62 interval = rbe; 63 63 } else if (d > 0) 64 64 parent = parent->rb_right; ··· 115 101 else if (d > 0) 116 102 p = &parent->rb_right; 117 103 else { 118 - if (nft_set_elem_active(&rbe->ext, genmask)) 119 - return -EEXIST; 120 - p = &parent->rb_left; 104 + if (nft_set_elem_active(&rbe->ext, genmask)) { 105 + if (nft_rbtree_interval_end(rbe) && 106 + !nft_rbtree_interval_end(new)) 107 + p = &parent->rb_left; 108 + else if (!nft_rbtree_interval_end(rbe) && 109 + nft_rbtree_interval_end(new)) 110 + p = &parent->rb_right; 111 + else 112 + return -EEXIST; 113 + } 121 114 } 122 115 } 123 116 rb_link_node(&new->node, parent, p); ··· 169 148 { 170 149 const struct nft_rbtree *priv = nft_set_priv(set); 171 150 const struct rb_node *parent = priv->root.rb_node; 172 - struct nft_rbtree_elem *rbe; 151 + struct nft_rbtree_elem *rbe, *this = elem->priv; 173 152 u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); 174 153 int d; 175 154 ··· 185 164 else { 186 165 if (!nft_set_elem_active(&rbe->ext, genmask)) { 187 166 parent = parent->rb_left; 167 + continue; 168 + } 169 + if (nft_rbtree_interval_end(rbe) && 170 + !nft_rbtree_interval_end(this)) { 171 + parent = parent->rb_left; 172 + continue; 173 + } else if (!nft_rbtree_interval_end(rbe) && 174 + nft_rbtree_interval_end(this)) { 175 + parent = parent->rb_right; 188 176 continue; 189 177 } 190 178 nft_set_elem_change_active(set, &rbe->ext);