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

Configure Feed

Select the types of activity you want to include in your feed.

netfilter: nft_set_rbtree: validate open interval overlap

Open intervals do not have an end element, in particular an open
interval at the end of the set is hard to validate because of it is
lacking the end element, and interval validation relies on such end
element to perform the checks.

This patch adds a new flag field to struct nft_set_elem, this is not an
issue because this is a temporary object that is allocated in the stack
from the insert/deactivate path. This flag field is used to specify that
this is the last element in this add/delete command.

The last flag is used, in combination with the start element cookie, to
check if there is a partial overlap, eg.

Already exists: 255.255.255.0-255.255.255.254
Add interval: 255.255.255.0-255.255.255.255
~~~~~~~~~~~~~
start element overlap

Basically, the idea is to check for an existing end element in the set
if there is an overlap with an existing start element.

However, the last open interval can come in any position in the add
command, the corner case can get a bit more complicated:

Already exists: 255.255.255.0-255.255.255.254
Add intervals: 255.255.255.0-255.255.255.255,255.255.255.0-255.255.255.254
~~~~~~~~~~~~~
start element overlap

To catch this overlap, annotate that the new start element is a possible
overlap, then report the overlap if the next element is another start
element that confirms that previous element in an open interval at the
end of the set.

For deletions, do not update the start cookie when deleting an open
interval, otherwise this can trigger spurious EEXIST when adding new
elements.

Unfortunately, there is no NFT_SET_ELEM_INTERVAL_OPEN flag which would
make easier to detect open interval overlaps.

Fixes: 7c84d41416d8 ("netfilter: nft_set_rbtree: Detect partial overlaps on insertion")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>

authored by

Pablo Neira Ayuso and committed by
Florian Westphal
64894696 782f2688

+82 -14
+4
include/net/netfilter/nf_tables.h
··· 277 277 unsigned char data[]; 278 278 }; 279 279 280 + #define NFT_SET_ELEM_INTERNAL_LAST 0x1 281 + 280 282 /* placeholder structure for opaque set element backend representation. */ 281 283 struct nft_elem_priv { }; 282 284 ··· 288 286 * @key: element key 289 287 * @key_end: closing element key 290 288 * @data: element data 289 + * @flags: flags 291 290 * @priv: element private data and extensions 292 291 */ 293 292 struct nft_set_elem { ··· 304 301 u32 buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)]; 305 302 struct nft_data val; 306 303 } data; 304 + u32 flags; 307 305 struct nft_elem_priv *priv; 308 306 }; 309 307
+17 -4
net/netfilter/nf_tables_api.c
··· 7270 7270 } 7271 7271 7272 7272 static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, 7273 - const struct nlattr *attr, u32 nlmsg_flags) 7273 + const struct nlattr *attr, u32 nlmsg_flags, 7274 + bool last) 7274 7275 { 7275 7276 struct nft_expr *expr_array[NFT_SET_EXPR_MAX] = {}; 7276 7277 struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; ··· 7557 7556 if (flags) 7558 7557 *nft_set_ext_flags(ext) = flags; 7559 7558 7559 + if (last) 7560 + elem.flags = NFT_SET_ELEM_INTERNAL_LAST; 7561 + else 7562 + elem.flags = 0; 7563 + 7560 7564 if (obj) 7561 7565 *nft_set_ext_obj(ext) = obj; 7562 7566 ··· 7725 7719 nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); 7726 7720 7727 7721 nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { 7728 - err = nft_add_set_elem(&ctx, set, attr, info->nlh->nlmsg_flags); 7722 + err = nft_add_set_elem(&ctx, set, attr, info->nlh->nlmsg_flags, 7723 + nla_is_last(attr, rem)); 7729 7724 if (err < 0) { 7730 7725 NL_SET_BAD_ATTR(extack, attr); 7731 7726 return err; ··· 7850 7843 } 7851 7844 7852 7845 static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, 7853 - const struct nlattr *attr) 7846 + const struct nlattr *attr, bool last) 7854 7847 { 7855 7848 struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; 7856 7849 struct nft_set_ext_tmpl tmpl; ··· 7917 7910 ext = nft_set_elem_ext(set, elem.priv); 7918 7911 if (flags) 7919 7912 *nft_set_ext_flags(ext) = flags; 7913 + 7914 + if (last) 7915 + elem.flags = NFT_SET_ELEM_INTERNAL_LAST; 7916 + else 7917 + elem.flags = 0; 7920 7918 7921 7919 trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set); 7922 7920 if (trans == NULL) ··· 8070 8058 return nft_set_flush(&ctx, set, genmask); 8071 8059 8072 8060 nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { 8073 - err = nft_del_setelem(&ctx, set, attr); 8061 + err = nft_del_setelem(&ctx, set, attr, 8062 + nla_is_last(attr, rem)); 8074 8063 if (err == -ENOENT && 8075 8064 NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_DESTROYSETELEM) 8076 8065 continue;
+61 -10
net/netfilter/nft_set_rbtree.c
··· 304 304 priv->start_rbe_cookie = (unsigned long)rbe; 305 305 } 306 306 307 + static void nft_rbtree_set_start_cookie_open(struct nft_rbtree *priv, 308 + const struct nft_rbtree_elem *rbe, 309 + unsigned long open_interval) 310 + { 311 + priv->start_rbe_cookie = (unsigned long)rbe | open_interval; 312 + } 313 + 314 + #define NFT_RBTREE_OPEN_INTERVAL 1UL 315 + 307 316 static bool nft_rbtree_cmp_start_cookie(struct nft_rbtree *priv, 308 317 const struct nft_rbtree_elem *rbe) 309 318 { 310 - return priv->start_rbe_cookie == (unsigned long)rbe; 319 + return (priv->start_rbe_cookie & ~NFT_RBTREE_OPEN_INTERVAL) == (unsigned long)rbe; 311 320 } 312 321 313 322 static bool nft_rbtree_insert_same_interval(const struct net *net, ··· 346 337 347 338 static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, 348 339 struct nft_rbtree_elem *new, 349 - struct nft_elem_priv **elem_priv, u64 tstamp) 340 + struct nft_elem_priv **elem_priv, u64 tstamp, bool last) 350 341 { 351 342 struct nft_rbtree_elem *rbe, *rbe_le = NULL, *rbe_ge = NULL, *rbe_prev; 352 343 struct rb_node *node, *next, *parent, **p, *first = NULL; 353 344 struct nft_rbtree *priv = nft_set_priv(set); 354 345 u8 cur_genmask = nft_genmask_cur(net); 355 346 u8 genmask = nft_genmask_next(net); 347 + unsigned long open_interval = 0; 356 348 int d; 357 349 358 350 /* Descend the tree to search for an existing element greater than the ··· 459 449 } 460 450 } 461 451 462 - if (nft_rbtree_interval_null(set, new)) 452 + if (nft_rbtree_interval_null(set, new)) { 463 453 priv->start_rbe_cookie = 0; 464 - else if (nft_rbtree_interval_start(new) && priv->start_rbe_cookie) 465 - priv->start_rbe_cookie = 0; 454 + } else if (nft_rbtree_interval_start(new) && priv->start_rbe_cookie) { 455 + if (nft_set_is_anonymous(set)) { 456 + priv->start_rbe_cookie = 0; 457 + } else if (priv->start_rbe_cookie & NFT_RBTREE_OPEN_INTERVAL) { 458 + /* Previous element is an open interval that partially 459 + * overlaps with an existing non-open interval. 460 + */ 461 + return -ENOTEMPTY; 462 + } 463 + } 466 464 467 465 /* - new start element matching existing start element: full overlap 468 466 * reported as -EEXIST, cleared by caller if NLM_F_EXCL is not given. ··· 478 460 if (rbe_ge && !nft_rbtree_cmp(set, new, rbe_ge) && 479 461 nft_rbtree_interval_start(rbe_ge) == nft_rbtree_interval_start(new)) { 480 462 *elem_priv = &rbe_ge->priv; 481 - nft_rbtree_set_start_cookie(priv, rbe_ge); 463 + 464 + /* - Corner case: new start element of open interval (which 465 + * comes as last element in the batch) overlaps the start of 466 + * an existing interval with an end element: partial overlap. 467 + */ 468 + node = rb_first(&priv->root); 469 + rbe = __nft_rbtree_next_active(node, genmask); 470 + if (rbe && nft_rbtree_interval_end(rbe)) { 471 + rbe = nft_rbtree_next_active(rbe, genmask); 472 + if (rbe && 473 + nft_rbtree_interval_start(rbe) && 474 + !nft_rbtree_cmp(set, new, rbe)) { 475 + if (last) 476 + return -ENOTEMPTY; 477 + 478 + /* Maybe open interval? */ 479 + open_interval = NFT_RBTREE_OPEN_INTERVAL; 480 + } 481 + } 482 + nft_rbtree_set_start_cookie_open(priv, rbe_ge, open_interval); 483 + 482 484 return -EEXIST; 483 485 } 484 486 ··· 551 513 */ 552 514 if (rbe_ge && 553 515 nft_rbtree_interval_end(rbe_ge) && nft_rbtree_interval_end(new)) 516 + return -ENOTEMPTY; 517 + 518 + /* - start element overlaps an open interval but end element is new: 519 + * partial overlap, reported as -ENOEMPTY. 520 + */ 521 + if (!rbe_ge && priv->start_rbe_cookie && nft_rbtree_interval_end(new)) 554 522 return -ENOTEMPTY; 555 523 556 524 /* Accepted element: pick insertion point depending on key value */ ··· 668 624 struct nft_elem_priv **elem_priv) 669 625 { 670 626 struct nft_rbtree_elem *rbe = nft_elem_priv_cast(elem->priv); 627 + bool last = !!(elem->flags & NFT_SET_ELEM_INTERNAL_LAST); 671 628 struct nft_rbtree *priv = nft_set_priv(set); 672 629 u64 tstamp = nft_net_tstamp(net); 673 630 int err; ··· 685 640 cond_resched(); 686 641 687 642 write_lock_bh(&priv->lock); 688 - err = __nft_rbtree_insert(net, set, rbe, elem_priv, tstamp); 643 + err = __nft_rbtree_insert(net, set, rbe, elem_priv, tstamp, last); 689 644 write_unlock_bh(&priv->lock); 645 + 646 + if (nft_rbtree_interval_end(rbe)) 647 + priv->start_rbe_cookie = 0; 648 + 690 649 } while (err == -EAGAIN); 691 650 692 651 return err; ··· 778 729 const struct nft_set_elem *elem) 779 730 { 780 731 struct nft_rbtree_elem *rbe, *this = nft_elem_priv_cast(elem->priv); 732 + bool last = !!(elem->flags & NFT_SET_ELEM_INTERNAL_LAST); 781 733 struct nft_rbtree *priv = nft_set_priv(set); 782 734 const struct rb_node *parent = priv->root.rb_node; 783 735 u8 genmask = nft_genmask_next(net); ··· 819 769 continue; 820 770 } 821 771 822 - if (nft_rbtree_interval_start(rbe)) 823 - nft_rbtree_set_start_cookie(priv, rbe); 824 - else if (!nft_rbtree_deactivate_same_interval(net, priv, rbe)) 772 + if (nft_rbtree_interval_start(rbe)) { 773 + if (!last) 774 + nft_rbtree_set_start_cookie(priv, rbe); 775 + } else if (!nft_rbtree_deactivate_same_interval(net, priv, rbe)) 825 776 return NULL; 826 777 827 778 nft_rbtree_flush(net, set, &rbe->priv);