summaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2024-09-03 01:09:58 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2024-09-03 18:19:44 +0200
commit4201f3938914d8df3c761754b9726770c4225d66 (patch)
tree50dfd475b57541f94aaf179cdc228a094f6e74b6 /net/netfilter
parent8bfb74ae12fa4cd3c9b49bb5913610b5709bffd7 (diff)
downloadlinux-4201f3938914d8df3c761754b9726770c4225d66.tar.gz
linux-4201f3938914d8df3c761754b9726770c4225d66.tar.bz2
linux-4201f3938914d8df3c761754b9726770c4225d66.zip
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 <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/nf_tables_api.c47
-rw-r--r--net/netfilter/nft_dynset.c2
2 files changed, 44 insertions, 5 deletions
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index ed85b10edb32..57259b5f3ef5 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -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;
@@ -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;
@@ -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.
@@ -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);
@@ -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;
}
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 6a10305de24b..88922e0e8e83 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -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);
}