netfilter: nf_tables: fix race when create new element in dynset
Packets may race when create the new element in nft_hash_update:
CPU0 CPU1
lookup_fast - fail lookup_fast - fail
new - ok new - ok
insert - ok insert - fail(EEXIST)
So when race happened, we reuse the existing element. Otherwise,
these *racing* packets will not be handled properly.
Fixes: 22fe54d5fe
("netfilter: nf_tables: add support for dynamic set updates")
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
61f9e2924f
commit
dab45060a5
|
@ -98,7 +98,7 @@ static bool nft_hash_update(struct nft_set *set, const u32 *key,
|
||||||
const struct nft_set_ext **ext)
|
const struct nft_set_ext **ext)
|
||||||
{
|
{
|
||||||
struct nft_hash *priv = nft_set_priv(set);
|
struct nft_hash *priv = nft_set_priv(set);
|
||||||
struct nft_hash_elem *he;
|
struct nft_hash_elem *he, *prev;
|
||||||
struct nft_hash_cmp_arg arg = {
|
struct nft_hash_cmp_arg arg = {
|
||||||
.genmask = NFT_GENMASK_ANY,
|
.genmask = NFT_GENMASK_ANY,
|
||||||
.set = set,
|
.set = set,
|
||||||
|
@ -112,9 +112,18 @@ static bool nft_hash_update(struct nft_set *set, const u32 *key,
|
||||||
he = new(set, expr, regs);
|
he = new(set, expr, regs);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
goto err1;
|
goto err1;
|
||||||
if (rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node,
|
|
||||||
nft_hash_params))
|
prev = rhashtable_lookup_get_insert_key(&priv->ht, &arg, &he->node,
|
||||||
|
nft_hash_params);
|
||||||
|
if (IS_ERR(prev))
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
||||||
|
/* Another cpu may race to insert the element with the same key */
|
||||||
|
if (prev) {
|
||||||
|
nft_set_elem_destroy(set, he, true);
|
||||||
|
he = prev;
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
*ext = &he->ext;
|
*ext = &he->ext;
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in a new issue