summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2015-06-04 20:57:20 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2015-06-04 20:57:20 +0200
commit7c357801945977a7a7759ead56b4cb320673219b (patch)
treed90c08dcd9286f37b9cfe24a4eabb5f73fb454de /src
parent933fb6b993d9ddd9a96d15edbea393dc56c932e3 (diff)
parent575cc4519aa177c573481f683e07c2789a2f870a (diff)
Merge branch 'next-4.1'
Diffstat (limited to 'src')
-rw-r--r--src/datatype.c82
-rw-r--r--src/evaluate.c47
-rw-r--r--src/expression.c42
-rw-r--r--src/netlink.c83
-rw-r--r--src/netlink_delinearize.c255
-rw-r--r--src/netlink_linearize.c26
-rw-r--r--src/parser_bison.y105
-rw-r--r--src/rule.c23
-rw-r--r--src/scanner.l10
-rw-r--r--src/segtree.c8
-rw-r--r--src/statement.c31
11 files changed, 564 insertions, 148 deletions
diff --git a/src/datatype.c b/src/datatype.c
index c93f76a3..f93337b1 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -760,11 +760,9 @@ const struct datatype icmpx_code_type = {
.sym_tbl = &icmpx_code_tbl,
};
-static void time_type_print(const struct expr *expr)
+void time_print(uint64_t seconds)
{
- uint64_t days, hours, minutes, seconds;
-
- seconds = mpz_get_uint64(expr->value);
+ uint64_t days, hours, minutes;
days = seconds / 86400;
seconds %= 86400;
@@ -775,8 +773,6 @@ static void time_type_print(const struct expr *expr)
minutes = seconds / 60;
seconds %= 60;
- printf("\"");
-
if (days > 0)
printf("%"PRIu64"d", days);
if (hours > 0)
@@ -785,8 +781,6 @@ static void time_type_print(const struct expr *expr)
printf("%"PRIu64"m", minutes);
if (seconds > 0)
printf("%"PRIu64"s", seconds);
-
- printf("\"");
}
enum {
@@ -805,8 +799,8 @@ static uint32_t str2int(char *tmp, const char *c, int k)
return atoi(tmp);
}
-static struct error_record *time_type_parse(const struct expr *sym,
- struct expr **res)
+struct error_record *time_parse(const struct location *loc, const char *str,
+ uint64_t *res)
{
int i, len;
unsigned int k = 0;
@@ -815,76 +809,82 @@ static struct error_record *time_type_parse(const struct expr *sym,
uint64_t d = 0, h = 0, m = 0, s = 0;
uint32_t mask = 0;
- c = sym->identifier;
+ c = str;
len = strlen(c);
for (i = 0; i < len; i++, c++) {
switch (*c) {
case 'd':
- if (mask & DAY) {
- return error(&sym->location,
+ if (mask & DAY)
+ return error(loc,
"Day has been specified twice");
- }
+
d = str2int(tmp, c, k);
k = 0;
mask |= DAY;
break;
case 'h':
- if (mask & HOUR) {
- return error(&sym->location,
+ if (mask & HOUR)
+ return error(loc,
"Hour has been specified twice");
- }
+
h = str2int(tmp, c, k);
k = 0;
- if (h > 23) {
- return error(&sym->location,
- "Hour needs to be 0-23");
- }
mask |= HOUR;
break;
case 'm':
- if (mask & MIN) {
- return error(&sym->location,
+ if (mask & MIN)
+ return error(loc,
"Minute has been specified twice");
- }
+
m = str2int(tmp, c, k);
k = 0;
- if (m > 59) {
- return error(&sym->location,
- "Minute needs to be 0-59");
- }
mask |= MIN;
break;
case 's':
- if (mask & SECS) {
- return error(&sym->location,
+ if (mask & SECS)
+ return error(loc,
"Second has been specified twice");
- }
+
s = str2int(tmp, c, k);
k = 0;
- if (s > 59) {
- return error(&sym->location,
- "second needs to be 0-59");
- }
mask |= SECS;
break;
default:
if (!isdigit(*c))
- return error(&sym->location, "wrong format");
+ return error(loc, "wrong time format");
- if (k++ >= array_size(tmp)) {
- return error(&sym->location,
- "value too large");
- }
+ if (k++ >= array_size(tmp))
+ return error(loc, "value too large");
break;
}
}
/* default to seconds if no unit was specified */
if (!mask)
- s = atoi(sym->identifier);
+ s = atoi(str);
else
s = 24*60*60*d+60*60*h+60*m+s;
+ *res = s;
+ return NULL;
+}
+
+
+static void time_type_print(const struct expr *expr)
+{
+ time_print(mpz_get_uint64(expr->value));
+}
+
+static struct error_record *time_type_parse(const struct expr *sym,
+ struct expr **res)
+{
+ struct error_record *erec;
+ uint64_t s;
+
+ erec = time_parse(&sym->location, sym->identifier, &s);
+ if (erec != NULL)
+ return erec;
+
if (s > UINT32_MAX)
return error(&sym->location, "value too large");
diff --git a/src/evaluate.c b/src/evaluate.c
index 7ecb7939..e260a803 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -614,7 +614,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
struct expr *i, *next;
list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
- if (dtype && off == 0)
+ if (expr_is_constant(*expr) && dtype && off == 0)
return expr_binary_error(ctx->msgs, i, *expr,
"unexpected concat component, "
"expecting %s",
@@ -675,6 +675,19 @@ static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
+{
+ struct expr *elem = *expr;
+
+ if (expr_evaluate(ctx, &elem->key) < 0)
+ return -1;
+
+ elem->dtype = elem->key->dtype;
+ elem->len = elem->key->len;
+ elem->flags = elem->key->flags;
+ return 0;
+}
+
static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *set = *expr, *i, *next;
@@ -1100,6 +1113,8 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
return expr_evaluate_list(ctx, expr);
case EXPR_SET:
return expr_evaluate_set(ctx, expr);
+ case EXPR_SET_ELEM:
+ return expr_evaluate_set_elem(ctx, expr);
case EXPR_MAP:
return expr_evaluate_map(ctx, expr);
case EXPR_MAPPING:
@@ -1646,6 +1661,30 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ expr_set_context(&ctx->ectx, NULL, 0);
+ if (expr_evaluate(ctx, &stmt->set.set) < 0)
+ return -1;
+ if (stmt->set.set->ops->type != EXPR_SET_REF)
+ return expr_error(ctx->msgs, stmt->set.set,
+ "Expression does not refer to a set");
+
+ if (stmt_evaluate_arg(ctx, stmt,
+ stmt->set.set->set->keytype,
+ stmt->set.set->set->keylen,
+ &stmt->set.key) < 0)
+ return -1;
+ if (expr_is_constant(stmt->set.key))
+ return expr_error(ctx->msgs, stmt->set.key,
+ "Key expression can not be constant");
+ if (stmt->set.key->comment != NULL)
+ return expr_error(ctx->msgs, stmt->set.key,
+ "Key expression comments are not supported");
+
+ return 0;
+}
+
int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
{
#ifdef DEBUG
@@ -1680,6 +1719,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_evaluate_redir(ctx, stmt);
case STMT_QUEUE:
return stmt_evaluate_queue(ctx, stmt);
+ case STMT_SET:
+ return stmt_evaluate_set(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
@@ -1722,6 +1763,10 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
return -1;
}
+ /* Default timeout value implies timeout support */
+ if (set->timeout)
+ set->flags |= SET_F_TIMEOUT;
+
if (!(set->flags & SET_F_MAP))
return 0;
diff --git a/src/expression.c b/src/expression.c
index 5b848da7..3edc5501 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -886,6 +886,44 @@ struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set)
return expr;
}
+static void set_elem_expr_print(const struct expr *expr)
+{
+ expr_print(expr->key);
+ if (expr->timeout) {
+ printf(" timeout ");
+ time_print(expr->timeout / 1000);
+ }
+ if (expr->expiration) {
+ printf(" expires ");
+ time_print(expr->expiration / 1000);
+ }
+ if (expr->comment)
+ printf(" comment \"%s\"", expr->comment);
+}
+
+static void set_elem_expr_destroy(struct expr *expr)
+{
+ xfree(expr->comment);
+ expr_free(expr->key);
+}
+
+static const struct expr_ops set_elem_expr_ops = {
+ .type = EXPR_SET_ELEM,
+ .name = "set element",
+ .print = set_elem_expr_print,
+ .destroy = set_elem_expr_destroy,
+};
+
+struct expr *set_elem_expr_alloc(const struct location *loc, struct expr *key)
+{
+ struct expr *expr;
+
+ expr = expr_alloc(loc, &set_elem_expr_ops, key->dtype,
+ key->byteorder, key->len);
+ expr->key = key;
+ return expr;
+}
+
void range_expr_value_low(mpz_t rop, const struct expr *expr)
{
switch (expr->ops->type) {
@@ -897,6 +935,8 @@ void range_expr_value_low(mpz_t rop, const struct expr *expr)
return range_expr_value_low(rop, expr->left);
case EXPR_MAPPING:
return range_expr_value_low(rop, expr->left);
+ case EXPR_SET_ELEM:
+ return range_expr_value_low(rop, expr->key);
default:
BUG("invalid range expression type %s\n", expr->ops->name);
}
@@ -919,6 +959,8 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
return range_expr_value_high(rop, expr->right);
case EXPR_MAPPING:
return range_expr_value_high(rop, expr->left);
+ case EXPR_SET_ELEM:
+ return range_expr_value_high(rop, expr->key);
default:
BUG("invalid range expression type %s\n", expr->ops->name);
}
diff --git a/src/netlink.c b/src/netlink.c
index 343d8bea..d31387f8 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -205,6 +205,7 @@ struct nft_set *alloc_nft_set(const struct handle *h)
static struct nft_set_elem *alloc_nft_setelem(const struct expr *expr)
{
+ const struct expr *elem, *key, *data;
struct nft_set_elem *nlse;
struct nft_data_linearize nld;
@@ -212,24 +213,34 @@ static struct nft_set_elem *alloc_nft_setelem(const struct expr *expr)
if (nlse == NULL)
memory_allocation_error();
- if (expr->ops->type == EXPR_VALUE ||
- expr->flags & EXPR_F_INTERVAL_END) {
- netlink_gen_data(expr, &nld);
- nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_KEY,
- &nld.value, nld.len);
+ data = NULL;
+ if (expr->ops->type == EXPR_MAPPING) {
+ elem = expr->left;
+ if (!(expr->flags & EXPR_F_INTERVAL_END))
+ data = expr->right;
} else {
- assert(expr->ops->type == EXPR_MAPPING);
- netlink_gen_data(expr->left, &nld);
- nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_KEY,
- &nld.value, nld.len);
- netlink_gen_data(expr->right, &nld);
- switch (expr->right->ops->type) {
+ elem = expr;
+ }
+ key = elem->key;
+
+ netlink_gen_data(key, &nld);
+ nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_KEY, &nld.value, nld.len);
+ if (elem->timeout)
+ nft_set_elem_attr_set_u64(nlse, NFT_SET_ELEM_ATTR_TIMEOUT,
+ elem->timeout);
+ if (elem->comment)
+ nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_USERDATA,
+ elem->comment, strlen(elem->comment) + 1);
+
+ if (data != NULL) {
+ netlink_gen_data(data, &nld);
+ switch (data->ops->type) {
case EXPR_VERDICT:
nft_set_elem_attr_set_u32(nlse, NFT_SET_ELEM_ATTR_VERDICT,
- expr->right->verdict);
- if (expr->chain != NULL)
+ data->verdict);
+ if (data->chain != NULL)
nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_CHAIN,
- nld.chain, strlen(nld.chain));
+ nld.chain, strlen(nld.chain));
break;
case EXPR_VALUE:
nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_DATA,
@@ -1063,6 +1074,11 @@ static struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
set->datalen = data_len * BITS_PER_BYTE;
}
+ if (nft_set_attr_is_set(nls, NFT_SET_ATTR_TIMEOUT))
+ set->timeout = nft_set_attr_get_u64(nls, NFT_SET_ATTR_TIMEOUT);
+ if (nft_set_attr_is_set(nls, NFT_SET_ATTR_GC_INTERVAL))
+ set->gc_int = nft_set_attr_get_u32(nls, NFT_SET_ATTR_GC_INTERVAL);
+
if (nft_set_attr_is_set(nls, NFT_SET_ATTR_POLICY))
set->policy = nft_set_attr_get_u32(nls, NFT_SET_ATTR_POLICY);
@@ -1126,6 +1142,11 @@ static int netlink_add_set_batch(struct netlink_ctx *ctx,
nft_set_attr_set_u32(nls, NFT_SET_ATTR_DATA_LEN,
set->datalen / BITS_PER_BYTE);
}
+ if (set->timeout)
+ nft_set_attr_set_u64(nls, NFT_SET_ATTR_TIMEOUT, set->timeout);
+ if (set->gc_int)
+ nft_set_attr_set_u32(nls, NFT_SET_ATTR_GC_INTERVAL, set->gc_int);
+
set->handle.set_id = ++set_id;
nft_set_attr_set_u32(nls, NFT_SET_ATTR_ID, set->handle.set_id);
@@ -1368,7 +1389,7 @@ static int netlink_delinearize_setelem(struct nft_set_elem *nlse,
struct set *set)
{
struct nft_data_delinearize nld;
- struct expr *expr, *data;
+ struct expr *expr, *key, *data;
uint32_t flags = 0;
nld.value =
@@ -1376,17 +1397,31 @@ static int netlink_delinearize_setelem(struct nft_set_elem *nlse,
if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_FLAGS))
flags = nft_set_elem_attr_get_u32(nlse, NFT_SET_ELEM_ATTR_FLAGS);
- expr = netlink_alloc_value(&netlink_location, &nld);
- expr->dtype = set->keytype;
- expr->byteorder = set->keytype->byteorder;
+ key = netlink_alloc_value(&netlink_location, &nld);
+ key->dtype = set->keytype;
+ key->byteorder = set->keytype->byteorder;
if (!(set->flags & SET_F_INTERVAL) &&
- expr->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
-
- if (expr->dtype->basetype != NULL &&
- expr->dtype->basetype->type == TYPE_BITMASK)
- expr = bitmask_expr_to_binops(expr);
+ key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(key->value, key->len / BITS_PER_BYTE);
+
+ if (key->dtype->basetype != NULL &&
+ key->dtype->basetype->type == TYPE_BITMASK)
+ key = bitmask_expr_to_binops(key);
+
+ expr = set_elem_expr_alloc(&netlink_location, key);
+ if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_TIMEOUT))
+ expr->timeout = nft_set_elem_attr_get_u64(nlse, NFT_SET_ELEM_ATTR_TIMEOUT);
+ if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_EXPIRATION))
+ expr->expiration = nft_set_elem_attr_get_u64(nlse, NFT_SET_ELEM_ATTR_EXPIRATION);
+ if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_USERDATA)) {
+ const void *data;
+ uint32_t len;
+
+ data = nft_set_elem_attr_get(nlse, NFT_SET_ELEM_ATTR_USERDATA, &len);
+ expr->comment = xmalloc(len);
+ memcpy((char *)expr->comment, data, len);
+ }
if (flags & NFT_SET_ELEM_INTERVAL_END) {
expr->flags |= EXPR_F_INTERVAL_END;
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index b23d5875..0645ea8b 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -692,6 +692,40 @@ static void netlink_parse_queue(struct netlink_parse_ctx *ctx,
list_add_tail(&stmt->list, &ctx->rule->stmts);
}
+static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nft_rule_expr *nle)
+{
+ struct expr *expr;
+ struct stmt *stmt;
+ struct set *set;
+ enum nft_registers sreg;
+ const char *name;
+
+ name = nft_rule_expr_get_str(nle, NFT_EXPR_DYNSET_SET_NAME);
+ set = set_lookup(ctx->table, name);
+ if (set == NULL)
+ return netlink_error(ctx, loc,
+ "Unknown set '%s' in dynset statement",
+ name);
+
+ sreg = netlink_parse_register(nle, NFT_EXPR_DYNSET_SREG_KEY);
+ expr = netlink_get_register(ctx, loc, sreg);
+ if (expr == NULL)
+ return netlink_error(ctx, loc,
+ "Dynset statement has no key expression");
+
+ expr = set_elem_expr_alloc(&expr->location, expr);
+ expr->timeout = nft_rule_expr_get_u64(nle, NFT_EXPR_DYNSET_TIMEOUT);
+
+ stmt = set_stmt_alloc(loc);
+ stmt->set.set = set_ref_expr_alloc(loc, set);
+ stmt->set.op = nft_rule_expr_get_u32(nle, NFT_EXPR_DYNSET_OP);
+ stmt->set.key = expr;
+
+ list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
static const struct {
const char *name;
void (*parse)(struct netlink_parse_ctx *ctx,
@@ -715,6 +749,7 @@ static const struct {
{ .name = "masq", .parse = netlink_parse_masq },
{ .name = "redir", .parse = netlink_parse_redir },
{ .name = "queue", .parse = netlink_parse_queue },
+ { .name = "dynset", .parse = netlink_parse_dynset },
};
static int netlink_parse_expr(struct nft_rule_expr *nle, void *arg)
@@ -743,6 +778,7 @@ struct rule_pp_ctx {
struct proto_ctx pctx;
enum proto_bases pbase;
struct stmt *pdep;
+ struct stmt *stmt;
};
/*
@@ -791,48 +827,57 @@ static void integer_type_postprocess(struct expr *expr)
}
}
-static void payload_match_postprocess(struct rule_pp_ctx *ctx,
- struct stmt *stmt, struct expr *expr)
+static void payload_match_expand(struct rule_pp_ctx *ctx, struct expr *expr)
{
struct expr *left = expr->left, *right = expr->right, *tmp;
struct list_head list = LIST_HEAD_INIT(list);
struct stmt *nstmt;
struct expr *nexpr;
+ payload_expr_expand(&list, left, &ctx->pctx);
+ list_for_each_entry(left, &list, list) {
+ tmp = constant_expr_splice(right, left->len);
+ expr_set_type(tmp, left->dtype, left->byteorder);
+ if (tmp->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE);
+
+ nexpr = relational_expr_alloc(&expr->location, expr->op,
+ left, tmp);
+ if (expr->op == OP_EQ)
+ left->ops->pctx_update(&ctx->pctx, nexpr);
+
+ nstmt = expr_stmt_alloc(&ctx->stmt->location, nexpr);
+ list_add_tail(&nstmt->list, &ctx->stmt->list);
+
+ /* Remember the first payload protocol expression to
+ * kill it later on if made redundant by a higher layer
+ * payload expression.
+ */
+ if (ctx->pbase == PROTO_BASE_INVALID &&
+ left->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(ctx, nstmt,
+ left->payload.base);
+ else
+ payload_dependency_kill(ctx, nexpr->left);
+ }
+ list_del(&ctx->stmt->list);
+ stmt_free(ctx->stmt);
+ ctx->stmt = NULL;
+}
+
+static void payload_match_postprocess(struct rule_pp_ctx *ctx,
+ struct expr *expr)
+{
switch (expr->op) {
case OP_EQ:
case OP_NEQ:
- payload_expr_expand(&list, left, &ctx->pctx);
- list_for_each_entry(left, &list, list) {
- tmp = constant_expr_splice(right, left->len);
- expr_set_type(tmp, left->dtype, left->byteorder);
- if (tmp->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE);
-
- nexpr = relational_expr_alloc(&expr->location, expr->op,
- left, tmp);
- if (expr->op == OP_EQ)
- left->ops->pctx_update(&ctx->pctx, nexpr);
-
- nstmt = expr_stmt_alloc(&stmt->location, nexpr);
- list_add_tail(&nstmt->list, &stmt->list);
-
- /* Remember the first payload protocol expression to
- * kill it later on if made redundant by a higher layer
- * payload expression.
- */
- if (ctx->pbase == PROTO_BASE_INVALID &&
- left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(ctx, nstmt,
- left->payload.base);
- else
- payload_dependency_kill(ctx, nexpr->left);
+ if (expr->right->ops->type == EXPR_VALUE) {
+ payload_match_expand(ctx, expr);
+ break;
}
- list_del(&stmt->list);
- stmt_free(stmt);
- break;
+ /* Fall through */
default:
- payload_expr_complete(left, &ctx->pctx);
+ payload_expr_complete(expr->left, &ctx->pctx);
expr_set_type(expr->right, expr->left->dtype,
expr->left->byteorder);
payload_dependency_kill(ctx, expr->left);
@@ -841,7 +886,6 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
}
static void meta_match_postprocess(struct rule_pp_ctx *ctx,
- struct stmt *stmt,
const struct expr *expr)
{
struct expr *left = expr->left;
@@ -852,7 +896,8 @@ static void meta_match_postprocess(struct rule_pp_ctx *ctx,
if (ctx->pbase == PROTO_BASE_INVALID &&
left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(ctx, stmt, left->meta.base);
+ payload_dependency_store(ctx, ctx->stmt,
+ left->meta.base);
break;
case OP_LOOKUP:
expr_set_type(expr->right, expr->left->dtype,
@@ -936,8 +981,7 @@ static void relational_binop_postprocess(struct expr *expr)
}
}
-static void expr_postprocess(struct rule_pp_ctx *ctx,
- struct stmt *stmt, struct expr **exprp)
+static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp, *i;
@@ -945,29 +989,29 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
switch (expr->ops->type) {
case EXPR_MAP:
- expr_postprocess(ctx, stmt, &expr->map);
- expr_postprocess(ctx, stmt, &expr->mappings);
+ expr_postprocess(ctx, &expr->map);
+ expr_postprocess(ctx, &expr->mappings);
break;
case EXPR_MAPPING:
- expr_postprocess(ctx, stmt, &expr->left);
- expr_postprocess(ctx, stmt, &expr->right);
+ expr_postprocess(ctx, &expr->left);
+ expr_postprocess(ctx, &expr->right);
break;
case EXPR_SET:
list_for_each_entry(i, &expr->expressions, list)
- expr_postprocess(ctx, stmt, &i);
+ expr_postprocess(ctx, &i);
break;
case EXPR_UNARY:
- expr_postprocess(ctx, stmt, &expr->arg);
+ expr_postprocess(ctx, &expr->arg);
expr_set_type(expr->arg, expr->arg->dtype, !expr->arg->byteorder);
*exprp = expr_get(expr->arg);
expr_free(expr);
break;
case EXPR_BINOP:
- expr_postprocess(ctx, stmt, &expr->left);
+ expr_postprocess(ctx, &expr->left);
expr_set_type(expr->right, expr->left->dtype,
expr->left->byteorder);
- expr_postprocess(ctx, stmt, &expr->right);
+ expr_postprocess(ctx, &expr->right);
expr_set_type(expr, expr->left->dtype,
expr->left->byteorder);
@@ -975,19 +1019,19 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
case EXPR_RELATIONAL:
switch (expr->left->ops->type) {
case EXPR_PAYLOAD:
- payload_match_postprocess(ctx, stmt, expr);
+ payload_match_postprocess(ctx, expr);
return;
default:
- expr_postprocess(ctx, stmt, &expr->left);
+ expr_postprocess(ctx, &expr->left);
break;
}
expr_set_type(expr->right, expr->left->dtype, expr->left->byteorder);
- expr_postprocess(ctx, stmt, &expr->right);
+ expr_postprocess(ctx, &expr->right);
switch (expr->left->ops->type) {
case EXPR_META:
- meta_match_postprocess(ctx, stmt, expr);
+ meta_match_postprocess(ctx, expr);
break;
case EXPR_BINOP:
relational_binop_postprocess(expr);
@@ -1028,8 +1072,11 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
break;
case EXPR_RANGE:
- expr_postprocess(ctx, stmt, &expr->left);
- expr_postprocess(ctx, stmt, &expr->right);
+ expr_postprocess(ctx, &expr->left);
+ expr_postprocess(ctx, &expr->right);
+ break;
+ case EXPR_SET_ELEM:
+ expr_postprocess(ctx, &expr->key);
break;
case EXPR_SET_REF:
case EXPR_EXTHDR:
@@ -1042,18 +1089,19 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
}
}
-static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
+static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
{
const struct proto_desc *desc, *base;
+ struct stmt *stmt = rctx->stmt;
int protocol;
- switch (rctx.pctx.family) {
+ switch (rctx->pctx.family) {
case NFPROTO_IPV4:
- stmt->reject.family = rctx.pctx.family;
+ stmt->reject.family = rctx->pctx.family;
stmt->reject.expr->dtype = &icmp_code_type;
break;
case NFPROTO_IPV6:
- stmt->reject.family = rctx.pctx.family;
+ stmt->reject.family = rctx->pctx.family;
stmt->reject.expr->dtype = &icmpv6_code_type;
break;
case NFPROTO_INET:
@@ -1061,8 +1109,8 @@ static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
stmt->reject.expr->dtype = &icmpx_code_type;
break;
}
- base = rctx.pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx.pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
@@ -1079,8 +1127,8 @@ static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
stmt->reject.expr->dtype = &icmpx_code_type;
break;
}
- base = rctx.pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx.pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
@@ -1100,44 +1148,119 @@ static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
}
}
+static bool expr_may_merge_range(struct expr *expr, struct expr *prev,
+ enum ops *op)
+{
+ struct expr *left, *prev_left;
+
+ if (prev->ops->type == EXPR_RELATIONAL &&
+ expr->ops->type == EXPR_RELATIONAL) {
+ /* ct and meta needs an unary to swap byteorder, in this case
+ * we have to explore the inner branch in this tree.
+ */
+ if (expr->left->ops->type == EXPR_UNARY)
+ left = expr->left->arg;
+ else
+ left = expr->left;
+
+ if (prev->left->ops->type == EXPR_UNARY)
+ prev_left = prev->left->arg;
+ else
+ prev_left = prev->left;
+
+ if (left->ops->type == prev_left->ops->type) {
+ if (expr->op == OP_LTE && prev->op == OP_GTE) {
+ *op = OP_EQ;
+ return true;
+ } else if (expr->op == OP_GT && prev->op == OP_LT) {
+ *op = OP_NEQ;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static void expr_postprocess_range(struct rule_pp_ctx *ctx, struct stmt *prev,
+ enum ops op)
+{
+ struct stmt *nstmt, *stmt = ctx->stmt;
+ struct expr *nexpr, *rel;
+
+ nexpr = range_expr_alloc(&prev->location, expr_clone(prev->expr->right),
+ expr_clone(stmt->expr->right));
+ expr_set_type(nexpr, stmt->expr->right->dtype,
+ stmt->expr->right->byteorder);
+
+ rel = relational_expr_alloc(&prev->location, op,
+ expr_clone(stmt->expr->left), nexpr);
+
+ nstmt = expr_stmt_alloc(&stmt->location, rel);
+ list_add_tail(&nstmt->list, &stmt->list);
+
+ list_del(&prev->list);
+ stmt_free(prev);
+
+ list_del(&stmt->list);
+ stmt_free(stmt);
+ ctx->stmt = nstmt;
+}
+
+static void stmt_expr_postprocess(struct rule_pp_ctx *ctx, struct stmt *prev)
+{
+ enum ops op;
+
+ if (prev && ctx->stmt->ops->type == prev->ops->type &&
+ expr_may_merge_range(ctx->stmt->expr, prev->expr, &op))
+ expr_postprocess_range(ctx, prev, op);
+
+ expr_postprocess(ctx, &ctx->stmt->expr);
+}
+
static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
{
struct rule_pp_ctx rctx;
- struct stmt *stmt, *next;
+ struct stmt *stmt, *next, *prev = NULL;
memset(&rctx, 0, sizeof(rctx));
proto_ctx_init(&rctx.pctx, rule->handle.family);
list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
+ rctx.stmt = stmt;
+
switch (stmt->ops->type) {
case STMT_EXPRESSION:
- expr_postprocess(&rctx, stmt, &stmt->expr);
+ stmt_expr_postprocess(&rctx, prev);
break;
case STMT_META:
if (stmt->meta.expr != NULL)
- expr_postprocess(&rctx, stmt, &stmt->meta.expr);
+ expr_postprocess(&rctx, &stmt->meta.expr);
break;
case STMT_CT:
if (stmt->ct.expr != NULL)
- expr_postprocess(&rctx, stmt, &stmt->ct.expr);
+ expr_postprocess(&rctx, &stmt->ct.expr);
break;
case STMT_NAT:
if (stmt->nat.addr != NULL)
- expr_postprocess(&rctx, stmt, &stmt->nat.addr);
+ expr_postprocess(&rctx, &stmt->nat.addr);
if (stmt->nat.proto != NULL)
- expr_postprocess(&rctx, stmt, &stmt->nat.proto);
+ expr_postprocess(&rctx, &stmt->nat.proto);
break;
case STMT_REDIR:
if (stmt->redir.proto != NULL)
- expr_postprocess(&rctx, stmt,
- &stmt->redir.proto);
+ expr_postprocess(&rctx, &stmt->redir.proto);
break;
case STMT_REJECT:
- stmt_reject_postprocess(rctx, stmt);
+ stmt_reject_postprocess(&rctx);
+ break;
+ case STMT_SET:
+ expr_postprocess(&rctx, &stmt->set.key);
break;
default:
break;
}
+ prev = rctx.stmt;
}
}
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 0a0b6864..fbc6ae12 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -529,6 +529,8 @@ static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
return netlink_gen_meta(ctx, expr, dreg);
case EXPR_CT:
return netlink_gen_ct(ctx, expr, dreg);
+ case EXPR_SET_ELEM:
+ return netlink_gen_expr(ctx, expr->key, dreg);
default:
BUG("unknown expression type %s\n", expr->ops->name);
}
@@ -799,6 +801,28 @@ static void netlink_gen_ct_stmt(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx->nlr, nle);
}
+static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ struct nft_rule_expr *nle;
+ enum nft_registers sreg_key;
+
+ sreg_key = get_register(ctx);
+ netlink_gen_expr(ctx, stmt->set.key, sreg_key);
+ release_register(ctx);
+
+ nle = alloc_nft_expr("dynset");
+ netlink_put_register(nle, NFT_EXPR_DYNSET_SREG_KEY, sreg_key);
+ nft_rule_expr_set_u64(nle, NFT_EXPR_DYNSET_TIMEOUT,
+ stmt->set.key->timeout);
+ nft_rule_expr_set_u32(nle, NFT_EXPR_DYNSET_OP, stmt->set.op);
+ nft_rule_expr_set_str(nle, NFT_EXPR_DYNSET_SET_NAME,
+ stmt->set.set->set->handle.set);
+ nft_rule_expr_set_u32(nle, NFT_EXPR_DYNSET_SET_ID,
+ stmt->set.set->set->handle.set_id);
+ nft_rule_add_expr(ctx->nlr, nle);
+}
+
static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
@@ -827,6 +851,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_queue_stmt(ctx, stmt);
case STMT_CT:
return netlink_gen_ct_stmt(ctx, stmt);
+ case STMT_SET:
+ return netlink_gen_set_stmt(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index b86381d9..eac3fcbe 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -181,6 +181,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%token INET "inet"
%token ADD "add"
+%token UPDATE "update"
%token CREATE "create"
%token INSERT "insert"
%token DELETE "delete"
@@ -201,6 +202,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%token CONSTANT "constant"
%token INTERVAL "interval"
+%token TIMEOUT "timeout"
+%token GC_INTERVAL "gc-interval"
%token ELEMENTS "elements"
%token POLICY "policy"
@@ -396,6 +399,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <string> identifier string comment_spec
%destructor { xfree($$); } identifier string comment_spec
+%type <val> time_spec
+
%type <val> type_identifier
%type <datatype> data_type
@@ -452,6 +457,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <stmt> queue_stmt queue_stmt_alloc
%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc
%type <val> queue_stmt_flags queue_stmt_flag
+%type <stmt> set_stmt
+%destructor { stmt_free($$); } set_stmt
+%type <val> set_stmt_op
%type <expr> symbol_expr verdict_expr integer_expr
%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr
@@ -468,8 +476,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%destructor { expr_free($$); } prefix_expr range_expr wildcard_expr
%type <expr> list_expr
%destructor { expr_free($$); } list_expr
-%type <expr> concat_expr map_lhs_expr
-%destructor { expr_free($$); } concat_expr map_lhs_expr
+%type <expr> concat_expr
+%destructor { expr_free($$); } concat_expr
%type <expr> map_expr
%destructor { expr_free($$); } map_expr
@@ -482,6 +490,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <expr> set_expr set_list_expr set_list_member_expr
%destructor { expr_free($$); } set_expr set_list_expr set_list_member_expr
+%type <expr> set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
+%destructor { expr_free($$); } set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
%type <expr> expr initializer_expr
%destructor { expr_free($$); } expr initializer_expr
@@ -940,6 +950,16 @@ set_block : /* empty */ { $$ = $<set>-1; }
$1->flags = $3;
$$ = $1;
}
+ | set_block TIMEOUT time_spec stmt_seperator
+ {
+ $1->timeout = $3 * 1000;
+ $$ = $1;
+ }
+ | set_block GC_INTERVAL time_spec stmt_seperator
+ {
+ $1->gc_int = $3 * 1000;
+ $$ = $1;
+ }
| set_block ELEMENTS '=' set_expr
{
$1->init = $4;
@@ -957,6 +977,7 @@ set_flag_list : set_flag_list COMMA set_flag
set_flag : CONSTANT { $$ = SET_F_CONSTANT; }
| INTERVAL { $$ = SET_F_INTERVAL; }
+ | TIMEOUT { $$ = SET_F_TIMEOUT; }
;
map_block_alloc : /* empty */
@@ -1093,6 +1114,20 @@ string : STRING
| QUOTED_STRING
;
+time_spec : STRING
+ {
+ struct error_record *erec;
+ uint64_t res;
+
+ erec = time_parse(&@1, $1, &res);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = res;
+ }
+ ;
+
family_spec : /* empty */ { $$ = NFPROTO_IPV4; }
| family_spec_explicit
;
@@ -1236,6 +1271,7 @@ stmt : verdict_stmt
| ct_stmt
| masq_stmt
| redir_stmt
+ | set_stmt
;
verdict_stmt : verdict_expr
@@ -1281,13 +1317,12 @@ verdict_map_list_expr : verdict_map_list_member_expr
| verdict_map_list_expr COMMA opt_newline
;
-verdict_map_list_member_expr: opt_newline map_lhs_expr COLON verdict_expr opt_newline
+verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_newline
{
$$ = mapping_expr_alloc(&@$, $2, $4);
}
;
-
counter_stmt : counter_stmt_alloc
| counter_stmt_alloc counter_args
@@ -1549,6 +1584,19 @@ queue_stmt_flag : BYPASS { $$ = NFT_QUEUE_FLAG_BYPASS; }
| FANOUT { $$ = NFT_QUEUE_FLAG_CPU_FANOUT; }
;
+set_stmt : SET set_stmt_op set_elem_expr symbol_expr
+ {
+ $$ = set_stmt_alloc(&@$);
+ $$->set.op = $2;
+ $$->set.key = $3;
+ $$->set.set = $4;
+ }
+ ;
+
+set_stmt_op : ADD { $$ = NFT_DYNSET_OP_ADD; }
+ | UPDATE { $$ = NFT_DYNSET_OP_UPDATE; }
+ ;
+
match_stmt : relational_expr
{
$$ = expr_stmt_alloc(&@$, $1);
@@ -1702,10 +1750,6 @@ multiton_expr : prefix_expr
| wildcard_expr
;
-map_lhs_expr : multiton_expr
- | concat_expr
- ;
-
map_expr : concat_expr MAP expr
{
$$ = map_expr_alloc(&@$, $1, $3);
@@ -1713,9 +1757,9 @@ map_expr : concat_expr MAP expr
;
expr : concat_expr
+ | multiton_expr
| set_expr
| map_expr
- | multiton_expr
;
set_expr : '{' set_list_expr '}'
@@ -1738,20 +1782,55 @@ set_list_expr : set_list_member_expr
| set_list_expr COMMA opt_newline
;
-set_list_member_expr : opt_newline expr opt_newline
+set_list_member_expr : opt_newline set_expr opt_newline
{
$$ = $2;
}
- | opt_newline map_lhs_expr COLON concat_expr opt_newline
+ | opt_newline set_elem_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = $2;
}
- | opt_newline map_lhs_expr COLON verdict_expr opt_newline
+ | opt_newline set_elem_expr COLON set_rhs_expr opt_newline
{
$$ = mapping_expr_alloc(&@$, $2, $4);
}
;
+set_elem_expr : set_elem_expr_alloc
+ | set_elem_expr_alloc set_elem_options
+ ;
+
+set_elem_expr_alloc : set_lhs_expr
+ {
+ $$ = set_elem_expr_alloc(&@1, $1);
+ }
+ ;
+
+set_elem_options : set_elem_option
+ {
+ $<expr>$ = $<expr>0;
+ }
+ | set_elem_options set_elem_option
+ ;
+
+set_elem_option : TIMEOUT time_spec
+ {
+ $<expr>0->timeout = $2 * 1000;
+ }
+ | COMMENT string
+ {
+ $<expr>0->comment = $2;
+ }
+ ;
+
+set_lhs_expr : concat_expr
+ | multiton_expr
+ ;
+
+set_rhs_expr : concat_expr
+ | verdict_expr
+ ;
+
initializer_expr : expr
| list_expr
;
diff --git a/src/rule.c b/src/rule.c
index 71143807..b2090ddd 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -136,6 +136,7 @@ static void do_set_print(const struct set *set, struct print_fmt_options *opts)
{
const char *delim = "";
const char *type;
+ uint32_t flags;
type = set->flags & SET_F_MAP ? "map" : "set";
printf("%s%s", opts->tab, type);
@@ -167,7 +168,12 @@ static void do_set_print(const struct set *set, struct print_fmt_options *opts)
}
}
- if (set->flags & (SET_F_CONSTANT | SET_F_INTERVAL)) {
+ flags = set->flags;
+ /* "timeout" flag is redundant if a default timeout exists */
+ if (set->timeout)
+ flags &= ~SET_F_TIMEOUT;
+
+ if (flags & (SET_F_CONSTANT | SET_F_INTERVAL | SET_F_TIMEOUT)) {
printf("%s%sflags ", opts->tab, opts->tab);
if (set->flags & SET_F_CONSTANT) {
printf("%sconstant", delim);
@@ -177,6 +183,21 @@ static void do_set_print(const struct set *set, struct print_fmt_options *opts)
printf("%sinterval", delim);
delim = ",";
}
+ if (set->flags & SET_F_TIMEOUT) {
+ printf("%stimeout", delim);
+ delim = ",";
+ }
+ printf("%s", opts->nl);
+ }
+
+ if (set->timeout) {
+ printf("%s%stimeout ", opts->tab, opts->tab);
+ time_print(set->timeout / 1000);
+ printf("%s", opts->nl);
+ }
+ if (set->gc_int) {
+ printf("%s%sgc-interval ", opts->tab, opts->tab);
+ time_print(set->gc_int / 1000);
printf("%s", opts->nl);
}
diff --git a/src/scanner.l b/src/scanner.l
index 73c4f8b1..985ea2a3 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -117,6 +117,8 @@ quotedstring \"[^"]*\"
comment #.*$
slash \/
+timestring ([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?
+
hex4 ([[:xdigit:]]{1,4})
v680 (({hex4}:){7}{hex4})
v670 ((:)((:{hex4}){7}))
@@ -255,6 +257,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"inet" { return INET; }
"add" { return ADD; }
+"update" { return UPDATE; }
"create" { return CREATE; }
"insert" { return INSERT; }
"delete" { return DELETE; }
@@ -269,6 +272,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"constant" { return CONSTANT; }
"interval" { return INTERVAL; }
+"timeout" { return TIMEOUT; }
+"gc-interval" { return GC_INTERVAL; }
"elements" { return ELEMENTS; }
"policy" { return POLICY; }
@@ -457,6 +462,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
return STRING;
}
+{timestring} {
+ yylval->string = xstrdup(yytext);
+ return STRING;
+ }
+
{decstring} {
errno = 0;
yylval->val = strtoull(yytext, NULL, 0);
diff --git a/src/segtree.c b/src/segtree.c
index 65221e9d..060951c0 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -419,6 +419,7 @@ static void set_insert_interval(struct expr *set, struct seg_tree *tree,
expr = constant_expr_alloc(&internal_location, tree->keytype,
tree->byteorder, tree->keylen, NULL);
mpz_set(expr->value, ei->left);
+ expr = set_elem_expr_alloc(&internal_location, expr);
if (ei->expr != NULL && ei->expr->ops->type == EXPR_MAPPING)
expr = mapping_expr_alloc(&ei->expr->location, expr,
@@ -473,9 +474,9 @@ extern void interval_map_decompose(struct expr *set);
static struct expr *expr_value(struct expr *expr)
{
if (expr->ops->type == EXPR_MAPPING)
- return expr->left;
+ return expr->left->key;
else
- return expr;
+ return expr->key;
}
static int expr_value_cmp(const void *p1, const void *p2)
@@ -565,6 +566,7 @@ void interval_map_decompose(struct expr *set)
mpz_set(tmp->value, range);
tmp = range_expr_alloc(&low->location, expr_value(low), tmp);
+ tmp = set_elem_expr_alloc(&low->location, tmp);
if (low->ops->type == EXPR_MAPPING)
tmp = mapping_expr_alloc(&tmp->location, tmp, low->right);
@@ -576,6 +578,7 @@ void interval_map_decompose(struct expr *set)
prefix_len = expr_value(i)->len - mpz_scan0(range, 0);
prefix = prefix_expr_alloc(&low->location, expr_value(low),
prefix_len);
+ prefix = set_elem_expr_alloc(&low->location, prefix);
if (low->ops->type == EXPR_MAPPING)
prefix = mapping_expr_alloc(&low->location, prefix,
low->right);
@@ -598,6 +601,7 @@ void interval_map_decompose(struct expr *set)
mpz_init_bitmask(i->value, i->len);
i = range_expr_alloc(&low->location, expr_value(low), i);
+ i = set_elem_expr_alloc(&low->location, i);
if (low->ops->type == EXPR_MAPPING)
i = mapping_expr_alloc(&i->location, i, low->right);
diff --git a/src/statement.c b/src/statement.c
index d72c6e9b..9ebc5938 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -377,3 +377,34 @@ struct stmt *redir_stmt_alloc(const struct location *loc)
{
return stmt_alloc(loc, &redir_stmt_ops);
}
+
+static const char * const set_stmt_op_names[] = {
+ [NFT_DYNSET_OP_ADD] = "add",
+ [NFT_DYNSET_OP_UPDATE] = "update",
+};
+
+static void set_stmt_print(const struct stmt *stmt)
+{
+ printf("set %s ", set_stmt_op_names[stmt->set.op]);
+ expr_print(stmt->set.key);
+ printf(" ");
+ expr_print(stmt->set.set);
+}
+
+static void set_stmt_destroy(struct stmt *stmt)
+{
+ expr_free(stmt->set.key);
+ expr_free(stmt->set.set);
+}
+
+static const struct stmt_ops set_stmt_ops = {
+ .type = STMT_SET,
+ .name = "set",
+ .print = set_stmt_print,
+ .destroy = set_stmt_destroy,
+};
+
+struct stmt *set_stmt_alloc(const struct location *loc)
+{
+ return stmt_alloc(loc, &set_stmt_ops);
+}