Skip to content

Commit 7395dfa

Browse files
committed
netfilter: nf_tables: use timestamp to check for set element timeout
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: c3e1b00 ("netfilter: nf_tables: add set element timeout support") Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 38ed1c7 commit 7395dfa

File tree

5 files changed

+42
-15
lines changed

5 files changed

+42
-15
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -808,10 +808,16 @@ static inline struct nft_set_elem_expr *nft_set_ext_expr(const struct nft_set_ex
808808
return nft_set_ext(ext, NFT_SET_EXT_EXPRESSIONS);
809809
}
810810

811-
static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
811+
static inline bool __nft_set_elem_expired(const struct nft_set_ext *ext,
812+
u64 tstamp)
812813
{
813814
return nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION) &&
814-
time_is_before_eq_jiffies64(*nft_set_ext_expiration(ext));
815+
time_after_eq64(tstamp, *nft_set_ext_expiration(ext));
816+
}
817+
818+
static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
819+
{
820+
return __nft_set_elem_expired(ext, get_jiffies_64());
815821
}
816822

817823
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
@@ -1779,6 +1785,7 @@ struct nftables_pernet {
17791785
struct list_head notify_list;
17801786
struct mutex commit_mutex;
17811787
u64 table_handle;
1788+
u64 tstamp;
17821789
unsigned int base_seq;
17831790
unsigned int gc_seq;
17841791
u8 validate_state;
@@ -1791,6 +1798,11 @@ static inline struct nftables_pernet *nft_pernet(const struct net *net)
17911798
return net_generic(net, nf_tables_net_id);
17921799
}
17931800

1801+
static inline u64 nft_net_tstamp(const struct net *net)
1802+
{
1803+
return nft_pernet(net)->tstamp;
1804+
}
1805+
17941806
#define __NFT_REDUCE_READONLY 1UL
17951807
#define NFT_REDUCE_READONLY (void *)__NFT_REDUCE_READONLY
17961808

net/netfilter/nf_tables_api.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9827,6 +9827,7 @@ struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
98279827
struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
98289828
{
98299829
struct nft_set_elem_catchall *catchall, *next;
9830+
u64 tstamp = nft_net_tstamp(gc->net);
98309831
const struct nft_set *set = gc->set;
98319832
struct nft_elem_priv *elem_priv;
98329833
struct nft_set_ext *ext;
@@ -9836,7 +9837,7 @@ struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
98369837
list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
98379838
ext = nft_set_elem_ext(set, catchall->elem);
98389839

9839-
if (!nft_set_elem_expired(ext))
9840+
if (!__nft_set_elem_expired(ext, tstamp))
98409841
continue;
98419842

98429843
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
@@ -10622,6 +10623,7 @@ static bool nf_tables_valid_genid(struct net *net, u32 genid)
1062210623
bool genid_ok;
1062310624

1062410625
mutex_lock(&nft_net->commit_mutex);
10626+
nft_net->tstamp = get_jiffies_64();
1062510627

1062610628
genid_ok = genid == 0 || nft_net->base_seq == genid;
1062710629
if (!genid_ok)

net/netfilter/nft_set_hash.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct nft_rhash_cmp_arg {
3636
const struct nft_set *set;
3737
const u32 *key;
3838
u8 genmask;
39+
u64 tstamp;
3940
};
4041

4142
static inline u32 nft_rhash_key(const void *data, u32 len, u32 seed)
@@ -62,7 +63,7 @@ static inline int nft_rhash_cmp(struct rhashtable_compare_arg *arg,
6263
return 1;
6364
if (nft_set_elem_is_dead(&he->ext))
6465
return 1;
65-
if (nft_set_elem_expired(&he->ext))
66+
if (__nft_set_elem_expired(&he->ext, x->tstamp))
6667
return 1;
6768
if (!nft_set_elem_active(&he->ext, x->genmask))
6869
return 1;
@@ -87,6 +88,7 @@ bool nft_rhash_lookup(const struct net *net, const struct nft_set *set,
8788
.genmask = nft_genmask_cur(net),
8889
.set = set,
8990
.key = key,
91+
.tstamp = get_jiffies_64(),
9092
};
9193

9294
he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -106,6 +108,7 @@ nft_rhash_get(const struct net *net, const struct nft_set *set,
106108
.genmask = nft_genmask_cur(net),
107109
.set = set,
108110
.key = elem->key.val.data,
111+
.tstamp = get_jiffies_64(),
109112
};
110113

111114
he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -131,6 +134,7 @@ static bool nft_rhash_update(struct nft_set *set, const u32 *key,
131134
.genmask = NFT_GENMASK_ANY,
132135
.set = set,
133136
.key = key,
137+
.tstamp = get_jiffies_64(),
134138
};
135139

136140
he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
@@ -175,6 +179,7 @@ static int nft_rhash_insert(const struct net *net, const struct nft_set *set,
175179
.genmask = nft_genmask_next(net),
176180
.set = set,
177181
.key = elem->key.val.data,
182+
.tstamp = nft_net_tstamp(net),
178183
};
179184
struct nft_rhash_elem *prev;
180185

@@ -216,6 +221,7 @@ nft_rhash_deactivate(const struct net *net, const struct nft_set *set,
216221
.genmask = nft_genmask_next(net),
217222
.set = set,
218223
.key = elem->key.val.data,
224+
.tstamp = nft_net_tstamp(net),
219225
};
220226

221227
rcu_read_lock();

net/netfilter/nft_set_pipapo.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ bool nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
504504
* @set: nftables API set representation
505505
* @data: Key data to be matched against existing elements
506506
* @genmask: If set, check that element is active in given genmask
507+
* @tstamp: timestamp to check for expired elements
507508
*
508509
* This is essentially the same as the lookup function, except that it matches
509510
* 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,
513514
*/
514515
static struct nft_pipapo_elem *pipapo_get(const struct net *net,
515516
const struct nft_set *set,
516-
const u8 *data, u8 genmask)
517+
const u8 *data, u8 genmask,
518+
u64 tstamp)
517519
{
518520
struct nft_pipapo_elem *ret = ERR_PTR(-ENOENT);
519521
struct nft_pipapo *priv = nft_set_priv(set);
@@ -566,7 +568,7 @@ static struct nft_pipapo_elem *pipapo_get(const struct net *net,
566568
goto out;
567569

568570
if (last) {
569-
if (nft_set_elem_expired(&f->mt[b].e->ext))
571+
if (__nft_set_elem_expired(&f->mt[b].e->ext, tstamp))
570572
goto next_match;
571573
if ((genmask &&
572574
!nft_set_elem_active(&f->mt[b].e->ext, genmask)))
@@ -606,7 +608,7 @@ nft_pipapo_get(const struct net *net, const struct nft_set *set,
606608
struct nft_pipapo_elem *e;
607609

608610
e = pipapo_get(net, set, (const u8 *)elem->key.val.data,
609-
nft_genmask_cur(net));
611+
nft_genmask_cur(net), get_jiffies_64());
610612
if (IS_ERR(e))
611613
return ERR_CAST(e);
612614

@@ -1173,6 +1175,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
11731175
struct nft_pipapo_match *m = priv->clone;
11741176
u8 genmask = nft_genmask_next(net);
11751177
struct nft_pipapo_elem *e, *dup;
1178+
u64 tstamp = nft_net_tstamp(net);
11761179
struct nft_pipapo_field *f;
11771180
const u8 *start_p, *end_p;
11781181
int i, bsize_max, err = 0;
@@ -1182,7 +1185,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
11821185
else
11831186
end = start;
11841187

1185-
dup = pipapo_get(net, set, start, genmask);
1188+
dup = pipapo_get(net, set, start, genmask, tstamp);
11861189
if (!IS_ERR(dup)) {
11871190
/* Check if we already have the same exact entry */
11881191
const struct nft_data *dup_key, *dup_end;
@@ -1204,7 +1207,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
12041207

12051208
if (PTR_ERR(dup) == -ENOENT) {
12061209
/* Look for partially overlapping entries */
1207-
dup = pipapo_get(net, set, end, nft_genmask_next(net));
1210+
dup = pipapo_get(net, set, end, nft_genmask_next(net), tstamp);
12081211
}
12091212

12101213
if (PTR_ERR(dup) != -ENOENT) {
@@ -1560,6 +1563,7 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
15601563
{
15611564
struct nft_pipapo *priv = nft_set_priv(set);
15621565
struct net *net = read_pnet(&set->net);
1566+
u64 tstamp = nft_net_tstamp(net);
15631567
int rules_f0, first_rule = 0;
15641568
struct nft_pipapo_elem *e;
15651569
struct nft_trans_gc *gc;
@@ -1594,7 +1598,7 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
15941598
/* synchronous gc never fails, there is no need to set on
15951599
* NFT_SET_ELEM_DEAD_BIT.
15961600
*/
1597-
if (nft_set_elem_expired(&e->ext)) {
1601+
if (__nft_set_elem_expired(&e->ext, tstamp)) {
15981602
priv->dirty = true;
15991603

16001604
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
@@ -1769,7 +1773,7 @@ static void *pipapo_deactivate(const struct net *net, const struct nft_set *set,
17691773
{
17701774
struct nft_pipapo_elem *e;
17711775

1772-
e = pipapo_get(net, set, data, nft_genmask_next(net));
1776+
e = pipapo_get(net, set, data, nft_genmask_next(net), nft_net_tstamp(net));
17731777
if (IS_ERR(e))
17741778
return NULL;
17751779

net/netfilter/nft_set_rbtree.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
313313
struct nft_rbtree *priv = nft_set_priv(set);
314314
u8 cur_genmask = nft_genmask_cur(net);
315315
u8 genmask = nft_genmask_next(net);
316+
u64 tstamp = nft_net_tstamp(net);
316317
int d;
317318

318319
/* Descend the tree to search for an existing element greater than the
@@ -360,7 +361,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
360361
/* perform garbage collection to avoid bogus overlap reports
361362
* but skip new elements in this transaction.
362363
*/
363-
if (nft_set_elem_expired(&rbe->ext) &&
364+
if (__nft_set_elem_expired(&rbe->ext, tstamp) &&
364365
nft_set_elem_active(&rbe->ext, cur_genmask)) {
365366
const struct nft_rbtree_elem *removed_end;
366367

@@ -551,6 +552,7 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,
551552
const struct nft_rbtree *priv = nft_set_priv(set);
552553
const struct rb_node *parent = priv->root.rb_node;
553554
u8 genmask = nft_genmask_next(net);
555+
u64 tstamp = nft_net_tstamp(net);
554556
int d;
555557

556558
while (parent != NULL) {
@@ -571,7 +573,7 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,
571573
nft_rbtree_interval_end(this)) {
572574
parent = parent->rb_right;
573575
continue;
574-
} else if (nft_set_elem_expired(&rbe->ext)) {
576+
} else if (__nft_set_elem_expired(&rbe->ext, tstamp)) {
575577
break;
576578
} else if (!nft_set_elem_active(&rbe->ext, genmask)) {
577579
parent = parent->rb_left;
@@ -624,9 +626,10 @@ static void nft_rbtree_gc(struct nft_set *set)
624626
{
625627
struct nft_rbtree *priv = nft_set_priv(set);
626628
struct nft_rbtree_elem *rbe, *rbe_end = NULL;
629+
struct net *net = read_pnet(&set->net);
630+
u64 tstamp = nft_net_tstamp(net);
627631
struct rb_node *node, *next;
628632
struct nft_trans_gc *gc;
629-
struct net *net;
630633

631634
set = nft_set_container_of(priv);
632635
net = read_pnet(&set->net);
@@ -648,7 +651,7 @@ static void nft_rbtree_gc(struct nft_set *set)
648651
rbe_end = rbe;
649652
continue;
650653
}
651-
if (!nft_set_elem_expired(&rbe->ext))
654+
if (!__nft_set_elem_expired(&rbe->ext, tstamp))
652655
continue;
653656

654657
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);

0 commit comments

Comments
 (0)