netfilter: nft_set_rbtree: skip elements in transaction from garbage collection
authorPablo Neira Ayuso <pablo@netfilter.org>
Sat, 14 Jan 2023 22:49:46 +0000 (23:49 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 23 Jan 2023 20:38:33 +0000 (21:38 +0100)
Skip interference with an ongoing transaction, do not perform garbage
collection on inactive elements. Reset annotated previous end interval
if the expired element is marked as busy (control plane removed the
element right before expiration).

Fixes: 8d8540c4f5e0 ("netfilter: nft_set_rbtree: add timeout support")
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nft_set_rbtree.c

index 217225e13faf7dca0d7ece9ba29fbf77f9ffb5f0..19ea4d3c35535552a3acf1ac18f33b91645f944a 100644 (file)
@@ -563,23 +563,37 @@ static void nft_rbtree_gc(struct work_struct *work)
        struct nft_rbtree *priv;
        struct rb_node *node;
        struct nft_set *set;
+       struct net *net;
+       u8 genmask;
 
        priv = container_of(work, struct nft_rbtree, gc_work.work);
        set  = nft_set_container_of(priv);
+       net  = read_pnet(&set->net);
+       genmask = nft_genmask_cur(net);
 
        write_lock_bh(&priv->lock);
        write_seqcount_begin(&priv->count);
        for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
                rbe = rb_entry(node, struct nft_rbtree_elem, node);
 
+               if (!nft_set_elem_active(&rbe->ext, genmask))
+                       continue;
+
+               /* elements are reversed in the rbtree for historical reasons,
+                * from highest to lowest value, that is why end element is
+                * always visited before the start element.
+                */
                if (nft_rbtree_interval_end(rbe)) {
                        rbe_end = rbe;
                        continue;
                }
                if (!nft_set_elem_expired(&rbe->ext))
                        continue;
-               if (nft_set_elem_mark_busy(&rbe->ext))
+
+               if (nft_set_elem_mark_busy(&rbe->ext)) {
+                       rbe_end = NULL;
                        continue;
+               }
 
                if (rbe_prev) {
                        rb_erase(&rbe_prev->node, &priv->root);