summaryrefslogtreecommitdiffstats
path: root/src/netlink_delinearize.c
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2020-12-09 16:46:26 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2020-12-17 17:24:49 +0100
commit242965f452e64fef9faff6689df4b2c205823209 (patch)
tree2eaab70fe894e57984a9392fb6bc423ccab5ccbf /src/netlink_delinearize.c
parentca7464bf51f3784b29e29a40cee2c73f1f0c4f7e (diff)
src: add support for multi-statement in dynamic sets and maps
This patch allows for two statements for dynamic set updates, e.g. nft rule x y add @y { ip daddr limit rate 1/second counter } Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/netlink_delinearize.c')
-rw-r--r--src/netlink_delinearize.c74
1 files changed, 60 insertions, 14 deletions
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 8b06c4c0..73150722 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1489,17 +1489,47 @@ static void netlink_parse_queue(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
+struct dynset_parse_ctx {
+ struct netlink_parse_ctx *nlctx;
+ const struct location *loc;
+ struct list_head stmt_list;
+};
+
+static int dynset_parse_expressions(struct nftnl_expr *e, void *data)
+{
+ struct dynset_parse_ctx *dynset_parse_ctx = data;
+ struct netlink_parse_ctx *ctx = dynset_parse_ctx->nlctx;
+ const struct location *loc = dynset_parse_ctx->loc;
+ struct stmt *stmt;
+
+ if (netlink_parse_expr(e, ctx) < 0 || !ctx->stmt) {
+ netlink_error(ctx, loc, "Could not parse dynset stmt");
+ return -1;
+ }
+ stmt = ctx->stmt;
+
+ list_add_tail(&stmt->list, &dynset_parse_ctx->stmt_list);
+
+ return 0;
+}
+
static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
+ struct dynset_parse_ctx dynset_parse_ctx = {
+ .nlctx = ctx,
+ .loc = loc,
+ };
struct expr *expr, *expr_data = NULL;
enum nft_registers sreg, sreg_data;
+ struct stmt *stmt, *dstmt, *next;
const struct nftnl_expr *dnle;
- struct stmt *stmt, *dstmt;
struct set *set;
const char *name;
+ init_list_head(&dynset_parse_ctx.stmt_list);
+
name = nftnl_expr_get_str(nle, NFTNL_EXPR_DYNSET_SET_NAME);
set = set_lookup(ctx->table, name);
if (set == NULL)
@@ -1523,16 +1553,25 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
expr = set_elem_expr_alloc(&expr->location, expr);
expr->timeout = nftnl_expr_get_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT);
- dstmt = NULL;
- dnle = nftnl_expr_get(nle, NFTNL_EXPR_DYNSET_EXPR, NULL);
- if (dnle != NULL) {
- if (netlink_parse_expr(dnle, ctx) < 0)
- goto out_err;
- if (ctx->stmt == NULL) {
- netlink_error(ctx, loc, "Could not parse dynset stmt");
- goto out_err;
+ if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_EXPR)) {
+ dstmt = NULL;
+ dnle = nftnl_expr_get(nle, NFTNL_EXPR_DYNSET_EXPR, NULL);
+ if (dnle != NULL) {
+ if (netlink_parse_expr(dnle, ctx) < 0)
+ goto out_err;
+ if (ctx->stmt == NULL) {
+ netlink_error(ctx, loc,
+ "Could not parse dynset stmt");
+ goto out_err;
+ }
+ dstmt = ctx->stmt;
+ list_add_tail(&dstmt->list,
+ &dynset_parse_ctx.stmt_list);
}
- dstmt = ctx->stmt;
+ } else if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS)) {
+ if (nftnl_expr_expr_foreach(nle, dynset_parse_expressions,
+ &dynset_parse_ctx) < 0)
+ goto out_err;
}
if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_SREG_DATA)) {
@@ -1546,27 +1585,34 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
stmt->map.set = set_ref_expr_alloc(loc, set);
stmt->map.key = expr;
stmt->map.data = expr_data;
- stmt->map.stmt = dstmt;
stmt->map.op = nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP);
+ list_splice_tail(&dynset_parse_ctx.stmt_list,
+ &stmt->map.stmt_list);
} else {
- if (dstmt != NULL && set->flags & NFT_SET_ANONYMOUS) {
+ if (!list_empty(&dynset_parse_ctx.stmt_list) &&
+ set->flags & NFT_SET_ANONYMOUS) {
stmt = meter_stmt_alloc(loc);
stmt->meter.set = set_ref_expr_alloc(loc, set);
stmt->meter.key = expr;
- stmt->meter.stmt = dstmt;
+ stmt->meter.stmt = list_first_entry(&dynset_parse_ctx.stmt_list,
+ struct stmt, list);
stmt->meter.size = set->desc.size;
} else {
stmt = set_stmt_alloc(loc);
stmt->set.set = set_ref_expr_alloc(loc, set);
stmt->set.op = nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP);
stmt->set.key = expr;
- stmt->set.stmt = dstmt;
+ list_splice_tail(&dynset_parse_ctx.stmt_list,
+ &stmt->set.stmt_list);
}
}
ctx->stmt = stmt;
return;
out_err:
+ list_for_each_entry_safe(dstmt, next, &dynset_parse_ctx.stmt_list, list)
+ stmt_free(dstmt);
+
xfree(expr);
}