|
@@ -23,6 +23,7 @@ struct nft_dynset {
|
|
|
enum nft_registers sreg_key:8;
|
|
enum nft_registers sreg_key:8;
|
|
|
enum nft_registers sreg_data:8;
|
|
enum nft_registers sreg_data:8;
|
|
|
u64 timeout;
|
|
u64 timeout;
|
|
|
|
|
+ struct nft_expr *expr;
|
|
|
struct nft_set_binding binding;
|
|
struct nft_set_binding binding;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -30,6 +31,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
|
|
|
struct nft_regs *regs)
|
|
struct nft_regs *regs)
|
|
|
{
|
|
{
|
|
|
const struct nft_dynset *priv = nft_expr_priv(expr);
|
|
const struct nft_dynset *priv = nft_expr_priv(expr);
|
|
|
|
|
+ struct nft_set_ext *ext;
|
|
|
u64 timeout;
|
|
u64 timeout;
|
|
|
void *elem;
|
|
void *elem;
|
|
|
|
|
|
|
@@ -44,7 +46,13 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
|
|
|
if (elem == NULL) {
|
|
if (elem == NULL) {
|
|
|
if (set->size)
|
|
if (set->size)
|
|
|
atomic_dec(&set->nelems);
|
|
atomic_dec(&set->nelems);
|
|
|
|
|
+ return NULL;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ ext = nft_set_elem_ext(set, elem);
|
|
|
|
|
+ if (priv->expr != NULL)
|
|
|
|
|
+ nft_expr_clone(nft_set_ext_expr(ext), priv->expr);
|
|
|
|
|
+
|
|
|
return elem;
|
|
return elem;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -55,18 +63,27 @@ static void nft_dynset_eval(const struct nft_expr *expr,
|
|
|
const struct nft_dynset *priv = nft_expr_priv(expr);
|
|
const struct nft_dynset *priv = nft_expr_priv(expr);
|
|
|
struct nft_set *set = priv->set;
|
|
struct nft_set *set = priv->set;
|
|
|
const struct nft_set_ext *ext;
|
|
const struct nft_set_ext *ext;
|
|
|
|
|
+ const struct nft_expr *sexpr;
|
|
|
u64 timeout;
|
|
u64 timeout;
|
|
|
|
|
|
|
|
if (set->ops->update(set, ®s->data[priv->sreg_key], nft_dynset_new,
|
|
if (set->ops->update(set, ®s->data[priv->sreg_key], nft_dynset_new,
|
|
|
expr, regs, &ext)) {
|
|
expr, regs, &ext)) {
|
|
|
|
|
+ sexpr = NULL;
|
|
|
|
|
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
|
|
|
|
|
+ sexpr = nft_set_ext_expr(ext);
|
|
|
|
|
+
|
|
|
if (priv->op == NFT_DYNSET_OP_UPDATE &&
|
|
if (priv->op == NFT_DYNSET_OP_UPDATE &&
|
|
|
nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
|
|
nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
|
|
|
timeout = priv->timeout ? : set->timeout;
|
|
timeout = priv->timeout ? : set->timeout;
|
|
|
*nft_set_ext_expiration(ext) = jiffies + timeout;
|
|
*nft_set_ext_expiration(ext) = jiffies + timeout;
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ } else if (sexpr == NULL)
|
|
|
|
|
+ goto out;
|
|
|
|
|
|
|
|
|
|
+ if (sexpr != NULL)
|
|
|
|
|
+ sexpr->ops->eval(sexpr, regs, pkt);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+out:
|
|
|
regs->verdict.code = NFT_BREAK;
|
|
regs->verdict.code = NFT_BREAK;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -77,6 +94,7 @@ static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
|
|
|
[NFTA_DYNSET_SREG_KEY] = { .type = NLA_U32 },
|
|
[NFTA_DYNSET_SREG_KEY] = { .type = NLA_U32 },
|
|
|
[NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
|
|
[NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
|
|
|
[NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 },
|
|
[NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 },
|
|
|
|
|
+ [NFTA_DYNSET_EXPR] = { .type = NLA_NESTED },
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
static int nft_dynset_init(const struct nft_ctx *ctx,
|
|
static int nft_dynset_init(const struct nft_ctx *ctx,
|
|
@@ -142,10 +160,29 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
|
|
|
} else if (set->flags & NFT_SET_MAP)
|
|
} else if (set->flags & NFT_SET_MAP)
|
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
+ if (tb[NFTA_DYNSET_EXPR] != NULL) {
|
|
|
|
|
+ if (!(set->flags & NFT_SET_EVAL))
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ if (!(set->flags & NFT_SET_ANONYMOUS))
|
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
|
+
|
|
|
|
|
+ priv->expr = nft_expr_init(ctx, tb[NFTA_DYNSET_EXPR]);
|
|
|
|
|
+ if (IS_ERR(priv->expr))
|
|
|
|
|
+ return PTR_ERR(priv->expr);
|
|
|
|
|
+
|
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
|
+ if (!(priv->expr->ops->type->flags & NFT_EXPR_STATEFUL))
|
|
|
|
|
+ goto err1;
|
|
|
|
|
+ } else if (set->flags & NFT_SET_EVAL)
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+
|
|
|
nft_set_ext_prepare(&priv->tmpl);
|
|
nft_set_ext_prepare(&priv->tmpl);
|
|
|
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
|
|
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
|
|
|
if (set->flags & NFT_SET_MAP)
|
|
if (set->flags & NFT_SET_MAP)
|
|
|
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
|
|
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 (set->flags & NFT_SET_TIMEOUT) {
|
|
if (set->flags & NFT_SET_TIMEOUT) {
|
|
|
if (timeout || set->timeout)
|
|
if (timeout || set->timeout)
|
|
|
nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
|
|
nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
|
|
@@ -155,10 +192,15 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
|
|
|
|
|
|
|
|
err = nf_tables_bind_set(ctx, set, &priv->binding);
|
|
err = nf_tables_bind_set(ctx, set, &priv->binding);
|
|
|
if (err < 0)
|
|
if (err < 0)
|
|
|
- return err;
|
|
|
|
|
|
|
+ goto err1;
|
|
|
|
|
|
|
|
priv->set = set;
|
|
priv->set = set;
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
+
|
|
|
|
|
+err1:
|
|
|
|
|
+ if (priv->expr != NULL)
|
|
|
|
|
+ nft_expr_destroy(ctx, priv->expr);
|
|
|
|
|
+ return err;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void nft_dynset_destroy(const struct nft_ctx *ctx,
|
|
static void nft_dynset_destroy(const struct nft_ctx *ctx,
|
|
@@ -167,6 +209,8 @@ static void nft_dynset_destroy(const struct nft_ctx *ctx,
|
|
|
struct nft_dynset *priv = nft_expr_priv(expr);
|
|
struct nft_dynset *priv = nft_expr_priv(expr);
|
|
|
|
|
|
|
|
nf_tables_unbind_set(ctx, priv->set, &priv->binding);
|
|
nf_tables_unbind_set(ctx, priv->set, &priv->binding);
|
|
|
|
|
+ if (priv->expr != NULL)
|
|
|
|
|
+ nft_expr_destroy(ctx, priv->expr);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
|
static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
|
@@ -184,6 +228,8 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
|
|
if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
|
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
+ if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
|
|
|
|
|
+ goto nla_put_failure;
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
nla_put_failure:
|