mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
netfilter: nftables: generalize set extension to support for several expressions
This patch replaces NFT_SET_EXPR by NFT_SET_EXT_EXPRESSIONS. This new extension allows to attach several expressions to one set element (not only one single expression as NFT_SET_EXPR provides). This patch prepares for support for several expressions per set element in the netlink userspace API. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
92b211a289
commit
563125a73a
@ -421,6 +421,20 @@ struct nft_set_type {
|
||||
};
|
||||
#define to_set_type(o) container_of(o, struct nft_set_type, ops)
|
||||
|
||||
struct nft_set_elem_expr {
|
||||
u8 size;
|
||||
unsigned char data[]
|
||||
__attribute__((aligned(__alignof__(struct nft_expr))));
|
||||
};
|
||||
|
||||
#define nft_setelem_expr_at(__elem_expr, __offset) \
|
||||
((struct nft_expr *)&__elem_expr->data[__offset])
|
||||
|
||||
#define nft_setelem_expr_foreach(__expr, __elem_expr, __size) \
|
||||
for (__expr = nft_setelem_expr_at(__elem_expr, 0), __size = 0; \
|
||||
__size < (__elem_expr)->size; \
|
||||
__size += (__expr)->ops->size, __expr = ((void *)(__expr)) + (__expr)->ops->size)
|
||||
|
||||
#define NFT_SET_EXPR_MAX 2
|
||||
|
||||
/**
|
||||
@ -547,7 +561,7 @@ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);
|
||||
* @NFT_SET_EXT_TIMEOUT: element timeout
|
||||
* @NFT_SET_EXT_EXPIRATION: element expiration time
|
||||
* @NFT_SET_EXT_USERDATA: user data associated with the element
|
||||
* @NFT_SET_EXT_EXPR: expression assiociated with the element
|
||||
* @NFT_SET_EXT_EXPRESSIONS: expressions assiciated with the element
|
||||
* @NFT_SET_EXT_OBJREF: stateful object reference associated with element
|
||||
* @NFT_SET_EXT_NUM: number of extension types
|
||||
*/
|
||||
@ -559,7 +573,7 @@ enum nft_set_extensions {
|
||||
NFT_SET_EXT_TIMEOUT,
|
||||
NFT_SET_EXT_EXPIRATION,
|
||||
NFT_SET_EXT_USERDATA,
|
||||
NFT_SET_EXT_EXPR,
|
||||
NFT_SET_EXT_EXPRESSIONS,
|
||||
NFT_SET_EXT_OBJREF,
|
||||
NFT_SET_EXT_NUM
|
||||
};
|
||||
@ -677,9 +691,9 @@ static inline struct nft_userdata *nft_set_ext_userdata(const struct nft_set_ext
|
||||
return nft_set_ext(ext, NFT_SET_EXT_USERDATA);
|
||||
}
|
||||
|
||||
static inline struct nft_expr *nft_set_ext_expr(const struct nft_set_ext *ext)
|
||||
static inline struct nft_set_elem_expr *nft_set_ext_expr(const struct nft_set_ext *ext)
|
||||
{
|
||||
return nft_set_ext(ext, NFT_SET_EXT_EXPR);
|
||||
return nft_set_ext(ext, NFT_SET_EXT_EXPRESSIONS);
|
||||
}
|
||||
|
||||
static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
|
||||
@ -909,11 +923,17 @@ static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
{
|
||||
struct nft_set_elem_expr *elem_expr;
|
||||
struct nft_expr *expr;
|
||||
u32 size;
|
||||
|
||||
if (__nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) {
|
||||
expr = nft_set_ext_expr(ext);
|
||||
expr->ops->eval(expr, regs, pkt);
|
||||
if (__nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) {
|
||||
elem_expr = nft_set_ext_expr(ext);
|
||||
nft_setelem_expr_foreach(expr, elem_expr, size) {
|
||||
expr->ops->eval(expr, regs, pkt);
|
||||
if (regs->verdict.code == NFT_BREAK)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4496,8 +4496,8 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
|
||||
[NFT_SET_EXT_DATA] = {
|
||||
.align = __alignof__(u32),
|
||||
},
|
||||
[NFT_SET_EXT_EXPR] = {
|
||||
.align = __alignof__(struct nft_expr),
|
||||
[NFT_SET_EXT_EXPRESSIONS] = {
|
||||
.align = __alignof__(struct nft_set_elem_expr),
|
||||
},
|
||||
[NFT_SET_EXT_OBJREF] = {
|
||||
.len = sizeof(struct nft_object *),
|
||||
@ -4573,6 +4573,29 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_set_elem_expr_dump(struct sk_buff *skb,
|
||||
const struct nft_set *set,
|
||||
const struct nft_set_ext *ext)
|
||||
{
|
||||
struct nft_set_elem_expr *elem_expr;
|
||||
u32 size, num_exprs = 0;
|
||||
struct nft_expr *expr;
|
||||
|
||||
elem_expr = nft_set_ext_expr(ext);
|
||||
nft_setelem_expr_foreach(expr, elem_expr, size)
|
||||
num_exprs++;
|
||||
|
||||
if (num_exprs == 1) {
|
||||
expr = nft_setelem_expr_at(elem_expr, 0);
|
||||
if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nf_tables_fill_setelem(struct sk_buff *skb,
|
||||
const struct nft_set *set,
|
||||
const struct nft_set_elem *elem)
|
||||
@ -4600,8 +4623,8 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
|
||||
set->dlen) < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR) &&
|
||||
nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0)
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS) &&
|
||||
nft_set_elem_expr_dump(skb, set, ext))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) &&
|
||||
@ -5096,8 +5119,8 @@ void *nft_set_elem_init(const struct nft_set *set,
|
||||
return elem;
|
||||
}
|
||||
|
||||
static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
struct nft_expr *expr)
|
||||
static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
struct nft_expr *expr)
|
||||
{
|
||||
if (expr->ops->destroy_clone) {
|
||||
expr->ops->destroy_clone(ctx, expr);
|
||||
@ -5107,6 +5130,16 @@ static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
}
|
||||
}
|
||||
|
||||
static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
struct nft_set_elem_expr *elem_expr)
|
||||
{
|
||||
struct nft_expr *expr;
|
||||
u32 size;
|
||||
|
||||
nft_setelem_expr_foreach(expr, elem_expr, size)
|
||||
__nft_set_elem_expr_destroy(ctx, expr);
|
||||
}
|
||||
|
||||
void nft_set_elem_destroy(const struct nft_set *set, void *elem,
|
||||
bool destroy_expr)
|
||||
{
|
||||
@ -5119,7 +5152,7 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
|
||||
nft_data_release(nft_set_ext_key(ext), NFT_DATA_VALUE);
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
|
||||
nft_data_release(nft_set_ext_data(ext), set->dtype);
|
||||
if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
|
||||
if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
|
||||
nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext));
|
||||
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
|
||||
@ -5136,7 +5169,7 @@ static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
|
||||
{
|
||||
struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
|
||||
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
|
||||
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
|
||||
nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext));
|
||||
|
||||
kfree(elem);
|
||||
@ -5171,6 +5204,18 @@ err_expr:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void nft_set_elem_expr_setup(const struct nft_set_ext *ext, int i,
|
||||
struct nft_expr *expr_array[])
|
||||
{
|
||||
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
|
||||
struct nft_expr *expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
|
||||
|
||||
memcpy(expr, expr_array[i], expr_array[i]->ops->size);
|
||||
elem_expr->size += expr_array[i]->ops->size;
|
||||
kfree(expr_array[i]);
|
||||
expr_array[i] = NULL;
|
||||
}
|
||||
|
||||
static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
const struct nlattr *attr, u32 nlmsg_flags)
|
||||
{
|
||||
@ -5186,11 +5231,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_data_desc desc;
|
||||
enum nft_registers dreg;
|
||||
struct nft_trans *trans;
|
||||
u32 flags = 0;
|
||||
u32 flags = 0, size = 0;
|
||||
u64 timeout;
|
||||
u64 expiration;
|
||||
u8 ulen;
|
||||
int err, i;
|
||||
u8 ulen;
|
||||
|
||||
err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
|
||||
nft_set_elem_policy, NULL);
|
||||
@ -5293,9 +5338,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
|
||||
}
|
||||
|
||||
if (set->num_exprs == 1)
|
||||
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPR,
|
||||
expr_array[0]->ops->size);
|
||||
if (set->num_exprs) {
|
||||
for (i = 0; i < set->num_exprs; i++)
|
||||
size += expr_array[i]->ops->size;
|
||||
|
||||
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPRESSIONS,
|
||||
sizeof(struct nft_set_elem_expr) +
|
||||
size);
|
||||
}
|
||||
|
||||
if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
|
||||
if (!(set->flags & NFT_SET_OBJECT)) {
|
||||
@ -5377,13 +5427,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
*nft_set_ext_obj(ext) = obj;
|
||||
obj->use++;
|
||||
}
|
||||
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_array[0] = NULL;
|
||||
}
|
||||
for (i = 0; i < set->num_exprs; i++)
|
||||
nft_set_elem_expr_setup(ext, i, expr_array);
|
||||
|
||||
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
|
||||
if (trans == NULL)
|
||||
|
@ -19,11 +19,30 @@ struct nft_dynset {
|
||||
enum nft_registers sreg_key:8;
|
||||
enum nft_registers sreg_data:8;
|
||||
bool invert;
|
||||
u8 num_exprs;
|
||||
u64 timeout;
|
||||
struct nft_expr *expr;
|
||||
struct nft_expr *expr_array[NFT_SET_EXPR_MAX];
|
||||
struct nft_set_binding binding;
|
||||
};
|
||||
|
||||
static int nft_dynset_expr_setup(const struct nft_dynset *priv,
|
||||
const struct nft_set_ext *ext)
|
||||
{
|
||||
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
|
||||
struct nft_expr *expr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->num_exprs; i++) {
|
||||
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
|
||||
if (nft_expr_clone(expr, priv->expr_array[i]) < 0)
|
||||
return -1;
|
||||
|
||||
elem_expr->size += priv->expr_array[i]->ops->size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
|
||||
struct nft_regs *regs)
|
||||
{
|
||||
@ -44,8 +63,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
|
||||
goto err1;
|
||||
|
||||
ext = nft_set_elem_ext(set, elem);
|
||||
if (priv->expr != NULL &&
|
||||
nft_expr_clone(nft_set_ext_expr(ext), priv->expr) < 0)
|
||||
if (priv->num_exprs && nft_dynset_expr_setup(priv, ext) < 0)
|
||||
goto err2;
|
||||
|
||||
return elem;
|
||||
@ -90,6 +108,41 @@ void nft_dynset_eval(const struct nft_expr *expr,
|
||||
regs->verdict.code = NFT_BREAK;
|
||||
}
|
||||
|
||||
static void nft_dynset_ext_add_expr(struct nft_dynset *priv)
|
||||
{
|
||||
u8 size = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->num_exprs; i++)
|
||||
size += priv->expr_array[i]->ops->size;
|
||||
|
||||
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPRESSIONS,
|
||||
sizeof(struct nft_set_elem_expr) + size);
|
||||
}
|
||||
|
||||
static struct nft_expr *
|
||||
nft_dynset_expr_alloc(const struct nft_ctx *ctx, const struct nft_set *set,
|
||||
const struct nlattr *attr, int pos)
|
||||
{
|
||||
struct nft_expr *expr;
|
||||
int err;
|
||||
|
||||
expr = nft_set_elem_expr_alloc(ctx, set, attr);
|
||||
if (IS_ERR(expr))
|
||||
return expr;
|
||||
|
||||
if (set->exprs[pos] && set->exprs[pos]->ops != expr->ops) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_dynset_expr;
|
||||
}
|
||||
|
||||
return expr;
|
||||
|
||||
err_dynset_expr:
|
||||
nft_expr_destroy(ctx, expr);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
|
||||
[NFTA_DYNSET_SET_NAME] = { .type = NLA_STRING,
|
||||
.len = NFT_SET_MAXNAMELEN - 1 },
|
||||
@ -110,7 +163,7 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
|
||||
u8 genmask = nft_genmask_next(ctx->net);
|
||||
struct nft_set *set;
|
||||
u64 timeout;
|
||||
int err;
|
||||
int err, i;
|
||||
|
||||
lockdep_assert_held(&ctx->net->nft.commit_mutex);
|
||||
|
||||
@ -179,17 +232,23 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
|
||||
} else if (set->flags & NFT_SET_MAP)
|
||||
return -EINVAL;
|
||||
|
||||
if (tb[NFTA_DYNSET_EXPR] != NULL) {
|
||||
if (tb[NFTA_DYNSET_EXPR]) {
|
||||
struct nft_expr *dynset_expr;
|
||||
|
||||
if (!(set->flags & NFT_SET_EVAL))
|
||||
return -EINVAL;
|
||||
|
||||
priv->expr = nft_set_elem_expr_alloc(ctx, set,
|
||||
tb[NFTA_DYNSET_EXPR]);
|
||||
if (IS_ERR(priv->expr))
|
||||
return PTR_ERR(priv->expr);
|
||||
dynset_expr = nft_dynset_expr_alloc(ctx, set,
|
||||
tb[NFTA_DYNSET_EXPR], 0);
|
||||
if (IS_ERR(dynset_expr))
|
||||
return PTR_ERR(dynset_expr);
|
||||
|
||||
if (set->num_exprs == 1 &&
|
||||
set->exprs[0]->ops != priv->expr->ops) {
|
||||
priv->num_exprs++;
|
||||
priv->expr_array[0] = dynset_expr;
|
||||
|
||||
if (set->num_exprs > 1 ||
|
||||
(set->num_exprs == 1 &&
|
||||
dynset_expr->ops != set->exprs[0]->ops)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_expr_free;
|
||||
}
|
||||
@ -199,9 +258,10 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
|
||||
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
|
||||
if (set->flags & NFT_SET_MAP)
|
||||
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
|
||||
if (priv->expr != NULL)
|
||||
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPR,
|
||||
priv->expr->ops->size);
|
||||
|
||||
if (priv->num_exprs)
|
||||
nft_dynset_ext_add_expr(priv);
|
||||
|
||||
if (set->flags & NFT_SET_TIMEOUT) {
|
||||
if (timeout || set->timeout)
|
||||
nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
|
||||
@ -220,8 +280,8 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
|
||||
return 0;
|
||||
|
||||
err_expr_free:
|
||||
if (priv->expr != NULL)
|
||||
nft_expr_destroy(ctx, priv->expr);
|
||||
for (i = 0; i < priv->num_exprs; i++)
|
||||
nft_expr_destroy(ctx, priv->expr_array[i]);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -246,9 +306,10 @@ static void nft_dynset_destroy(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
{
|
||||
struct nft_dynset *priv = nft_expr_priv(expr);
|
||||
int i;
|
||||
|
||||
if (priv->expr != NULL)
|
||||
nft_expr_destroy(ctx, priv->expr);
|
||||
for (i = 0; i < priv->num_exprs; i++)
|
||||
nft_expr_destroy(ctx, priv->expr_array[i]);
|
||||
|
||||
nf_tables_destroy_set(ctx, priv->set);
|
||||
}
|
||||
@ -271,8 +332,10 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
||||
cpu_to_be64(jiffies_to_msecs(priv->timeout)),
|
||||
NFTA_DYNSET_PAD))
|
||||
goto nla_put_failure;
|
||||
if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
|
||||
goto nla_put_failure;
|
||||
if (priv->num_exprs == 1) {
|
||||
if (nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr_array[0]))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags)))
|
||||
goto nla_put_failure;
|
||||
return 0;
|
||||
|
@ -293,6 +293,22 @@ cont:
|
||||
rhashtable_walk_exit(&hti);
|
||||
}
|
||||
|
||||
static bool nft_rhash_expr_needs_gc_run(const struct nft_set *set,
|
||||
struct nft_set_ext *ext)
|
||||
{
|
||||
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
|
||||
struct nft_expr *expr;
|
||||
u32 size;
|
||||
|
||||
nft_setelem_expr_foreach(expr, elem_expr, size) {
|
||||
if (expr->ops->gc &&
|
||||
expr->ops->gc(read_pnet(&set->net), expr))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void nft_rhash_gc(struct work_struct *work)
|
||||
{
|
||||
struct nft_set *set;
|
||||
@ -314,16 +330,13 @@ static void nft_rhash_gc(struct work_struct *work)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPR)) {
|
||||
struct nft_expr *expr = nft_set_ext_expr(&he->ext);
|
||||
if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPRESSIONS) &&
|
||||
nft_rhash_expr_needs_gc_run(set, &he->ext))
|
||||
goto needs_gc_run;
|
||||
|
||||
if (expr->ops->gc &&
|
||||
expr->ops->gc(read_pnet(&set->net), expr))
|
||||
goto gc;
|
||||
}
|
||||
if (!nft_set_elem_expired(&he->ext))
|
||||
continue;
|
||||
gc:
|
||||
needs_gc_run:
|
||||
if (nft_set_elem_mark_busy(&he->ext))
|
||||
continue;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user