]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 6.1
authorSasha Levin <sashal@kernel.org>
Fri, 28 Jun 2024 00:21:22 +0000 (20:21 -0400)
committerSasha Levin <sashal@kernel.org>
Fri, 28 Jun 2024 00:21:22 +0000 (20:21 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-6.1/netfilter-nf_tables-use-timestamp-to-check-for-set-e.patch [new file with mode: 0644]
queue-6.1/series

diff --git a/queue-6.1/netfilter-nf_tables-use-timestamp-to-check-for-set-e.patch b/queue-6.1/netfilter-nf_tables-use-timestamp-to-check-for-set-e.patch
new file mode 100644 (file)
index 0000000..6e6bdf7
--- /dev/null
@@ -0,0 +1,299 @@
+From 71dfebe3d83aa74a767ed8bf30eb2ac2d795788b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Jun 2024 01:53:13 +0200
+Subject: netfilter: nf_tables: use timestamp to check for set element timeout
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+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: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_tables.h | 16 ++++++++++++++--
+ net/netfilter/nf_tables_api.c     |  4 +++-
+ net/netfilter/nft_set_hash.c      |  8 +++++++-
+ net/netfilter/nft_set_pipapo.c    | 18 +++++++++++-------
+ net/netfilter/nft_set_rbtree.c    |  6 ++++--
+ 5 files changed, 39 insertions(+), 13 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index 2fa344cb66f60..964cf7578bd50 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -784,10 +784,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,
+@@ -1711,6 +1717,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;
+@@ -1723,6 +1730,11 @@ 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;
++}
++
+ #define __NFT_REDUCE_READONLY 1UL
+ #define NFT_REDUCE_READONLY   (void *)__NFT_REDUCE_READONLY
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index 1c4b7a8ec2cc6..e838a6617b0aa 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -9377,6 +9377,7 @@ struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
+ 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;
+@@ -9386,7 +9387,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);
+@@ -10138,6 +10139,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)
+diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c
+index 2013de934cef0..1fd3b413350dc 100644
+--- a/net/netfilter/nft_set_hash.c
++++ b/net/netfilter/nft_set_hash.c
+@@ -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();
+diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c
+index 2299ced939c47..a56ed216c2233 100644
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -504,6 +504,7 @@ bool nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
+  * @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 @@ bool nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
+  */
+ 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);
+@@ -566,7 +568,7 @@ static struct nft_pipapo_elem *pipapo_get(const struct net *net,
+                       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)))
+@@ -603,7 +605,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());
+ }
+ /**
+@@ -1197,6 +1199,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;
+@@ -1206,7 +1209,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;
+@@ -1228,7 +1231,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) {
+@@ -1581,6 +1584,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;
+@@ -1615,7 +1619,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);
+@@ -1786,7 +1790,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;
+diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
+index 5bf5572e945cc..021d9e76129a5 100644
+--- a/net/netfilter/nft_set_rbtree.c
++++ b/net/netfilter/nft_set_rbtree.c
+@@ -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;
+-- 
+2.43.0
+
index a5e19c20d23bc46b84d9184b73f4fcf3b275a89a..a11b5403bcb43d9124841cc9ba3fe6e804841846 100644 (file)
@@ -13,3 +13,4 @@ pinctrl-rockchip-fix-pinmux-reset-in-rockchip_pmx_se.patch
 mips-pci-lantiq-restore-reset-gpio-polarity.patch
 dt-bindings-i2c-drop-unneeded-quotes.patch
 dt-bindings-i2c-atmel-at91sam-correct-path-to-i2c-co.patch
+netfilter-nf_tables-use-timestamp-to-check-for-set-e.patch