Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8dfda79 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso Committed by Greg Kroah-Hartman
Browse files

netfilter: nf_tables: use timestamp to check for set element timeout



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.

[ NB: rbtree GC updates has been excluded because GC is asynchronous. ]

Fixes: c3e1b005 ("netfilter: nf_tables: add set element timeout support")
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 61fbbac2
Loading
Loading
Loading
Loading
+19 −2
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/rhashtable.h>
#include <net/netfilter/nf_flow_table.h>
#include <net/netlink.h>
#include <net/netns/generic.h>

#define NFT_JUMP_STACK_SIZE	16

@@ -636,10 +637,16 @@ static inline struct nft_expr *nft_set_ext_expr(const struct nft_set_ext *ext)
	return nft_set_ext(ext, NFT_SET_EXT_EXPR);
}

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,
@@ -1423,11 +1430,21 @@ struct nftables_pernet {
	struct list_head	module_list;
	struct list_head	notify_list;
	struct mutex		commit_mutex;
	u64			tstamp;
	unsigned int		base_seq;
	u8			validate_state;
	unsigned int		gc_seq;
};

extern unsigned int nf_tables_net_id;

static inline u64 nft_net_tstamp(const struct net *net)
{
	struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id);

	return nft_net->tstamp;
}

int nf_msecs_to_jiffies64(const struct nlattr *nla, u64 *result);
__be64 nf_jiffies64_to_msecs(u64 input);

+1 −0
Original line number Diff line number Diff line
@@ -7365,6 +7365,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)
+7 −1
Original line number Diff line number Diff line
@@ -41,6 +41,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)
@@ -67,7 +68,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;
@@ -91,6 +92,7 @@ static 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_fast(&priv->ht, &arg, nft_rhash_params);
@@ -109,6 +111,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_fast(&priv->ht, &arg, nft_rhash_params);
@@ -132,6 +135,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_fast(&priv->ht, &arg, nft_rhash_params);
@@ -175,6 +179,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;

@@ -217,6 +222,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();
+4 −2
Original line number Diff line number Diff line
@@ -318,6 +318,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, err;

	/* Descend the tree to search for an existing element greater than the
@@ -365,7 +366,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)) {
			err = nft_rbtree_gc_elem(set, priv, rbe);
			if (err < 0)
@@ -540,6 +541,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) {
@@ -560,7 +562,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;