Skip to content

Commit

Permalink
netfilter: nf_tables: convert hash and rbtree to set extensions
Browse files Browse the repository at this point in the history
The set implementations' private struct will only contain the elements
needed to maintain the search structure, all other elements are moved
to the set extensions.

Element allocation and initialization is performed centrally by
nf_tables_api instead of by the different set implementations'
->insert() functions. A new "elemsize" member in the set ops specifies
the amount of memory to reserve for internal usage. Destruction
will also be moved out of the set implementations by a following patch.

Except for element allocation, the patch is a simple conversion to
using data from the extension area.

Signed-off-by: Patrick McHardy <[email protected]>
Signed-off-by: Pablo Neira Ayuso <[email protected]>
  • Loading branch information
kaber authored and ummakynes committed Mar 25, 2015
1 parent 3ac4c07 commit fe2811e
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 121 deletions.
14 changes: 10 additions & 4 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,15 @@ struct nft_userdata {
*
* @cookie: implementation specific element cookie
* @key: element key
* @data: element data (maps only)
* @flags: element flags (end of interval)
* @priv: element private data and extensions
*
* The cookie can be used to store a handle to the element for subsequent
* removal.
*/
struct nft_set_elem {
void *cookie;
struct nft_data key;
struct nft_data data;
u32 flags;
void *priv;
};

struct nft_set;
Expand Down Expand Up @@ -214,6 +212,7 @@ struct nft_set_estimate {
* @destroy: destroy private data of set instance
* @list: nf_tables_set_ops list node
* @owner: module reference
* @elemsize: element private size
* @features: features supported by the implementation
*/
struct nft_set_ops {
Expand Down Expand Up @@ -241,6 +240,7 @@ struct nft_set_ops {

struct list_head list;
struct module *owner;
unsigned int elemsize;
u32 features;
};

Expand Down Expand Up @@ -417,6 +417,12 @@ static inline u8 *nft_set_ext_flags(const struct nft_set_ext *ext)
return nft_set_ext(ext, NFT_SET_EXT_FLAGS);
}

static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}

/**
* struct nft_expr_type - nf_tables expression type
*
Expand Down
119 changes: 89 additions & 30 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -2771,10 +2771,11 @@ static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
const struct nft_set_iter *iter,
const struct nft_set_elem *elem)
{
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
enum nft_registers dreg;

dreg = nft_type_to_reg(set->dtype);
return nft_validate_data_load(ctx, dreg, &elem->data,
return nft_validate_data_load(ctx, dreg, nft_set_ext_data(ext),
set->dtype == NFT_DATA_VERDICT ?
NFT_DATA_VERDICT : NFT_DATA_VALUE);
}
Expand Down Expand Up @@ -2889,27 +2890,28 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
const struct nft_set *set,
const struct nft_set_elem *elem)
{
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
unsigned char *b = skb_tail_pointer(skb);
struct nlattr *nest;

nest = nla_nest_start(skb, NFTA_LIST_ELEM);
if (nest == NULL)
goto nla_put_failure;

if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE,
set->klen) < 0)
if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, nft_set_ext_key(ext),
NFT_DATA_VALUE, set->klen) < 0)
goto nla_put_failure;

if (set->flags & NFT_SET_MAP &&
!(elem->flags & NFT_SET_ELEM_INTERVAL_END) &&
nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data,
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
nft_data_dump(skb, NFTA_SET_ELEM_DATA, nft_set_ext_data(ext),
set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
set->dlen) < 0)
goto nla_put_failure;

if (elem->flags != 0)
if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags)))
goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
nla_put_be32(skb, NFTA_SET_ELEM_FLAGS,
htonl(*nft_set_ext_flags(ext))))
goto nla_put_failure;

nla_nest_end(skb, nest);
return 0;
Expand Down Expand Up @@ -3130,15 +3132,42 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
return trans;
}

static void *nft_set_elem_init(const struct nft_set *set,
const struct nft_set_ext_tmpl *tmpl,
const struct nft_data *key,
const struct nft_data *data,
gfp_t gfp)
{
struct nft_set_ext *ext;
void *elem;

elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);
if (elem == NULL)
return NULL;

ext = nft_set_elem_ext(set, elem);
nft_set_ext_init(ext, tmpl);

memcpy(nft_set_ext_key(ext), key, set->klen);
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
memcpy(nft_set_ext_data(ext), data, set->dlen);

return elem;
}

static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
const struct nlattr *attr)
{
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
struct nft_data_desc d1, d2;
struct nft_set_ext_tmpl tmpl;
struct nft_set_ext *ext;
struct nft_set_elem elem;
struct nft_set_binding *binding;
struct nft_data data;
enum nft_registers dreg;
struct nft_trans *trans;
u32 flags;
int err;

if (set->size && set->nelems == set->size)
Expand All @@ -3152,22 +3181,26 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (nla[NFTA_SET_ELEM_KEY] == NULL)
return -EINVAL;

elem.flags = 0;
nft_set_ext_prepare(&tmpl);

flags = 0;
if (nla[NFTA_SET_ELEM_FLAGS] != NULL) {
elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END)
flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
if (flags & ~NFT_SET_ELEM_INTERVAL_END)
return -EINVAL;
if (!(set->flags & NFT_SET_INTERVAL) &&
elem.flags & NFT_SET_ELEM_INTERVAL_END)
flags & NFT_SET_ELEM_INTERVAL_END)
return -EINVAL;
if (flags != 0)
nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
}

if (set->flags & NFT_SET_MAP) {
if (nla[NFTA_SET_ELEM_DATA] == NULL &&
!(elem.flags & NFT_SET_ELEM_INTERVAL_END))
!(flags & NFT_SET_ELEM_INTERVAL_END))
return -EINVAL;
if (nla[NFTA_SET_ELEM_DATA] != NULL &&
elem.flags & NFT_SET_ELEM_INTERVAL_END)
flags & NFT_SET_ELEM_INTERVAL_END)
return -EINVAL;
} else {
if (nla[NFTA_SET_ELEM_DATA] != NULL)
Expand All @@ -3185,8 +3218,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (set->ops->get(set, &elem) == 0)
goto err2;

nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY);

if (nla[NFTA_SET_ELEM_DATA] != NULL) {
err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]);
err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]);
if (err < 0)
goto err2;

Expand All @@ -3203,29 +3238,42 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
};

err = nft_validate_data_load(&bind_ctx, dreg,
&elem.data, d2.type);
&data, d2.type);
if (err < 0)
goto err3;
}

nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA);
}

err = -ENOMEM;
elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data, GFP_KERNEL);
if (elem.priv == NULL)
goto err3;

ext = nft_set_elem_ext(set, elem.priv);
if (flags)
*nft_set_ext_flags(ext) = flags;

trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
if (trans == NULL)
goto err3;
goto err4;

err = set->ops->insert(set, &elem);
if (err < 0)
goto err4;
goto err5;

nft_trans_elem(trans) = elem;
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0;

err4:
err5:
kfree(trans);
err4:
kfree(elem.priv);
err3:
if (nla[NFTA_SET_ELEM_DATA] != NULL)
nft_data_uninit(&elem.data, d2.type);
nft_data_uninit(&data, d2.type);
err2:
nft_data_uninit(&elem.key, d1.type);
err1:
Expand Down Expand Up @@ -3557,6 +3605,7 @@ static int nf_tables_commit(struct sk_buff *skb)
struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
struct nft_trans_elem *te;
struct nft_set_ext *ext;

/* Bump generation counter, invalidate any dump in progress */
while (++net->nft.base_seq == 0);
Expand Down Expand Up @@ -3641,14 +3690,16 @@ static int nf_tables_commit(struct sk_buff *skb)
break;
case NFT_MSG_DELSETELEM:
te = (struct nft_trans_elem *)trans->data;
ext = nft_set_elem_ext(te->set, te->elem.priv);

nf_tables_setelem_notify(&trans->ctx, te->set,
&te->elem,
NFT_MSG_DELSETELEM, 0);
te->set->ops->get(te->set, &te->elem);
nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
if (te->set->flags & NFT_SET_MAP &&
!(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
nft_data_uninit(&te->elem.data, te->set->dtype);
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
nft_data_uninit(nft_set_ext_data(ext),
te->set->dtype);
te->set->ops->remove(te->set, &te->elem);
nft_trans_destroy(trans);
break;
Expand Down Expand Up @@ -3691,6 +3742,7 @@ static int nf_tables_abort(struct sk_buff *skb)
struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
struct nft_trans_elem *te;
struct nft_set_ext *ext;

list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
switch (trans->msg_type) {
Expand Down Expand Up @@ -3752,11 +3804,13 @@ static int nf_tables_abort(struct sk_buff *skb)
case NFT_MSG_NEWSETELEM:
nft_trans_elem_set(trans)->nelems--;
te = (struct nft_trans_elem *)trans->data;
ext = nft_set_elem_ext(te->set, te->elem.priv);

te->set->ops->get(te->set, &te->elem);
nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
if (te->set->flags & NFT_SET_MAP &&
!(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
nft_data_uninit(&te->elem.data, te->set->dtype);
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
nft_data_uninit(nft_set_ext_data(ext),
te->set->dtype);
te->set->ops->remove(te->set, &te->elem);
nft_trans_destroy(trans);
break;
Expand Down Expand Up @@ -3836,13 +3890,18 @@ static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
const struct nft_set_iter *iter,
const struct nft_set_elem *elem)
{
if (elem->flags & NFT_SET_ELEM_INTERVAL_END)
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
const struct nft_data *data;

if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
*nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
return 0;

switch (elem->data.verdict) {
data = nft_set_ext_data(ext);
switch (data->verdict) {
case NFT_JUMP:
case NFT_GOTO:
return nf_tables_check_loops(ctx, elem->data.chain);
return nf_tables_check_loops(ctx, data->chain);
default:
return 0;
}
Expand Down
Loading

0 comments on commit fe2811e

Please sign in to comment.