Skip to content

Commit

Permalink
netfilter: nf_tables: set element timeout update support
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
ummakynes committed Sep 3, 2024
1 parent 8bfb74a commit 4201f39
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 6 deletions.
16 changes: 15 additions & 1 deletion include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ static inline bool __nft_set_elem_expired(const struct nft_set_ext *ext,
u64 tstamp)
{
if (!nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) ||
nft_set_ext_timeout(ext)->timeout == 0)
READ_ONCE(nft_set_ext_timeout(ext)->timeout) == 0)
return false;

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

enum nft_trans_elem_flags {
NFT_TRANS_UPD_TIMEOUT = (1 << 0),
NFT_TRANS_UPD_EXPIRATION = (1 << 1),
};

struct nft_trans_elem {
struct nft_trans nft_trans;
struct nft_set *set;
struct nft_elem_priv *elem_priv;
u64 timeout;
u64 expiration;
u8 update_flags;
bool bound;
};

Expand All @@ -1762,6 +1770,12 @@ struct nft_trans_elem {
nft_trans_container_elem(trans)->set
#define nft_trans_elem_priv(trans) \
nft_trans_container_elem(trans)->elem_priv
#define nft_trans_elem_update_flags(trans) \
nft_trans_container_elem(trans)->update_flags
#define nft_trans_elem_timeout(trans) \
nft_trans_container_elem(trans)->timeout
#define nft_trans_elem_expiration(trans) \
nft_trans_container_elem(trans)->expiration
#define nft_trans_elem_set_bound(trans) \
nft_trans_container_elem(trans)->bound

Expand Down
47 changes: 43 additions & 4 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -5815,7 +5815,7 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
goto nla_put_failure;

if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT)) {
u64 timeout = nft_set_ext_timeout(ext)->timeout;
u64 timeout = READ_ONCE(nft_set_ext_timeout(ext)->timeout);
u64 set_timeout = READ_ONCE(set->timeout);
__be64 msecs = 0;

Expand Down Expand Up @@ -6852,6 +6852,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
struct nft_data_desc desc;
enum nft_registers dreg;
struct nft_trans *trans;
u8 update_flags;
u64 expiration;
u64 timeout;
int err, i;
Expand Down Expand Up @@ -7163,8 +7164,30 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF) &&
*nft_set_ext_obj(ext) != *nft_set_ext_obj(ext2)))
goto err_element_clash;
else if (!(nlmsg_flags & NLM_F_EXCL))
else if (!(nlmsg_flags & NLM_F_EXCL)) {
err = 0;
if (nft_set_ext_exists(ext2, NFT_SET_EXT_TIMEOUT)) {
update_flags = 0;
if (timeout != nft_set_ext_timeout(ext2)->timeout) {
nft_trans_elem_timeout(trans) = timeout;
if (expiration == 0)
expiration = timeout;

update_flags |= NFT_TRANS_UPD_TIMEOUT;
}
if (expiration) {
nft_trans_elem_expiration(trans) = expiration;
update_flags |= NFT_TRANS_UPD_EXPIRATION;
}

if (update_flags) {
nft_trans_elem_priv(trans) = elem_priv;
nft_trans_elem_update_flags(trans) = update_flags;
nft_trans_commit_list_add_tail(ctx->net, trans);
goto err_elem_free;
}
}
}
} else if (err == -ENOTEMPTY) {
/* ENOTEMPTY reports overlapping between this element
* and an existing one.
Expand Down Expand Up @@ -10489,7 +10512,22 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
case NFT_MSG_NEWSETELEM:
te = nft_trans_container_elem(trans);

nft_setelem_activate(net, te->set, te->elem_priv);
if (te->update_flags) {
const struct nft_set_ext *ext =
nft_set_elem_ext(te->set, te->elem_priv);

if (te->update_flags & NFT_TRANS_UPD_TIMEOUT) {
WRITE_ONCE(nft_set_ext_timeout(ext)->timeout,
te->timeout);
}
if (te->update_flags & NFT_TRANS_UPD_EXPIRATION) {
WRITE_ONCE(nft_set_ext_timeout(ext)->expiration,
get_jiffies_64() + te->expiration);
}
} else {
nft_setelem_activate(net, te->set, te->elem_priv);
}

nf_tables_setelem_notify(&ctx, te->set,
te->elem_priv,
NFT_MSG_NEWSETELEM);
Expand Down Expand Up @@ -10789,7 +10827,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWSETELEM:
if (nft_trans_elem_set_bound(trans)) {
if (nft_trans_elem_update_flags(trans) ||
nft_trans_elem_set_bound(trans)) {
nft_trans_destroy(trans);
break;
}
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/nft_dynset.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void nft_dynset_eval(const struct nft_expr *expr,
expr, regs, &ext)) {
if (priv->op == NFT_DYNSET_OP_UPDATE &&
nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
nft_set_ext_timeout(ext)->timeout != 0) {
READ_ONCE(nft_set_ext_timeout(ext)->timeout) != 0) {
timeout = priv->timeout ? : READ_ONCE(set->timeout);
WRITE_ONCE(nft_set_ext_timeout(ext)->expiration, get_jiffies_64() + timeout);
}
Expand Down

0 comments on commit 4201f39

Please sign in to comment.