Skip to content

Commit 4201f39

Browse files
committed
netfilter: nf_tables: set element timeout update support
Store new timeout and expiration in transaction object, use them to update elements from .commit path. Otherwise, discard update if .abort path is exercised. Use update_flags in the transaction to note whether the timeout, expiration, or both need to be updated. Annotate access to timeout extension now that it can be updated while lockless read access is possible. Reject timeout updates on elements with no timeout extension. Element transaction remains in the 96 bytes kmalloc slab on x86_64 after this update. This patch requires ("netfilter: nf_tables: use timestamp to check for set element timeout") to make sure an element does not expire while transaction is ongoing. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 8bfb74a commit 4201f39

File tree

3 files changed

+59
-6
lines changed

3 files changed

+59
-6
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ static inline bool __nft_set_elem_expired(const struct nft_set_ext *ext,
833833
u64 tstamp)
834834
{
835835
if (!nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) ||
836-
nft_set_ext_timeout(ext)->timeout == 0)
836+
READ_ONCE(nft_set_ext_timeout(ext)->timeout) == 0)
837837
return false;
838838

839839
return time_after_eq64(tstamp, READ_ONCE(nft_set_ext_timeout(ext)->expiration));
@@ -1749,10 +1749,18 @@ struct nft_trans_table {
17491749
#define nft_trans_table_update(trans) \
17501750
nft_trans_container_table(trans)->update
17511751

1752+
enum nft_trans_elem_flags {
1753+
NFT_TRANS_UPD_TIMEOUT = (1 << 0),
1754+
NFT_TRANS_UPD_EXPIRATION = (1 << 1),
1755+
};
1756+
17521757
struct nft_trans_elem {
17531758
struct nft_trans nft_trans;
17541759
struct nft_set *set;
17551760
struct nft_elem_priv *elem_priv;
1761+
u64 timeout;
1762+
u64 expiration;
1763+
u8 update_flags;
17561764
bool bound;
17571765
};
17581766

@@ -1762,6 +1770,12 @@ struct nft_trans_elem {
17621770
nft_trans_container_elem(trans)->set
17631771
#define nft_trans_elem_priv(trans) \
17641772
nft_trans_container_elem(trans)->elem_priv
1773+
#define nft_trans_elem_update_flags(trans) \
1774+
nft_trans_container_elem(trans)->update_flags
1775+
#define nft_trans_elem_timeout(trans) \
1776+
nft_trans_container_elem(trans)->timeout
1777+
#define nft_trans_elem_expiration(trans) \
1778+
nft_trans_container_elem(trans)->expiration
17651779
#define nft_trans_elem_set_bound(trans) \
17661780
nft_trans_container_elem(trans)->bound
17671781

net/netfilter/nf_tables_api.c

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5815,7 +5815,7 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
58155815
goto nla_put_failure;
58165816

58175817
if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT)) {
5818-
u64 timeout = nft_set_ext_timeout(ext)->timeout;
5818+
u64 timeout = READ_ONCE(nft_set_ext_timeout(ext)->timeout);
58195819
u64 set_timeout = READ_ONCE(set->timeout);
58205820
__be64 msecs = 0;
58215821

@@ -6852,6 +6852,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
68526852
struct nft_data_desc desc;
68536853
enum nft_registers dreg;
68546854
struct nft_trans *trans;
6855+
u8 update_flags;
68556856
u64 expiration;
68566857
u64 timeout;
68576858
int err, i;
@@ -7163,8 +7164,30 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
71637164
nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF) &&
71647165
*nft_set_ext_obj(ext) != *nft_set_ext_obj(ext2)))
71657166
goto err_element_clash;
7166-
else if (!(nlmsg_flags & NLM_F_EXCL))
7167+
else if (!(nlmsg_flags & NLM_F_EXCL)) {
71677168
err = 0;
7169+
if (nft_set_ext_exists(ext2, NFT_SET_EXT_TIMEOUT)) {
7170+
update_flags = 0;
7171+
if (timeout != nft_set_ext_timeout(ext2)->timeout) {
7172+
nft_trans_elem_timeout(trans) = timeout;
7173+
if (expiration == 0)
7174+
expiration = timeout;
7175+
7176+
update_flags |= NFT_TRANS_UPD_TIMEOUT;
7177+
}
7178+
if (expiration) {
7179+
nft_trans_elem_expiration(trans) = expiration;
7180+
update_flags |= NFT_TRANS_UPD_EXPIRATION;
7181+
}
7182+
7183+
if (update_flags) {
7184+
nft_trans_elem_priv(trans) = elem_priv;
7185+
nft_trans_elem_update_flags(trans) = update_flags;
7186+
nft_trans_commit_list_add_tail(ctx->net, trans);
7187+
goto err_elem_free;
7188+
}
7189+
}
7190+
}
71687191
} else if (err == -ENOTEMPTY) {
71697192
/* ENOTEMPTY reports overlapping between this element
71707193
* and an existing one.
@@ -10489,7 +10512,22 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
1048910512
case NFT_MSG_NEWSETELEM:
1049010513
te = nft_trans_container_elem(trans);
1049110514

10492-
nft_setelem_activate(net, te->set, te->elem_priv);
10515+
if (te->update_flags) {
10516+
const struct nft_set_ext *ext =
10517+
nft_set_elem_ext(te->set, te->elem_priv);
10518+
10519+
if (te->update_flags & NFT_TRANS_UPD_TIMEOUT) {
10520+
WRITE_ONCE(nft_set_ext_timeout(ext)->timeout,
10521+
te->timeout);
10522+
}
10523+
if (te->update_flags & NFT_TRANS_UPD_EXPIRATION) {
10524+
WRITE_ONCE(nft_set_ext_timeout(ext)->expiration,
10525+
get_jiffies_64() + te->expiration);
10526+
}
10527+
} else {
10528+
nft_setelem_activate(net, te->set, te->elem_priv);
10529+
}
10530+
1049310531
nf_tables_setelem_notify(&ctx, te->set,
1049410532
te->elem_priv,
1049510533
NFT_MSG_NEWSETELEM);
@@ -10789,7 +10827,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
1078910827
nft_trans_destroy(trans);
1079010828
break;
1079110829
case NFT_MSG_NEWSETELEM:
10792-
if (nft_trans_elem_set_bound(trans)) {
10830+
if (nft_trans_elem_update_flags(trans) ||
10831+
nft_trans_elem_set_bound(trans)) {
1079310832
nft_trans_destroy(trans);
1079410833
break;
1079510834
}

net/netfilter/nft_dynset.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ void nft_dynset_eval(const struct nft_expr *expr,
9595
expr, regs, &ext)) {
9696
if (priv->op == NFT_DYNSET_OP_UPDATE &&
9797
nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
98-
nft_set_ext_timeout(ext)->timeout != 0) {
98+
READ_ONCE(nft_set_ext_timeout(ext)->timeout) != 0) {
9999
timeout = priv->timeout ? : READ_ONCE(set->timeout);
100100
WRITE_ONCE(nft_set_ext_timeout(ext)->expiration, get_jiffies_64() + timeout);
101101
}

0 commit comments

Comments
 (0)