]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
netfilter: nf_tables: use timestamp to check for set element timeout
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 12 Aug 2024 10:24:43 +0000 (12:24 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Aug 2024 03:45:50 +0000 (05:45 +0200)
commit 7395dfacfff65e9938ac0889dafa1ab01e987d15 upstream

Add a timestamp field at the beginning of the transaction, store it
in the nftables per-netns area.

Update set backend .insert, .deactivate and sync gc path to use the
timestamp, this avoids that an element expires while control plane
transaction is still unfinished.

.lookup and .update, which are used from packet path, still use the
current time to check if the element has expired. And .get path and dump
also since this runs lockless under rcu read size lock. Then, there is
async gc which also needs to check the current time since it runs
asynchronously from a workqueue.

Fixes: c3e1b005ed1c ("netfilter: nf_tables: add set element timeout support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/net/netfilter/nf_tables.h
net/netfilter/nf_tables_api.c
net/netfilter/nft_set_hash.c
net/netfilter/nft_set_pipapo.c
net/netfilter/nft_set_rbtree.c

index 3ff6b3362800beec569b012f8ba61b4f0b1d44b2..d241c6d8174ab72785469941138cdf5b076d2837 100644 (file)
@@ -772,10 +772,16 @@ static inline struct nft_set_elem_expr *nft_set_ext_expr(const struct nft_set_ex
        return nft_set_ext(ext, NFT_SET_EXT_EXPRESSIONS);
 }
 
-static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
+static inline bool __nft_set_elem_expired(const struct nft_set_ext *ext,
+                                         u64 tstamp)
 {
        return nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION) &&
-              time_is_before_eq_jiffies64(*nft_set_ext_expiration(ext));
+              time_after_eq64(tstamp, *nft_set_ext_expiration(ext));
+}
+
+static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
+{
+       return __nft_set_elem_expired(ext, get_jiffies_64());
 }
 
 static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
@@ -1679,6 +1685,7 @@ struct nftables_pernet {
        struct list_head        notify_list;
        struct mutex            commit_mutex;
        u64                     table_handle;
+       u64                     tstamp;
        unsigned int            base_seq;
        u8                      validate_state;
        unsigned int            gc_seq;
@@ -1691,4 +1698,9 @@ static inline struct nftables_pernet *nft_pernet(const struct net *net)
        return net_generic(net, nf_tables_net_id);
 }
 
+static inline u64 nft_net_tstamp(const struct net *net)
+{
+       return nft_pernet(net)->tstamp;
+}
+
 #endif /* _NET_NF_TABLES_H */
index 95c28893002e3a896f252e48eb8972e3bbc36d28..04f4f75e74dae48b58a255ff5e89b76ad4c60c42 100644 (file)
@@ -9185,6 +9185,7 @@ dead_elem:
 struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
 {
        struct nft_set_elem_catchall *catchall, *next;
+       u64 tstamp = nft_net_tstamp(gc->net);
        const struct nft_set *set = gc->set;
        struct nft_set_elem elem;
        struct nft_set_ext *ext;
@@ -9194,7 +9195,7 @@ struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
        list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
                ext = nft_set_elem_ext(set, catchall->elem);
 
-               if (!nft_set_elem_expired(ext))
+               if (!__nft_set_elem_expired(ext, tstamp))
                        continue;
 
                gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
@@ -9946,6 +9947,7 @@ static bool nf_tables_valid_genid(struct net *net, u32 genid)
        bool genid_ok;
 
        mutex_lock(&nft_net->commit_mutex);
+       nft_net->tstamp = get_jiffies_64();
 
        genid_ok = genid == 0 || nft_net->base_seq == genid;
        if (!genid_ok)
index 2013de934cef096ac8fa512c1cd4bc18c7c52c10..1fd3b413350dcc82d59135e3a6475b027d22802d 100644 (file)
@@ -35,6 +35,7 @@ struct nft_rhash_cmp_arg {
        const struct nft_set            *set;
        const u32                       *key;
        u8                              genmask;
+       u64                             tstamp;
 };
 
 static inline u32 nft_rhash_key(const void *data, u32 len, u32 seed)
@@ -61,7 +62,7 @@ static inline int nft_rhash_cmp(struct rhashtable_compare_arg *arg,
                return 1;
        if (nft_set_elem_is_dead(&he->ext))
                return 1;
-       if (nft_set_elem_expired(&he->ext))
+       if (__nft_set_elem_expired(&he->ext, x->tstamp))
                return 1;
        if (!nft_set_elem_active(&he->ext, x->genmask))
                return 1;
@@ -86,6 +87,7 @@ bool nft_rhash_lookup(const struct net *net, const struct nft_set *set,
                .genmask = nft_genmask_cur(net),
                .set     = set,
                .key     = key,
+               .tstamp  = get_jiffies_64(),
        };
 
        he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -104,6 +106,7 @@ static void *nft_rhash_get(const struct net *net, const struct nft_set *set,
                .genmask = nft_genmask_cur(net),
                .set     = set,
                .key     = elem->key.val.data,
+               .tstamp  = get_jiffies_64(),
        };
 
        he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -127,6 +130,7 @@ static bool nft_rhash_update(struct nft_set *set, const u32 *key,
                .genmask = NFT_GENMASK_ANY,
                .set     = set,
                .key     = key,
+               .tstamp  = get_jiffies_64(),
        };
 
        he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -170,6 +174,7 @@ static int nft_rhash_insert(const struct net *net, const struct nft_set *set,
                .genmask = nft_genmask_next(net),
                .set     = set,
                .key     = elem->key.val.data,
+               .tstamp  = nft_net_tstamp(net),
        };
        struct nft_rhash_elem *prev;
 
@@ -212,6 +217,7 @@ static void *nft_rhash_deactivate(const struct net *net,
                .genmask = nft_genmask_next(net),
                .set     = set,
                .key     = elem->key.val.data,
+               .tstamp  = nft_net_tstamp(net),
        };
 
        rcu_read_lock();
index 8b1a88a16e5c1f167921b25956473757b1258809..d9c1c467ea6848df6e1cb31e524427651497bd1f 100644 (file)
@@ -504,6 +504,7 @@ out:
  * @set:       nftables API set representation
  * @data:      Key data to be matched against existing elements
  * @genmask:   If set, check that element is active in given genmask
+ * @tstamp:    timestamp to check for expired elements
  *
  * This is essentially the same as the lookup function, except that it matches
  * key data against the uncommitted copy and doesn't use preallocated maps for
@@ -513,7 +514,8 @@ out:
  */
 static struct nft_pipapo_elem *pipapo_get(const struct net *net,
                                          const struct nft_set *set,
-                                         const u8 *data, u8 genmask)
+                                         const u8 *data, u8 genmask,
+                                         u64 tstamp)
 {
        struct nft_pipapo_elem *ret = ERR_PTR(-ENOENT);
        struct nft_pipapo *priv = nft_set_priv(set);
@@ -568,7 +570,7 @@ next_match:
                        goto out;
 
                if (last) {
-                       if (nft_set_elem_expired(&f->mt[b].e->ext))
+                       if (__nft_set_elem_expired(&f->mt[b].e->ext, tstamp))
                                goto next_match;
                        if ((genmask &&
                             !nft_set_elem_active(&f->mt[b].e->ext, genmask)))
@@ -605,7 +607,7 @@ static void *nft_pipapo_get(const struct net *net, const struct nft_set *set,
                            const struct nft_set_elem *elem, unsigned int flags)
 {
        return pipapo_get(net, set, (const u8 *)elem->key.val.data,
-                        nft_genmask_cur(net));
+                        nft_genmask_cur(net), get_jiffies_64());
 }
 
 /**
@@ -1199,6 +1201,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
        struct nft_pipapo *priv = nft_set_priv(set);
        struct nft_pipapo_match *m = priv->clone;
        u8 genmask = nft_genmask_next(net);
+       u64 tstamp = nft_net_tstamp(net);
        struct nft_pipapo_field *f;
        const u8 *start_p, *end_p;
        int i, bsize_max, err = 0;
@@ -1208,7 +1211,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
        else
                end = start;
 
-       dup = pipapo_get(net, set, start, genmask);
+       dup = pipapo_get(net, set, start, genmask, tstamp);
        if (!IS_ERR(dup)) {
                /* Check if we already have the same exact entry */
                const struct nft_data *dup_key, *dup_end;
@@ -1230,7 +1233,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
 
        if (PTR_ERR(dup) == -ENOENT) {
                /* Look for partially overlapping entries */
-               dup = pipapo_get(net, set, end, nft_genmask_next(net));
+               dup = pipapo_get(net, set, end, nft_genmask_next(net), tstamp);
        }
 
        if (PTR_ERR(dup) != -ENOENT) {
@@ -1583,6 +1586,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
        struct nft_set *set = (struct nft_set *) _set;
        struct nft_pipapo *priv = nft_set_priv(set);
        struct net *net = read_pnet(&set->net);
+       u64 tstamp = nft_net_tstamp(net);
        int rules_f0, first_rule = 0;
        struct nft_pipapo_elem *e;
        struct nft_trans_gc *gc;
@@ -1617,7 +1621,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
                /* synchronous gc never fails, there is no need to set on
                 * NFT_SET_ELEM_DEAD_BIT.
                 */
-               if (nft_set_elem_expired(&e->ext)) {
+               if (__nft_set_elem_expired(&e->ext, tstamp)) {
                        priv->dirty = true;
 
                        gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
@@ -1788,7 +1792,7 @@ static void *pipapo_deactivate(const struct net *net, const struct nft_set *set,
 {
        struct nft_pipapo_elem *e;
 
-       e = pipapo_get(net, set, data, nft_genmask_next(net));
+       e = pipapo_get(net, set, data, nft_genmask_next(net), nft_net_tstamp(net));
        if (IS_ERR(e))
                return NULL;
 
index 5bf5572e945cc3ed2e912b8dcf9f460a40086366..021d9e76129a5ed0b5913d6853a02b84da02ca12 100644 (file)
@@ -314,6 +314,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
        struct nft_rbtree *priv = nft_set_priv(set);
        u8 cur_genmask = nft_genmask_cur(net);
        u8 genmask = nft_genmask_next(net);
+       u64 tstamp = nft_net_tstamp(net);
        int d;
 
        /* Descend the tree to search for an existing element greater than the
@@ -361,7 +362,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
                /* perform garbage collection to avoid bogus overlap reports
                 * but skip new elements in this transaction.
                 */
-               if (nft_set_elem_expired(&rbe->ext) &&
+               if (__nft_set_elem_expired(&rbe->ext, tstamp) &&
                    nft_set_elem_active(&rbe->ext, cur_genmask)) {
                        const struct nft_rbtree_elem *removed_end;
 
@@ -548,6 +549,7 @@ static void *nft_rbtree_deactivate(const struct net *net,
        const struct rb_node *parent = priv->root.rb_node;
        struct nft_rbtree_elem *rbe, *this = elem->priv;
        u8 genmask = nft_genmask_next(net);
+       u64 tstamp = nft_net_tstamp(net);
        int d;
 
        while (parent != NULL) {
@@ -568,7 +570,7 @@ static void *nft_rbtree_deactivate(const struct net *net,
                                   nft_rbtree_interval_end(this)) {
                                parent = parent->rb_right;
                                continue;
-                       } else if (nft_set_elem_expired(&rbe->ext)) {
+                       } else if (__nft_set_elem_expired(&rbe->ext, tstamp)) {
                                break;
                        } else if (!nft_set_elem_active(&rbe->ext, genmask)) {
                                parent = parent->rb_left;