Skip to content

Commit 7c84d41

Browse files
sbrivio-rhummakynes
authored andcommitted
netfilter: nft_set_rbtree: Detect partial overlaps on insertion
...and return -ENOTEMPTY to the front-end in this case, instead of proceeding. Currently, nft takes care of checking for these cases and not sending them to the kernel, but if we drop the set_overlap() call in nft we can end up in situations like: # nft add table t # nft add set t s '{ type inet_service ; flags interval ; }' # nft add element t s '{ 1 - 5 }' # nft add element t s '{ 6 - 10 }' # nft add element t s '{ 4 - 7 }' # nft list set t s table ip t { set s { type inet_service flags interval elements = { 1-3, 4-5, 6-7 } } } This change has the primary purpose of making the behaviour consistent with nft_set_pipapo, but is also functional to avoid inconsistent behaviour if userspace sends overlapping elements for any reason. v2: When we meet the same key data in the tree, as start element while inserting an end element, or as end element while inserting a start element, actually check that the existing element is active, before resetting the overlap flag (Pablo Neira Ayuso) Signed-off-by: Stefano Brivio <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 6f7c9ca commit 7c84d41

File tree

1 file changed

+67
-3
lines changed

1 file changed

+67
-3
lines changed

net/netfilter/nft_set_rbtree.c

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,43 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
213213
u8 genmask = nft_genmask_next(net);
214214
struct nft_rbtree_elem *rbe;
215215
struct rb_node *parent, **p;
216+
bool overlap = false;
216217
int d;
217218

219+
/* Detect overlaps as we descend the tree. Set the flag in these cases:
220+
*
221+
* a1. |__ _ _? >|__ _ _ (insert start after existing start)
222+
* a2. _ _ __>| ?_ _ __| (insert end before existing end)
223+
* a3. _ _ ___| ?_ _ _>| (insert end after existing end)
224+
* a4. >|__ _ _ _ _ __| (insert start before existing end)
225+
*
226+
* and clear it later on, as we eventually reach the points indicated by
227+
* '?' above, in the cases described below. We'll always meet these
228+
* later, locally, due to tree ordering, and overlaps for the intervals
229+
* that are the closest together are always evaluated last.
230+
*
231+
* b1. |__ _ _! >|__ _ _ (insert start after existing end)
232+
* b2. _ _ __>| !_ _ __| (insert end before existing start)
233+
* b3. !_____>| (insert end after existing start)
234+
*
235+
* Case a4. resolves to b1.:
236+
* - if the inserted start element is the leftmost, because the '0'
237+
* element in the tree serves as end element
238+
* - otherwise, if an existing end is found. Note that end elements are
239+
* always inserted after corresponding start elements.
240+
*
241+
* For a new, rightmost pair of elements, we'll hit cases b1. and b3.,
242+
* in that order.
243+
*
244+
* The flag is also cleared in two special cases:
245+
*
246+
* b4. |__ _ _!|<_ _ _ (insert start right before existing end)
247+
* b5. |__ _ >|!__ _ _ (insert end right after existing start)
248+
*
249+
* which always happen as last step and imply that no further
250+
* overlapping is possible.
251+
*/
252+
218253
parent = NULL;
219254
p = &priv->root.rb_node;
220255
while (*p != NULL) {
@@ -223,17 +258,42 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
223258
d = memcmp(nft_set_ext_key(&rbe->ext),
224259
nft_set_ext_key(&new->ext),
225260
set->klen);
226-
if (d < 0)
261+
if (d < 0) {
227262
p = &parent->rb_left;
228-
else if (d > 0)
263+
264+
if (nft_rbtree_interval_start(new)) {
265+
overlap = nft_rbtree_interval_start(rbe) &&
266+
nft_set_elem_active(&rbe->ext,
267+
genmask);
268+
} else {
269+
overlap = nft_rbtree_interval_end(rbe) &&
270+
nft_set_elem_active(&rbe->ext,
271+
genmask);
272+
}
273+
} else if (d > 0) {
229274
p = &parent->rb_right;
230-
else {
275+
276+
if (nft_rbtree_interval_end(new)) {
277+
overlap = nft_rbtree_interval_end(rbe) &&
278+
nft_set_elem_active(&rbe->ext,
279+
genmask);
280+
} else if (nft_rbtree_interval_end(rbe) &&
281+
nft_set_elem_active(&rbe->ext, genmask)) {
282+
overlap = true;
283+
}
284+
} else {
231285
if (nft_rbtree_interval_end(rbe) &&
232286
nft_rbtree_interval_start(new)) {
233287
p = &parent->rb_left;
288+
289+
if (nft_set_elem_active(&rbe->ext, genmask))
290+
overlap = false;
234291
} else if (nft_rbtree_interval_start(rbe) &&
235292
nft_rbtree_interval_end(new)) {
236293
p = &parent->rb_right;
294+
295+
if (nft_set_elem_active(&rbe->ext, genmask))
296+
overlap = false;
237297
} else if (nft_set_elem_active(&rbe->ext, genmask)) {
238298
*ext = &rbe->ext;
239299
return -EEXIST;
@@ -242,6 +302,10 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
242302
}
243303
}
244304
}
305+
306+
if (overlap)
307+
return -ENOTEMPTY;
308+
245309
rb_link_node_rcu(&new->node, parent, p);
246310
rb_insert_color(&new->node, &priv->root);
247311
return 0;

0 commit comments

Comments
 (0)