Commit 3dd0673a authored by Patrick McHardy's avatar Patrick McHardy Committed by Pablo Neira Ayuso

netfilter: nf_tables: prepare set element accounting for async updates

Use atomic operations for the element count to avoid races with async
updates.

To properly handle the transactional semantics during netlink updates,
deleted but not yet committed elements are accounted for seperately and
are treated as being already removed. This means for the duration of
a netlink transaction, the limit might be exceeded by the amount of
elements deleted. Set implementations must be prepared to handle this.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 4a8678ef
...@@ -258,6 +258,7 @@ void nft_unregister_set(struct nft_set_ops *ops); ...@@ -258,6 +258,7 @@ void nft_unregister_set(struct nft_set_ops *ops);
* @dtype: data type (verdict or numeric type defined by userspace) * @dtype: data type (verdict or numeric type defined by userspace)
* @size: maximum set size * @size: maximum set size
* @nelems: number of elements * @nelems: number of elements
* @ndeact: number of deactivated elements queued for removal
* @timeout: default timeout value in msecs * @timeout: default timeout value in msecs
* @gc_int: garbage collection interval in msecs * @gc_int: garbage collection interval in msecs
* @policy: set parameterization (see enum nft_set_policies) * @policy: set parameterization (see enum nft_set_policies)
...@@ -275,7 +276,8 @@ struct nft_set { ...@@ -275,7 +276,8 @@ struct nft_set {
u32 ktype; u32 ktype;
u32 dtype; u32 dtype;
u32 size; u32 size;
u32 nelems; atomic_t nelems;
u32 ndeact;
u64 timeout; u64 timeout;
u32 gc_int; u32 gc_int;
u16 policy; u16 policy;
......
...@@ -3238,9 +3238,6 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -3238,9 +3238,6 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
u32 flags; u32 flags;
int err; int err;
if (set->size && set->nelems == set->size)
return -ENFILE;
err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
nft_set_elem_policy); nft_set_elem_policy);
if (err < 0) if (err < 0)
...@@ -3391,11 +3388,15 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, ...@@ -3391,11 +3388,15 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
return -EBUSY; return -EBUSY;
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
if (set->size &&
!atomic_add_unless(&set->nelems, 1, set->size + set->ndeact))
return -ENFILE;
err = nft_add_set_elem(&ctx, set, attr); err = nft_add_set_elem(&ctx, set, attr);
if (err < 0) if (err < 0) {
atomic_dec(&set->nelems);
break; break;
}
set->nelems++;
} }
return err; return err;
} }
...@@ -3477,7 +3478,7 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, ...@@ -3477,7 +3478,7 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
if (err < 0) if (err < 0)
break; break;
set->nelems--; set->ndeact++;
} }
return err; return err;
} }
...@@ -3810,6 +3811,8 @@ static int nf_tables_commit(struct sk_buff *skb) ...@@ -3810,6 +3811,8 @@ static int nf_tables_commit(struct sk_buff *skb)
&te->elem, &te->elem,
NFT_MSG_DELSETELEM, 0); NFT_MSG_DELSETELEM, 0);
te->set->ops->remove(te->set, &te->elem); te->set->ops->remove(te->set, &te->elem);
atomic_dec(&te->set->nelems);
te->set->ndeact--;
break; break;
} }
} }
...@@ -3913,16 +3916,16 @@ static int nf_tables_abort(struct sk_buff *skb) ...@@ -3913,16 +3916,16 @@ static int nf_tables_abort(struct sk_buff *skb)
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
case NFT_MSG_NEWSETELEM: case NFT_MSG_NEWSETELEM:
nft_trans_elem_set(trans)->nelems--;
te = (struct nft_trans_elem *)trans->data; te = (struct nft_trans_elem *)trans->data;
te->set->ops->remove(te->set, &te->elem); te->set->ops->remove(te->set, &te->elem);
atomic_dec(&te->set->nelems);
break; break;
case NFT_MSG_DELSETELEM: case NFT_MSG_DELSETELEM:
te = (struct nft_trans_elem *)trans->data; te = (struct nft_trans_elem *)trans->data;
nft_trans_elem_set(trans)->nelems++;
te->set->ops->activate(te->set, &te->elem); te->set->ops->activate(te->set, &te->elem);
te->set->ndeact--;
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
......
...@@ -203,7 +203,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, ...@@ -203,7 +203,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
static void nft_hash_gc(struct work_struct *work) static void nft_hash_gc(struct work_struct *work)
{ {
const struct nft_set *set; struct nft_set *set;
struct nft_hash_elem *he; struct nft_hash_elem *he;
struct nft_hash *priv; struct nft_hash *priv;
struct nft_set_gc_batch *gcb = NULL; struct nft_set_gc_batch *gcb = NULL;
...@@ -237,6 +237,7 @@ static void nft_hash_gc(struct work_struct *work) ...@@ -237,6 +237,7 @@ static void nft_hash_gc(struct work_struct *work)
if (gcb == NULL) if (gcb == NULL)
goto out; goto out;
rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params); rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params);
atomic_dec(&set->nelems);
nft_set_gc_batch_add(gcb, he); nft_set_gc_batch_add(gcb, he);
} }
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment