summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/evaluate.c37
-rw-r--r--src/expression.c16
-rw-r--r--src/netlink.c6
-rw-r--r--src/netlink_delinearize.c49
-rw-r--r--src/netlink_linearize.c40
-rw-r--r--src/parser_bison.y57
-rw-r--r--src/scanner.l2
-rw-r--r--src/statement.c33
8 files changed, 228 insertions, 12 deletions
diff --git a/src/evaluate.c b/src/evaluate.c
index 9d89d905..53f19b29 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1638,6 +1638,41 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
&stmt->payload.val);
}
+static int stmt_evaluate_flow(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct expr *key, *set, *setref;
+
+ expr_set_context(&ctx->ectx, NULL, 0);
+ if (expr_evaluate(ctx, &stmt->flow.key) < 0)
+ return -1;
+ if (expr_is_constant(stmt->flow.key))
+ return expr_error(ctx->msgs, stmt->flow.key,
+ "Flow key expression can not be constant");
+ if (stmt->flow.key->comment)
+ return expr_error(ctx->msgs, stmt->flow.key,
+ "Flow key expression can not contain comments");
+
+ /* Declare an empty set */
+ key = stmt->flow.key;
+ set = set_expr_alloc(&key->location);
+ set->set_flags |= SET_F_EVAL;
+ if (key->timeout)
+ set->set_flags |= SET_F_TIMEOUT;
+
+ setref = implicit_set_declaration(ctx, stmt->flow.table ?: "__ft%d",
+ key->dtype, key->len, set);
+
+ stmt->flow.set = setref;
+
+ if (stmt_evaluate(ctx, stmt->flow.stmt) < 0)
+ return -1;
+ if (!(stmt->flow.stmt->flags & STMT_F_STATEFUL))
+ return stmt_binary_error(ctx, stmt->flow.stmt, stmt,
+ "Per-flow statement must be stateful");
+
+ return 0;
+}
+
static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
{
return stmt_evaluate_arg(ctx, stmt,
@@ -2257,6 +2292,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_evaluate_verdict(ctx, stmt);
case STMT_PAYLOAD:
return stmt_evaluate_payload(ctx, stmt);
+ case STMT_FLOW:
+ return stmt_evaluate_flow(ctx, stmt);
case STMT_META:
return stmt_evaluate_meta(ctx, stmt);
case STMT_CT:
diff --git a/src/expression.c b/src/expression.c
index ab195e5f..a10af5d3 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -16,6 +16,7 @@
#include <limits.h>
#include <expression.h>
+#include <statement.h>
#include <datatype.h>
#include <rule.h>
#include <gmputil.h>
@@ -852,10 +853,14 @@ struct expr *map_expr_alloc(const struct location *loc, struct expr *arg,
static void set_ref_expr_print(const struct expr *expr)
{
- if (expr->set->flags & SET_F_ANONYMOUS)
- expr_print(expr->set->init);
- else
+ if (expr->set->flags & SET_F_ANONYMOUS) {
+ if (expr->set->flags & SET_F_EVAL)
+ printf("table %s", expr->set->handle.set);
+ else
+ expr_print(expr->set->init);
+ } else {
printf("@%s", expr->set->handle.set);
+ }
}
static void set_ref_expr_clone(struct expr *new, const struct expr *expr)
@@ -899,6 +904,11 @@ static void set_elem_expr_print(const struct expr *expr)
}
if (expr->comment)
printf(" comment \"%s\"", expr->comment);
+
+ if (expr->stmt) {
+ printf(" : ");
+ stmt_print(expr->stmt);
+ }
}
static void set_elem_expr_destroy(struct expr *expr)
diff --git a/src/netlink.c b/src/netlink.c
index d727cd2d..b0dcb907 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1460,6 +1460,12 @@ static int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
expr->comment = xmalloc(len);
memcpy((char *)expr->comment, data, len);
}
+ if (nftnl_set_elem_is_set(nlse, NFT_SET_ELEM_ATTR_EXPR)) {
+ const struct nftnl_expr *nle;
+
+ nle = nftnl_set_elem_get(nlse, NFT_SET_ELEM_ATTR_EXPR, NULL);
+ expr->stmt = netlink_parse_set_expr(set, nle);
+ }
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 1767098d..de5d66c0 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -35,6 +35,9 @@ struct netlink_parse_ctx {
struct expr *registers[1 + NFT_REG32_15 - NFT_REG32_00 + 1];
};
+static int netlink_parse_expr(const struct nftnl_expr *nle,
+ struct netlink_parse_ctx *ctx);
+
static void __fmtstring(3, 4) netlink_error(struct netlink_parse_ctx *ctx,
const struct location *loc,
const char *fmt, ...)
@@ -910,8 +913,9 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
+ const struct nftnl_expr *dnle;
struct expr *expr;
- struct stmt *stmt;
+ struct stmt *stmt, *dstmt;
struct set *set;
enum nft_registers sreg;
const char *name;
@@ -938,10 +942,28 @@ 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);
- 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;
+ dstmt = NULL;
+ dnle = nftnl_expr_get(nle, NFTNL_EXPR_DYNSET_EXPR, NULL);
+ if (dnle != NULL) {
+ if (netlink_parse_expr(dnle, ctx) < 0)
+ return;
+ if (ctx->stmt == NULL)
+ return netlink_error(ctx, loc,
+ "Could not parse dynset stmt");
+ dstmt = ctx->stmt;
+ }
+
+ if (dstmt != NULL) {
+ stmt = flow_stmt_alloc(loc);
+ stmt->flow.set = set_ref_expr_alloc(loc, set);
+ stmt->flow.key = expr;
+ stmt->flow.stmt = dstmt;
+ } 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;
+ }
ctx->stmt = stmt;
}
@@ -1011,6 +1033,20 @@ static int netlink_parse_rule_expr(struct nftnl_expr *nle, void *arg)
return 0;
}
+struct stmt *netlink_parse_set_expr(const struct set *set,
+ const struct nftnl_expr *nle)
+{
+ struct netlink_parse_ctx ctx, *pctx = &ctx;
+
+ pctx->rule = rule_alloc(&netlink_location, &set->handle);
+ pctx->table = table_lookup(&set->handle);
+ assert(pctx->table != NULL);
+
+ if (netlink_parse_expr(nle, pctx) < 0)
+ return NULL;
+ return pctx->stmt;
+}
+
struct rule_pp_ctx {
struct proto_ctx pctx;
struct payload_dep_ctx pdctx;
@@ -1717,6 +1753,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
stmt->payload.expr->byteorder);
expr_postprocess(&rctx, &stmt->payload.val);
break;
+ case STMT_FLOW:
+ expr_postprocess(&rctx, &stmt->flow.key);
+ break;
case STMT_META:
if (stmt->meta.expr != NULL)
expr_postprocess(&rctx, &stmt->meta.expr);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index fbf6e2c3..62bb25c4 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1017,6 +1017,7 @@ static void netlink_gen_ct_stmt(struct netlink_linearize_ctx *ctx,
static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
+ struct set *set = stmt->flow.set->set;
struct nftnl_expr *nle;
enum nft_registers sreg_key;
@@ -1029,10 +1030,39 @@ static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT,
stmt->set.key->timeout);
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->set.op);
- nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME,
- stmt->set.set->set->handle.set);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID,
- stmt->set.set->set->handle.set_id);
+ nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
+ nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_flow_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ struct nftnl_expr *nle;
+ enum nft_registers sreg_key;
+ enum nft_dynset_ops op;
+ struct set *set;
+
+ sreg_key = get_register(ctx, stmt->flow.key);
+ netlink_gen_expr(ctx, stmt->flow.key, sreg_key);
+ release_register(ctx, stmt->flow.key);
+
+ set = stmt->flow.set->set;
+ if (stmt->flow.key->timeout)
+ op = NFT_DYNSET_OP_UPDATE;
+ else
+ op = NFT_DYNSET_OP_ADD;
+
+ nle = alloc_nft_expr("dynset");
+ netlink_put_register(nle, NFT_EXPR_DYNSET_SREG_KEY, sreg_key);
+ if (stmt->flow.key->timeout)
+ nftnl_expr_set_u64(nle, NFT_EXPR_DYNSET_TIMEOUT,
+ stmt->flow.key->timeout);
+ nftnl_expr_set_u32(nle, NFT_EXPR_DYNSET_OP, op);
+ nftnl_expr_set_str(nle, NFT_EXPR_DYNSET_SET_NAME, set->handle.set);
+ nftnl_expr_set_u32(nle, NFT_EXPR_DYNSET_SET_ID, set->handle.set_id);
+ nftnl_expr_set(nle, NFT_EXPR_DYNSET_EXPR,
+ netlink_gen_stmt_stateful(ctx, stmt->flow.stmt), 0);
nftnl_rule_add_expr(ctx->nlr, nle);
}
@@ -1046,6 +1076,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_expr(ctx, stmt->expr, NFT_REG_VERDICT);
case STMT_VERDICT:
return netlink_gen_verdict_stmt(ctx, stmt);
+ case STMT_FLOW:
+ return netlink_gen_flow_stmt(ctx, stmt);
case STMT_PAYLOAD:
return netlink_gen_payload_stmt(ctx, stmt);
case STMT_META:
diff --git a/src/parser_bison.y b/src/parser_bison.y
index b8d33861..8a7785b3 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -216,6 +216,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%token PERFORMANCE "performance"
%token SIZE "size"
+%token FLOW "flow"
+
%token <val> NUM "number"
%token <string> STRING "string"
%token <string> QUOTED_STRING
@@ -484,6 +486,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <stmt> set_stmt
%destructor { stmt_free($$); } set_stmt
%type <val> set_stmt_op
+%type <stmt> flow_stmt flow_stmt_alloc
+%destructor { stmt_free($$); } flow_stmt flow_stmt_alloc
%type <expr> symbol_expr verdict_expr integer_expr
%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr
@@ -519,6 +523,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%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> flow_key_expr flow_key_expr_alloc
+%destructor { expr_free($$); } flow_key_expr flow_key_expr_alloc
+
%type <expr> expr initializer_expr
%destructor { expr_free($$); } expr initializer_expr
@@ -1306,6 +1313,7 @@ stmt_list : stmt
stmt : verdict_stmt
| match_stmt
+ | flow_stmt
| counter_stmt
| payload_stmt
| meta_stmt
@@ -1757,6 +1765,41 @@ set_stmt_op : ADD { $$ = NFT_DYNSET_OP_ADD; }
| UPDATE { $$ = NFT_DYNSET_OP_UPDATE; }
;
+flow_stmt : flow_stmt_alloc flow_stmt_opts flow_key_expr stmt
+ {
+ $1->flow.key = $3;
+ $1->flow.stmt = $4;
+ $$->location = @$;
+ $$ = $1;
+ }
+ | flow_stmt_alloc flow_key_expr stmt
+ {
+ $1->flow.key = $2;
+ $1->flow.stmt = $3;
+ $$->location = @$;
+ $$ = $1;
+ }
+ ;
+
+flow_stmt_alloc : FLOW
+ {
+ $$ = flow_stmt_alloc(&@$);
+ }
+ ;
+
+flow_stmt_opts : flow_stmt_opt
+ {
+ $<stmt>$ = $<stmt>0;
+ }
+ | flow_stmt_opts flow_stmt_opt
+ ;
+
+flow_stmt_opt : TABLE identifier
+ {
+ $<stmt>0->flow.table = $2;
+ }
+ ;
+
match_stmt : relational_expr
{
$$ = expr_stmt_alloc(&@$, $1);
@@ -1941,6 +1984,20 @@ set_list_member_expr : opt_newline set_expr opt_newline
}
;
+flow_key_expr : flow_key_expr_alloc
+ | flow_key_expr_alloc set_elem_options
+ {
+ $$->location = @$;
+ $$ = $1;
+ }
+ ;
+
+flow_key_expr_alloc : concat_expr
+ {
+ $$ = set_elem_expr_alloc(&@1, $1);
+ }
+ ;
+
set_elem_expr : set_elem_expr_alloc
| set_elem_expr_alloc set_elem_options
;
diff --git a/src/scanner.l b/src/scanner.l
index e8b216ef..b0221145 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -285,6 +285,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"performance" { return PERFORMANCE; }
"memory" { return MEMORY; }
+"flow" { return FLOW; }
+
"counter" { return COUNTER; }
"packets" { return PACKETS; }
"bytes" { return BYTES; }
diff --git a/src/statement.c b/src/statement.c
index 41498418..988cfeb7 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -41,6 +41,8 @@ struct stmt *stmt_alloc(const struct location *loc,
void stmt_free(struct stmt *stmt)
{
+ if (stmt == NULL)
+ return;
if (stmt->ops->destroy)
stmt->ops->destroy(stmt);
xfree(stmt);
@@ -103,6 +105,37 @@ struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr)
return stmt;
}
+static void flow_stmt_print(const struct stmt *stmt)
+{
+ printf("flow ");
+ if (stmt->flow.set) {
+ expr_print(stmt->flow.set);
+ printf(" ");
+ }
+ expr_print(stmt->flow.key);
+ printf(" ");
+ stmt_print(stmt->flow.stmt);
+}
+
+static void flow_stmt_destroy(struct stmt *stmt)
+{
+ expr_free(stmt->flow.key);
+ expr_free(stmt->flow.set);
+ stmt_free(stmt->flow.stmt);
+}
+
+static const struct stmt_ops flow_stmt_ops = {
+ .type = STMT_FLOW,
+ .name = "flow",
+ .print = flow_stmt_print,
+ .destroy = flow_stmt_destroy,
+};
+
+struct stmt *flow_stmt_alloc(const struct location *loc)
+{
+ return stmt_alloc(loc, &flow_stmt_ops);
+}
+
static void counter_stmt_print(const struct stmt *stmt)
{
printf("counter packets %" PRIu64 " bytes %" PRIu64,