uint64_t timeout;
uint64_t expiration;
const char *comment;
+ struct stmt *stmt;
};
struct {
/* EXPR_UNARY */
extern int netlink_get_set(struct netlink_ctx *ctx, const struct handle *h,
const struct location *loc);
+extern struct stmt *netlink_parse_set_expr(const struct set *set,
+ const struct nftnl_expr *nle);
+
extern int netlink_add_setelems(struct netlink_ctx *ctx, const struct handle *h,
const struct expr *expr);
extern int netlink_delete_setelems(struct netlink_ctx *ctx, const struct handle *h,
SET_F_INTERVAL = 0x4,
SET_F_MAP = 0x8,
SET_F_TIMEOUT = 0x10,
+ SET_F_EVAL = 0x20,
};
/**
extern struct stmt *set_stmt_alloc(const struct location *loc);
+struct flow_stmt {
+ struct expr *set;
+ struct expr *key;
+ struct stmt *stmt;
+ const char *table;
+};
+
+extern struct stmt *flow_stmt_alloc(const struct location *loc);
+
/**
* enum stmt_types - statement types
*
* @STMT_INVALID: uninitialised
* @STMT_EXPRESSION: expression statement (relational)
* @STMT_VERDICT: verdict statement
+ * @STMT_FLOW: flow statement
* @STMT_COUNTER: counters
* @STMT_PAYLOAD: payload statement
* @STMT_META: meta statement
STMT_INVALID,
STMT_EXPRESSION,
STMT_VERDICT,
+ STMT_FLOW,
STMT_COUNTER,
STMT_PAYLOAD,
STMT_META,
union {
struct expr *expr;
+ struct flow_stmt flow;
struct counter_stmt counter;
struct payload_stmt payload;
struct meta_stmt meta;
&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,
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:
#include <limits.h>
#include <expression.h>
+#include <statement.h>
#include <datatype.h>
#include <rule.h>
#include <gmputil.h>
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)
}
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)
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;
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, ...)
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;
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;
}
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;
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);
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;
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);
}
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:
%token PERFORMANCE "performance"
%token SIZE "size"
+%token FLOW "flow"
+
%token <val> NUM "number"
%token <string> STRING "string"
%token <string> QUOTED_STRING
%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
%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
stmt : verdict_stmt
| match_stmt
+ | flow_stmt
| counter_stmt
| payload_stmt
| meta_stmt
| 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);
}
;
+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
;
"performance" { return PERFORMANCE; }
"memory" { return MEMORY; }
+"flow" { return FLOW; }
+
"counter" { return COUNTER; }
"packets" { return PACKETS; }
"bytes" { return BYTES; }
void stmt_free(struct stmt *stmt)
{
+ if (stmt == NULL)
+ return;
if (stmt->ops->destroy)
stmt->ops->destroy(stmt);
xfree(stmt);
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,