summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2018-08-24 09:52:22 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2018-08-24 09:52:22 +0200
commita55ca1a24b7b216144dc737f621fb68f4a924e38 (patch)
treee104f4c5d5ad34b39f58a3b05b80aba85858d0c0
parent0e90798e98121abab274434ec60f0b873f510021 (diff)
src: integrate stateful expressions into sets and maps
The following example shows how to populate a set from the packet path using the destination IP address, for each entry there is a counter. The entry expires after the 1 hour timeout if no packets matching this entry are seen. table ip x { set xyz { type ipv4_addr size 65535 flags dynamic,timeout timeout 1h } chain y { type filter hook output priority filter; policy accept; update @xyz { ip daddr counter } counter } } Similar example, that creates a mapping better IP address and mark, where the mark is assigned using an incremental sequence generator from 0 to 1 inclusive. table ip x { map xyz { type ipv4_addr : mark size 65535 flags dynamic,timeout timeout 1h } chain y { type filter hook input priority filter; policy accept; update @xyz { ip saddr counter : numgen inc mod 2 } } } Supported stateful statements are: limit, quota, counter and connlimit. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/statement.h2
-rw-r--r--src/evaluate.c14
-rw-r--r--src/expression.c7
-rw-r--r--src/netlink_delinearize.c26
-rw-r--r--src/netlink_linearize.c8
-rw-r--r--src/parser_bison.y32
-rw-r--r--src/rule.c7
-rw-r--r--src/statement.c12
8 files changed, 84 insertions, 24 deletions
diff --git a/include/statement.h b/include/statement.h
index 6c583a91..48ba6673 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -184,6 +184,7 @@ uint32_t fwd_stmt_type(const char *type);
struct set_stmt {
struct expr *set;
struct expr *key;
+ struct stmt *stmt;
enum nft_dynset_ops op;
};
@@ -195,6 +196,7 @@ struct map_stmt {
struct expr *set;
struct expr *key;
struct expr *data;
+ struct stmt *stmt;
enum nft_dynset_ops op;
};
diff --git a/src/evaluate.c b/src/evaluate.c
index 9bc67d8f..647e1606 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2708,6 +2708,13 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->set.key->comment != NULL)
return expr_error(ctx->msgs, stmt->set.key,
"Key expression comments are not supported");
+ if (stmt->set.stmt) {
+ if (stmt_evaluate(ctx, stmt->set.stmt) < 0)
+ return -1;
+ if (!(stmt->set.stmt->flags & STMT_F_STATEFUL))
+ return stmt_binary_error(ctx, stmt->set.stmt, stmt,
+ "meter statement must be stateful");
+ }
return 0;
}
@@ -2739,6 +2746,13 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->map.data->comment != NULL)
return expr_error(ctx->msgs, stmt->map.data,
"Data expression comments are not supported");
+ if (stmt->map.stmt) {
+ if (stmt_evaluate(ctx, stmt->map.stmt) < 0)
+ return -1;
+ if (!(stmt->map.stmt->flags & STMT_F_STATEFUL))
+ return stmt_binary_error(ctx, stmt->map.stmt, stmt,
+ "meter statement must be stateful");
+ }
return 0;
}
diff --git a/src/expression.c b/src/expression.c
index bea0f4c8..0bd51122 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -1045,13 +1045,12 @@ static void set_elem_expr_print(const struct expr *expr,
nft_print(octx, " expires ");
time_print(expr->expiration, octx);
}
- if (expr->comment)
- nft_print(octx, " comment \"%s\"", expr->comment);
-
if (expr->stmt) {
- nft_print(octx, " : ");
+ nft_print(octx, " ");
stmt_print(expr->stmt, octx);
}
+ if (expr->comment)
+ nft_print(octx, " comment \"%s\"", expr->comment);
}
static void set_elem_expr_destroy(struct expr *expr)
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 898c737f..6c5188cd 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1312,23 +1312,27 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
expr_data = netlink_get_register(ctx, loc, sreg_data);
}
- if (dstmt != NULL) {
- stmt = meter_stmt_alloc(loc);
- stmt->meter.set = set_ref_expr_alloc(loc, set);
- stmt->meter.key = expr;
- stmt->meter.stmt = dstmt;
- stmt->meter.size = set->desc.size;
- } else if (expr_data != NULL) {
+ if (expr_data != NULL) {
stmt = map_stmt_alloc(loc);
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);
} 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;
+ if (dstmt != NULL && 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.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;
+ }
}
ctx->stmt = stmt;
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 821fcd0a..0bd946a1 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1269,6 +1269,10 @@ static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name);
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
nftnl_rule_add_expr(ctx->nlr, nle);
+
+ if (stmt->set.stmt)
+ nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
+ netlink_gen_stmt_stateful(ctx, stmt->set.stmt), 0);
}
static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
@@ -1296,6 +1300,10 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name);
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
+ if (stmt->map.stmt)
+ nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
+ netlink_gen_stmt_stateful(ctx, stmt->map.stmt), 0);
+
nftnl_rule_add_expr(ctx->nlr, nle);
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 199ef13d..cc114717 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -561,8 +561,8 @@ int nft_lex(void *, void *, void *);
%destructor { stmt_list_free($$); xfree($$); } stmt_list
%type <stmt> stmt match_stmt verdict_stmt
%destructor { stmt_free($$); } stmt match_stmt verdict_stmt
-%type <stmt> counter_stmt counter_stmt_alloc
-%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc
+%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt
+%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt
%type <stmt> payload_stmt
%destructor { stmt_free($$); } payload_stmt
%type <stmt> ct_stmt
@@ -2112,16 +2112,19 @@ stmt_list : stmt
}
;
+stateful_stmt : counter_stmt
+ | limit_stmt
+ | quota_stmt
+ | connlimit_stmt
+ ;
+
stmt : verdict_stmt
| match_stmt
| meter_stmt
- | connlimit_stmt
- | counter_stmt
| payload_stmt
+ | stateful_stmt
| meta_stmt
| log_stmt
- | limit_stmt
- | quota_stmt
| reject_stmt
| nat_stmt
| tproxy_stmt
@@ -2862,6 +2865,14 @@ set_stmt : SET set_stmt_op set_elem_expr_stmt symbol_expr
$$->set.key = $4;
$$->set.set = $2;
}
+ | set_stmt_op symbol_expr '{' set_elem_expr_stmt stateful_stmt '}'
+ {
+ $$ = set_stmt_alloc(&@$);
+ $$->set.op = $1;
+ $$->set.key = $4;
+ $$->set.set = $2;
+ $$->set.stmt = $5;
+ }
;
set_stmt_op : ADD { $$ = NFT_DYNSET_OP_ADD; }
@@ -2876,6 +2887,15 @@ map_stmt : set_stmt_op symbol_expr '{' set_elem_expr_stmt COLON set_elem_expr_s
$$->map.data = $6;
$$->map.set = $2;
}
+ | set_stmt_op symbol_expr '{' set_elem_expr_stmt stateful_stmt COLON set_elem_expr_stmt '}'
+ {
+ $$ = map_stmt_alloc(&@$);
+ $$->map.op = $1;
+ $$->map.key = $4;
+ $$->map.data = $7;
+ $$->map.stmt = $5;
+ $$->map.set = $2;
+ }
;
meter_stmt : flow_stmt_legacy_alloc flow_stmt_opts '{' meter_key_expr stmt '}'
diff --git a/src/rule.c b/src/rule.c
index aef43638..470b112e 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -336,10 +336,11 @@ static void set_print_declaration(const struct set *set,
const char *type;
uint32_t flags;
- if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
- type = "map";
- else if (set->flags & NFT_SET_EVAL)
+ if ((set->flags & (NFT_SET_EVAL | NFT_SET_ANONYMOUS)) ==
+ (NFT_SET_EVAL | NFT_SET_ANONYMOUS))
type = "meter";
+ else if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+ type = "map";
else
type = "set";
diff --git a/src/statement.c b/src/statement.c
index 039ca943..98e56844 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -630,6 +630,12 @@ static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
expr_print(stmt->set.set, octx);
nft_print(octx, " { ");
expr_print(stmt->set.key, octx);
+ if (stmt->set.stmt) {
+ nft_print(octx, " ");
+ octx->stateless++;
+ stmt_print(stmt->set.stmt, octx);
+ octx->stateless--;
+ }
nft_print(octx, " }");
}
@@ -658,6 +664,12 @@ static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
expr_print(stmt->map.set, octx);
nft_print(octx, " { ");
expr_print(stmt->map.key, octx);
+ if (stmt->map.stmt) {
+ nft_print(octx, " ");
+ octx->stateless++;
+ stmt_print(stmt->map.stmt, octx);
+ octx->stateless--;
+ }
nft_print(octx, " : ");
expr_print(stmt->map.data, octx);
nft_print(octx, " }");