Skip to content

Commit c016c7e

Browse files
committed
netfilter: nf_tables: honor NLM_F_EXCL flag in set element insertion
If the NLM_F_EXCL flag is set, then new elements that clash with an existing one return EEXIST. In case you try to add an element whose data area differs from what we have, then this returns EBUSY. If no flag is specified at all, then this returns success to userspace. This patch also update the set insert operation so we can fetch the existing element that clashes with the one you want to add, we need this to make sure the element data doesn't differ. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 5ca8cc5 commit c016c7e

File tree

4 files changed

+38
-14
lines changed

4 files changed

+38
-14
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ struct nft_set_ops {
251251

252252
int (*insert)(const struct net *net,
253253
const struct nft_set *set,
254-
const struct nft_set_elem *elem);
254+
const struct nft_set_elem *elem,
255+
struct nft_set_ext **ext);
255256
void (*activate)(const struct net *net,
256257
const struct nft_set *set,
257258
const struct nft_set_elem *elem);

net/netfilter/nf_tables_api.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3483,12 +3483,12 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
34833483
}
34843484

34853485
static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
3486-
const struct nlattr *attr)
3486+
const struct nlattr *attr, u32 nlmsg_flags)
34873487
{
34883488
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
34893489
struct nft_data_desc d1, d2;
34903490
struct nft_set_ext_tmpl tmpl;
3491-
struct nft_set_ext *ext;
3491+
struct nft_set_ext *ext, *ext2;
34923492
struct nft_set_elem elem;
34933493
struct nft_set_binding *binding;
34943494
struct nft_userdata *udata;
@@ -3615,9 +3615,19 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
36153615
goto err4;
36163616

36173617
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
3618-
err = set->ops->insert(ctx->net, set, &elem);
3619-
if (err < 0)
3618+
err = set->ops->insert(ctx->net, set, &elem, &ext2);
3619+
if (err) {
3620+
if (err == -EEXIST) {
3621+
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
3622+
nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) &&
3623+
memcmp(nft_set_ext_data(ext),
3624+
nft_set_ext_data(ext2), set->dlen) != 0)
3625+
err = -EBUSY;
3626+
else if (!(nlmsg_flags & NLM_F_EXCL))
3627+
err = 0;
3628+
}
36203629
goto err5;
3630+
}
36213631

36223632
nft_trans_elem(trans) = elem;
36233633
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
@@ -3673,7 +3683,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
36733683
!atomic_add_unless(&set->nelems, 1, set->size + set->ndeact))
36743684
return -ENFILE;
36753685

3676-
err = nft_add_set_elem(&ctx, set, attr);
3686+
err = nft_add_set_elem(&ctx, set, attr, nlh->nlmsg_flags);
36773687
if (err < 0) {
36783688
atomic_dec(&set->nelems);
36793689
break;

net/netfilter/nft_set_hash.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ static bool nft_hash_update(struct nft_set *set, const u32 *key,
126126
}
127127

128128
static int nft_hash_insert(const struct net *net, const struct nft_set *set,
129-
const struct nft_set_elem *elem)
129+
const struct nft_set_elem *elem,
130+
struct nft_set_ext **ext)
130131
{
131132
struct nft_hash *priv = nft_set_priv(set);
132133
struct nft_hash_elem *he = elem->priv;
@@ -135,9 +136,17 @@ static int nft_hash_insert(const struct net *net, const struct nft_set *set,
135136
.set = set,
136137
.key = elem->key.val.data,
137138
};
138-
139-
return rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node,
140-
nft_hash_params);
139+
struct nft_hash_elem *prev;
140+
141+
prev = rhashtable_lookup_get_insert_key(&priv->ht, &arg, &he->node,
142+
nft_hash_params);
143+
if (IS_ERR(prev))
144+
return PTR_ERR(prev);
145+
if (prev) {
146+
*ext = &prev->ext;
147+
return -EEXIST;
148+
}
149+
return 0;
141150
}
142151

143152
static void nft_hash_activate(const struct net *net, const struct nft_set *set,

net/netfilter/nft_set_rbtree.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set,
9494
}
9595

9696
static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
97-
struct nft_rbtree_elem *new)
97+
struct nft_rbtree_elem *new,
98+
struct nft_set_ext **ext)
9899
{
99100
struct nft_rbtree *priv = nft_set_priv(set);
100101
u8 genmask = nft_genmask_next(net);
@@ -122,8 +123,10 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
122123
else if (!nft_rbtree_interval_end(rbe) &&
123124
nft_rbtree_interval_end(new))
124125
p = &parent->rb_right;
125-
else
126+
else {
127+
*ext = &rbe->ext;
126128
return -EEXIST;
129+
}
127130
}
128131
}
129132
}
@@ -133,13 +136,14 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
133136
}
134137

135138
static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,
136-
const struct nft_set_elem *elem)
139+
const struct nft_set_elem *elem,
140+
struct nft_set_ext **ext)
137141
{
138142
struct nft_rbtree_elem *rbe = elem->priv;
139143
int err;
140144

141145
spin_lock_bh(&nft_rbtree_lock);
142-
err = __nft_rbtree_insert(net, set, rbe);
146+
err = __nft_rbtree_insert(net, set, rbe, ext);
143147
spin_unlock_bh(&nft_rbtree_lock);
144148

145149
return err;

0 commit comments

Comments
 (0)