Skip to content

Commit

Permalink
netfilter: nftables: generalize set expressions support
Browse files Browse the repository at this point in the history
Currently, the set infrastucture allows for one single expressions per
element. This patch extends the existing infrastructure to allow for up
to two expressions. This is not updating the netlink API yet, this is
coming as an initial preparation patch.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Pablo Neira Ayuso committed Dec 12, 2020
1 parent 86d21fc commit 8cfd9b0
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 28 deletions.
5 changes: 4 additions & 1 deletion include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,8 @@ struct nft_set_type {
};
#define to_set_type(o) container_of(o, struct nft_set_type, ops)

#define NFT_SET_EXPR_MAX 2

/**
* struct nft_set - nf_tables set instance
*
Expand Down Expand Up @@ -448,13 +450,14 @@ struct nft_set {
u16 policy;
u16 udlen;
unsigned char *udata;
struct nft_expr *expr;
/* runtime data below here */
const struct nft_set_ops *ops ____cacheline_aligned;
u16 flags:14,
genmask:2;
u8 klen;
u8 dlen;
u8 num_exprs;
struct nft_expr *exprs[NFT_SET_EXPR_MAX];
unsigned char data[]
__attribute__((aligned(__alignof__(u64))));
};
Expand Down
90 changes: 64 additions & 26 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -3841,9 +3841,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,

nla_nest_end(skb, nest);

if (set->expr) {
if (set->num_exprs == 1) {
nest = nla_nest_start_noflag(skb, NFTA_SET_EXPR);
if (nf_tables_fill_expr_info(skb, set->expr) < 0)
if (nf_tables_fill_expr_info(skb, set->exprs[0]) < 0)
goto nla_put_failure;

nla_nest_end(skb, nest);
Expand Down Expand Up @@ -4279,6 +4279,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
err = PTR_ERR(expr);
goto err_set_alloc_name;
}
set->exprs[0] = expr;
set->num_exprs++;
}

udata = NULL;
Expand All @@ -4296,7 +4298,6 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
set->dtype = dtype;
set->objtype = objtype;
set->dlen = desc.dlen;
set->expr = expr;
set->flags = flags;
set->size = desc.size;
set->policy = policy;
Expand Down Expand Up @@ -4325,8 +4326,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
err_set_trans:
ops->destroy(set);
err_set_init:
if (expr)
nft_expr_destroy(&ctx, expr);
for (i = 0; i < set->num_exprs; i++)
nft_expr_destroy(&ctx, set->exprs[i]);
err_set_alloc_name:
kfree(set->name);
err_set_name:
Expand All @@ -4336,11 +4337,13 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,

static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
{
int i;

if (WARN_ON(set->use > 0))
return;

if (set->expr)
nft_expr_destroy(ctx, set->expr);
for (i = 0; i < set->num_exprs; i++)
nft_expr_destroy(ctx, set->exprs[i]);

set->ops->destroy(set);
kfree(set->name);
Expand Down Expand Up @@ -5139,17 +5142,46 @@ static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
kfree(elem);
}

static int nft_set_elem_expr_clone(const struct nft_ctx *ctx,
struct nft_set *set,
struct nft_expr *expr_array[])
{
struct nft_expr *expr;
int err, i, k;

for (i = 0; i < set->num_exprs; i++) {
expr = kzalloc(set->exprs[i]->ops->size, GFP_KERNEL);
if (!expr)
goto err_expr;

err = nft_expr_clone(expr, set->exprs[i]);
if (err < 0) {
nft_expr_destroy(ctx, expr);
goto err_expr;
}
expr_array[i] = expr;
}

return 0;

err_expr:
for (k = i - 1; k >= 0; k++)
nft_expr_destroy(ctx, expr_array[i]);

return -ENOMEM;
}

static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
const struct nlattr *attr, u32 nlmsg_flags)
{
struct nft_expr *expr_array[NFT_SET_EXPR_MAX] = {};
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
u8 genmask = nft_genmask_next(ctx->net);
struct nft_set_ext_tmpl tmpl;
struct nft_set_ext *ext, *ext2;
struct nft_set_elem elem;
struct nft_set_binding *binding;
struct nft_object *obj = NULL;
struct nft_expr *expr = NULL;
struct nft_userdata *udata;
struct nft_data_desc desc;
enum nft_registers dreg;
Expand All @@ -5158,7 +5190,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
u64 timeout;
u64 expiration;
u8 ulen;
int err;
int err, i;

err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
nft_set_elem_policy, NULL);
Expand Down Expand Up @@ -5216,23 +5248,27 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return err;
}

if (nla[NFTA_SET_ELEM_EXPR] != NULL) {
if (nla[NFTA_SET_ELEM_EXPR]) {
struct nft_expr *expr;

if (set->num_exprs != 1)
return -EOPNOTSUPP;

expr = nft_set_elem_expr_alloc(ctx, set,
nla[NFTA_SET_ELEM_EXPR]);
if (IS_ERR(expr))
return PTR_ERR(expr);

err = -EOPNOTSUPP;
if (set->expr && set->expr->ops != expr->ops)
goto err_set_elem_expr;
} else if (set->expr) {
expr = kzalloc(set->expr->ops->size, GFP_KERNEL);
if (!expr)
return -ENOMEM;
expr_array[0] = expr;

err = nft_expr_clone(expr, set->expr);
if (err < 0)
if (set->exprs[0] && set->exprs[0]->ops != expr->ops) {
err = -EOPNOTSUPP;
goto err_set_elem_expr;
}
} else if (set->num_exprs > 0) {
err = nft_set_elem_expr_clone(ctx, set, expr_array);
if (err < 0)
goto err_set_elem_expr_clone;
}

err = nft_setelem_parse_key(ctx, set, &elem.key.val,
Expand All @@ -5257,9 +5293,9 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
}

if (expr)
if (set->num_exprs == 1)
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPR,
expr->ops->size);
expr_array[0]->ops->size);

if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
if (!(set->flags & NFT_SET_OBJECT)) {
Expand Down Expand Up @@ -5341,10 +5377,12 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
*nft_set_ext_obj(ext) = obj;
obj->use++;
}
if (expr) {
if (set->num_exprs == 1) {
struct nft_expr *expr = expr_array[0];

memcpy(nft_set_ext_expr(ext), expr, expr->ops->size);
kfree(expr);
expr = NULL;
expr_array[0] = NULL;
}

trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
Expand Down Expand Up @@ -5406,9 +5444,9 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
err_parse_key:
nft_data_release(&elem.key.val, NFT_DATA_VALUE);
err_set_elem_expr:
if (expr != NULL)
nft_expr_destroy(ctx, expr);

for (i = 0; i < set->num_exprs && expr_array[i]; i++)
nft_expr_destroy(ctx, expr_array[i]);
err_set_elem_expr_clone:
return err;
}

Expand Down
3 changes: 2 additions & 1 deletion net/netfilter/nft_dynset.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
if (IS_ERR(priv->expr))
return PTR_ERR(priv->expr);

if (set->expr && set->expr->ops != priv->expr->ops) {
if (set->num_exprs == 1 &&
set->exprs[0]->ops != priv->expr->ops) {
err = -EOPNOTSUPP;
goto err_expr_free;
}
Expand Down

0 comments on commit 8cfd9b0

Please sign in to comment.