Skip to content

Commit

Permalink
netfilter: nf_tables: honor set timeout and garbage collection updates
Browse files Browse the repository at this point in the history
[ Upstream commit 123b996 ]

Set timeout and garbage collection interval updates are ignored on
updates. Add transaction to update global set element timeout and
garbage collection interval.

Fixes: 9651851 ("netfilter: add nftables")
Suggested-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
ummakynes authored and gregkh committed Jan 12, 2023
1 parent 49677ea commit ac95cda
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 19 deletions.
13 changes: 12 additions & 1 deletion include/net/netfilter/nf_tables.h
Expand Up @@ -562,7 +562,9 @@ void *nft_set_catchall_gc(const struct nft_set *set);

static inline unsigned long nft_set_gc_interval(const struct nft_set *set)
{
return set->gc_int ? msecs_to_jiffies(set->gc_int) : HZ;
u32 gc_int = READ_ONCE(set->gc_int);

return gc_int ? msecs_to_jiffies(gc_int) : HZ;
}

/**
Expand Down Expand Up @@ -1511,6 +1513,9 @@ struct nft_trans_rule {
struct nft_trans_set {
struct nft_set *set;
u32 set_id;
u32 gc_int;
u64 timeout;
bool update;
bool bound;
};

Expand All @@ -1520,6 +1525,12 @@ struct nft_trans_set {
(((struct nft_trans_set *)trans->data)->set_id)
#define nft_trans_set_bound(trans) \
(((struct nft_trans_set *)trans->data)->bound)
#define nft_trans_set_update(trans) \
(((struct nft_trans_set *)trans->data)->update)
#define nft_trans_set_timeout(trans) \
(((struct nft_trans_set *)trans->data)->timeout)
#define nft_trans_set_gc_int(trans) \
(((struct nft_trans_set *)trans->data)->gc_int)

struct nft_trans_chain {
bool update;
Expand Down
63 changes: 45 additions & 18 deletions net/netfilter/nf_tables_api.c
Expand Up @@ -465,26 +465,38 @@ static int nft_delrule_by_chain(struct nft_ctx *ctx)
return 0;
}

static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
struct nft_set *set)
static int __nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
struct nft_set *set,
const struct nft_set_desc *desc)
{
struct nft_trans *trans;

trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
if (trans == NULL)
return -ENOMEM;

if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] && !desc) {
nft_trans_set_id(trans) =
ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
nft_activate_next(ctx->net, set);
}
nft_trans_set(trans) = set;
if (desc) {
nft_trans_set_update(trans) = true;
nft_trans_set_gc_int(trans) = desc->gc_int;
nft_trans_set_timeout(trans) = desc->timeout;
}
nft_trans_commit_list_add_tail(ctx->net, trans);

return 0;
}

static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
struct nft_set *set)
{
return __nft_trans_set_add(ctx, msg_type, set, NULL);
}

static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set)
{
int err;
Expand Down Expand Up @@ -3899,8 +3911,10 @@ static int nf_tables_fill_set_concat(struct sk_buff *skb,
static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
const struct nft_set *set, u16 event, u16 flags)
{
struct nlmsghdr *nlh;
u64 timeout = READ_ONCE(set->timeout);
u32 gc_int = READ_ONCE(set->gc_int);
u32 portid = ctx->portid;
struct nlmsghdr *nlh;
struct nlattr *nest;
u32 seq = ctx->seq;
int i;
Expand Down Expand Up @@ -3936,13 +3950,13 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
nla_put_be32(skb, NFTA_SET_OBJ_TYPE, htonl(set->objtype)))
goto nla_put_failure;

if (set->timeout &&
if (timeout &&
nla_put_be64(skb, NFTA_SET_TIMEOUT,
nf_jiffies64_to_msecs(set->timeout),
nf_jiffies64_to_msecs(timeout),
NFTA_SET_PAD))
goto nla_put_failure;
if (set->gc_int &&
nla_put_be32(skb, NFTA_SET_GC_INTERVAL, htonl(set->gc_int)))
if (gc_int &&
nla_put_be32(skb, NFTA_SET_GC_INTERVAL, htonl(gc_int)))
goto nla_put_failure;

if (set->policy != NFT_SET_POL_PERFORMANCE) {
Expand Down Expand Up @@ -4487,7 +4501,10 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
for (i = 0; i < num_exprs; i++)
nft_expr_destroy(&ctx, exprs[i]);

return err;
if (err < 0)
return err;

return __nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set, &desc);
}

if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
Expand Down Expand Up @@ -5877,7 +5894,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return err;
} else if (set->flags & NFT_SET_TIMEOUT &&
!(flags & NFT_SET_ELEM_INTERVAL_END)) {
timeout = set->timeout;
timeout = READ_ONCE(set->timeout);
}

expiration = 0;
Expand Down Expand Up @@ -5978,7 +5995,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (err < 0)
goto err_parse_key_end;

if (timeout != set->timeout) {
if (timeout != READ_ONCE(set->timeout)) {
err = nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
if (err < 0)
goto err_parse_key_end;
Expand Down Expand Up @@ -8833,14 +8850,20 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
break;
case NFT_MSG_NEWSET:
nft_clear(net, nft_trans_set(trans));
/* This avoids hitting -EBUSY when deleting the table
* from the transaction.
*/
if (nft_set_is_anonymous(nft_trans_set(trans)) &&
!list_empty(&nft_trans_set(trans)->bindings))
trans->ctx.table->use--;
if (nft_trans_set_update(trans)) {
struct nft_set *set = nft_trans_set(trans);

WRITE_ONCE(set->timeout, nft_trans_set_timeout(trans));
WRITE_ONCE(set->gc_int, nft_trans_set_gc_int(trans));
} else {
nft_clear(net, nft_trans_set(trans));
/* This avoids hitting -EBUSY when deleting the table
* from the transaction.
*/
if (nft_set_is_anonymous(nft_trans_set(trans)) &&
!list_empty(&nft_trans_set(trans)->bindings))
trans->ctx.table->use--;
}
nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
NFT_MSG_NEWSET, GFP_KERNEL);
nft_trans_destroy(trans);
Expand Down Expand Up @@ -9062,6 +9085,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWSET:
if (nft_trans_set_update(trans)) {
nft_trans_destroy(trans);
break;
}
trans->ctx.table->use--;
if (nft_trans_set_bound(trans)) {
nft_trans_destroy(trans);
Expand Down

0 comments on commit ac95cda

Please sign in to comment.