]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: integrate stateful expressions into sets and maps
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 24 Aug 2018 07:52:22 +0000 (09:52 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 24 Aug 2018 07:52:22 +0000 (09:52 +0200)
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>
include/statement.h
src/evaluate.c
src/expression.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser_bison.y
src/rule.c
src/statement.c

index 6c583a918eb9371c06600aa072a1bdd794d422af..48ba6673553b1d39d7c51fc33af0f0a04ffab68f 100644 (file)
@@ -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;
 };
 
index 9bc67d8f71f1fd375f05eea08e2dae22c0be5c2c..647e16069ba4b1e150250329a723ae64869c5117 100644 (file)
@@ -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;
 }
index bea0f4c8d9bc1f9f8c777457632d1b38a2cd01bf..0bd5112287e7db9422d5047362f3a9b99f6fb029 100644 (file)
@@ -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)
index 898c737f9b288462064226fe51bf913b97a45b69..6c5188cd9b5f282cb714464a2f45ff6774301e1d 100644 (file)
@@ -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;
index 821fcd0a6377a7d222c6edb0c7b7de55d9c86878..0bd946a1cf80b424ca5c5c7c9e4c7779ae27df4e 100644 (file)
@@ -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);
 }
 
index 199ef13d8c1d03faf3af06879795f4fe796038e3..cc114717f579bd0345ce74809ee56aaa4024705f 100644 (file)
@@ -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 '}'
index aef43638f863cba764217e06dd0a8874ea272b9a..470b112ecacf97e47c7dc234748009b1ce7afa4d 100644 (file)
@@ -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";
 
index 039ca943e92c41335fb7a0678265e941f3ebf00f..98e56844848624150ebf48753434f7ef328b2eb9 100644 (file)
@@ -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, " }");