]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add quota statement
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 26 Aug 2016 09:19:18 +0000 (11:19 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 29 Aug 2016 18:30:28 +0000 (20:30 +0200)
This new statement is stateful, so it can be used from flow tables, eg.

 # nft add rule filter input \
        flow table http { ip saddr timeout 60s quota over 50 mbytes } drop

This basically sets a quota per source IP address of 50 mbytes after
which packets are dropped. Note that the timeout releases the entry if
no traffic is seen from this IP after 60 seconds.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/statement.h
src/evaluate.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser_bison.y
src/scanner.l
src/statement.c
tests/py/any/quota.t [new file with mode: 0644]
tests/py/any/quota.t.payload [new file with mode: 0644]

index 1b2155175a181cf8696264bf4361e39629ce5a8a..e278b70637c46489da02b952467c51f7ecc0fb3d 100644 (file)
@@ -105,6 +105,13 @@ struct queue_stmt {
 
 extern struct stmt *queue_stmt_alloc(const struct location *loc);
 
+struct quota_stmt {
+       uint64_t                bytes;
+       uint32_t                flags;
+};
+
+struct stmt *quota_stmt_alloc(const struct location *loc);
+
 #include <ct.h>
 struct ct_stmt {
        enum nft_ct_keys                key;
@@ -200,6 +207,7 @@ extern struct stmt *xt_stmt_alloc(const struct location *loc);
  * @STMT_DUP:          dup statement
  * @STMT_FWD:          forward statement
  * @STMT_XT:           XT statement
+ * @STMT_QUOTA:                quota statement
  */
 enum stmt_types {
        STMT_INVALID,
@@ -221,6 +229,7 @@ enum stmt_types {
        STMT_DUP,
        STMT_FWD,
        STMT_XT,
+       STMT_QUOTA,
 };
 
 /**
@@ -272,6 +281,7 @@ struct stmt {
                struct masq_stmt        masq;
                struct redir_stmt       redir;
                struct queue_stmt       queue;
+               struct quota_stmt       quota;
                struct ct_stmt          ct;
                struct set_stmt         set;
                struct dup_stmt         dup;
index 2f94ac6e39a8dd26c8ac58ead3b50a34dc830dba..d669b85beb679e3bbc0ac3d4196019185c2b3d6f 100644 (file)
@@ -2366,6 +2366,7 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
        switch (stmt->ops->type) {
        case STMT_COUNTER:
        case STMT_LIMIT:
+       case STMT_QUOTA:
                return 0;
        case STMT_EXPRESSION:
                return stmt_evaluate_expr(ctx, stmt);
index 12d0b4a277795a373eef416f6683c65ab3ad61fa..e9e0a823650c9aa88bc8e421c8a6b58ff67fd47c 100644 (file)
@@ -620,6 +620,19 @@ static void netlink_parse_limit(struct netlink_parse_ctx *ctx,
        ctx->stmt = stmt;
 }
 
+static void netlink_parse_quota(struct netlink_parse_ctx *ctx,
+                               const struct location *loc,
+                               const struct nftnl_expr *nle)
+{
+       struct stmt *stmt;
+
+       stmt = quota_stmt_alloc(loc);
+       stmt->quota.bytes = nftnl_expr_get_u64(nle, NFTNL_EXPR_QUOTA_BYTES);
+       stmt->quota.flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_QUOTA_FLAGS);
+
+       ctx->stmt = stmt;
+}
+
 static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
                                 const struct location *loc,
                                 const struct nftnl_expr *expr)
@@ -989,6 +1002,7 @@ static const struct {
        { .name = "fwd",        .parse = netlink_parse_fwd },
        { .name = "target",     .parse = netlink_parse_target },
        { .name = "match",      .parse = netlink_parse_match },
+       { .name = "quota",      .parse = netlink_parse_quota },
 };
 
 static int netlink_parse_expr(const struct nftnl_expr *nle,
index f4db685e17ca99b8448364330e407a649393ad61..a14d0ff9af24c15443df3e8f3addb9bc96db94da 100644 (file)
@@ -656,6 +656,19 @@ netlink_gen_limit_stmt(struct netlink_linearize_ctx *ctx,
        return nle;
 }
 
+static struct nftnl_expr *
+netlink_gen_quota_stmt(struct netlink_linearize_ctx *ctx,
+                      const struct stmt *stmt)
+{
+       struct nftnl_expr *nle;
+
+       nle = alloc_nft_expr("quota");
+       nftnl_expr_set_u64(nle, NFTNL_EXPR_QUOTA_BYTES, stmt->quota.bytes);
+       nftnl_expr_set_u32(nle, NFTNL_EXPR_QUOTA_FLAGS, stmt->quota.flags);
+
+       return nle;
+}
+
 static struct nftnl_expr *
 netlink_gen_stmt_stateful(struct netlink_linearize_ctx *ctx,
                          const struct stmt *stmt)
@@ -665,6 +678,8 @@ netlink_gen_stmt_stateful(struct netlink_linearize_ctx *ctx,
                return netlink_gen_counter_stmt(ctx, stmt);
        case STMT_LIMIT:
                return netlink_gen_limit_stmt(ctx, stmt);
+       case STMT_QUOTA:
+               return netlink_gen_quota_stmt(ctx, stmt);
        default:
                BUG("unknown stateful statement type %s\n", stmt->ops->name);
        }
@@ -1105,6 +1120,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
                return netlink_gen_fwd_stmt(ctx, stmt);
        case STMT_COUNTER:
        case STMT_LIMIT:
+       case STMT_QUOTA:
                nle = netlink_gen_stmt_stateful(ctx, stmt);
                nftnl_rule_add_expr(ctx->nlr, nle);
                break;
index 8c0f625ca5b172f4df96d524549cc8d1a6d7562b..6b58fe779373963ca94ba7780d270829f3b5f985 100644 (file)
@@ -376,6 +376,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token OVER                    "over"
 %token UNTIL                   "until"
 
+%token QUOTA                   "quota"
+
 %token NANOSECOND              "nanosecond"
 %token MICROSECOND             "microsecond"
 %token MILLISECOND             "millisecond"
@@ -431,8 +433,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %destructor { handle_free(&$$); } set_spec set_identifier
 %type <val>                    family_spec family_spec_explicit chain_policy prio_spec
 
-%type <string>                 dev_spec
-%destructor { xfree($$); }     dev_spec
+%type <string>                 dev_spec quota_unit
+%destructor { xfree($$); }     dev_spec quota_unit
 
 %type <table>                  table_block_alloc table_block
 %destructor { close_scope(state); table_free($$); }    table_block_alloc
@@ -466,9 +468,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %type <stmt>                   log_stmt log_stmt_alloc
 %destructor { stmt_free($$); } log_stmt log_stmt_alloc
 %type <val>                    level_type
-%type <stmt>                   limit_stmt
-%destructor { stmt_free($$); } limit_stmt
-%type <val>                    limit_burst limit_mode time_unit
+%type <stmt>                   limit_stmt quota_stmt
+%destructor { stmt_free($$); } limit_stmt quota_stmt
+%type <val>                    limit_burst limit_mode time_unit quota_mode
 %type <stmt>                   reject_stmt reject_stmt_alloc
 %destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
 %type <stmt>                   nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
@@ -1373,6 +1375,7 @@ stmt                      :       verdict_stmt
                        |       meta_stmt
                        |       log_stmt
                        |       limit_stmt
+                       |       quota_stmt
                        |       reject_stmt
                        |       nat_stmt
                        |       queue_stmt
@@ -1542,6 +1545,31 @@ limit_stmt               :       LIMIT   RATE    limit_mode      NUM     SLASH   time_unit       limit_burst
                        }
                        ;
 
+quota_mode             :       OVER            { $$ = NFT_QUOTA_F_INV; }
+                       |       UNTIL           { $$ = 0; }
+                       |       /* empty */     { $$ = 0; }
+                       ;
+
+quota_unit             :       BYTES           { $$ = xstrdup("bytes"); }
+                       |       STRING          { $$ = $1; }
+                       ;
+
+quota_stmt             :       QUOTA   quota_mode NUM quota_unit
+                       {
+                               struct error_record *erec;
+                               uint64_t rate;
+
+                               erec = data_unit_parse(&@$, $4, &rate);
+                               if (erec != NULL) {
+                                       erec_queue(erec, state->msgs);
+                                       YYERROR;
+                               }
+                               $$ = quota_stmt_alloc(&@$);
+                               $$->quota.bytes = $3 * rate;
+                               $$->quota.flags = $2;
+                       }
+                       ;
+
 limit_mode             :       OVER                            { $$ = NFT_LIMIT_F_INV; }
                        |       UNTIL                           { $$ = 0; }
                        |       /* empty */                     { $$ = 0; }
index e9384fd66b3c9fb1bd74341ed0f4150b928b799b..53b79aa5ddb2d97b0665da4a7f43f3c83f812a7c 100644 (file)
@@ -319,6 +319,8 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
 "until"                        { return UNTIL; }
 "over"                 { return OVER; }
 
+"quota"                        { return QUOTA; }
+
 "nanosecond"           { return NANOSECOND; }
 "microsecond"          { return MICROSECOND; }
 "millisecond"          { return MILLISECOND; }
index 59b133c22c07e31805a5603d933a40e0c2b07105..8ccd48911d2b364c73c13d66846ef271190d4551 100644 (file)
@@ -325,6 +325,32 @@ struct stmt *queue_stmt_alloc(const struct location *loc)
        return stmt_alloc(loc, &queue_stmt_ops);
 }
 
+static void quota_stmt_print(const struct stmt *stmt)
+{
+       bool inv = stmt->quota.flags & NFT_QUOTA_F_INV;
+       const char *data_unit;
+       uint64_t bytes;
+
+       data_unit = get_rate(stmt->quota.bytes, &bytes);
+       printf("quota %s%"PRIu64" %s",
+              inv ? "over " : "", bytes, data_unit);
+}
+
+static const struct stmt_ops quota_stmt_ops = {
+       .type           = STMT_QUOTA,
+       .name           = "quota",
+       .print          = quota_stmt_print,
+};
+
+struct stmt *quota_stmt_alloc(const struct location *loc)
+{
+       struct stmt *stmt;
+
+       stmt = stmt_alloc(loc, &quota_stmt_ops);
+       stmt->flags |= STMT_F_STATEFUL;
+       return stmt;
+}
+
 static void reject_stmt_print(const struct stmt *stmt)
 {
        printf("reject");
diff --git a/tests/py/any/quota.t b/tests/py/any/quota.t
new file mode 100644 (file)
index 0000000..9a8db11
--- /dev/null
@@ -0,0 +1,24 @@
+:output;type filter hook output priority 0
+:ingress;type filter hook ingress device lo priority 0
+
+*ip;test-ip4;output
+*ip6;test-ip6;output
+*inet;test-inet;output
+*arp;test-arp;output
+*bridge;test-bridge;output
+*netdev;test-netdev;ingress
+
+quota 1025 bytes;ok
+quota 1 kbytes;ok
+quota 2 kbytes;ok
+quota 1025 kbytes;ok
+quota 1023 mbytes;ok
+quota 10230 mbytes;ok
+quota 1023000 mbytes;ok
+
+quota over 1 kbytes;ok
+quota over 2 kbytes;ok
+quota over 1025 kbytes;ok
+quota over 1023 mbytes;ok
+quota over 10230 mbytes;ok
+quota over 1023000 mbytes;ok
diff --git a/tests/py/any/quota.t.payload b/tests/py/any/quota.t.payload
new file mode 100644 (file)
index 0000000..519db2c
--- /dev/null
@@ -0,0 +1,52 @@
+# quota 1025 bytes
+ip test-ip4 output 
+  [ quota bytes 1025 flags 0 ]
+
+# quota 1 kbytes
+ip test-ip4 output 
+  [ quota bytes 1024 flags 0 ]
+
+# quota 2 kbytes
+ip test-ip4 output 
+  [ quota bytes 2048 flags 0 ]
+
+# quota 1025 kbytes
+ip test-ip4 output 
+  [ quota bytes 1049600 flags 0 ]
+
+# quota 1023 mbytes
+ip test-ip4 output 
+  [ quota bytes 1072693248 flags 0 ]
+
+# quota 10230 mbytes
+ip test-ip4 output 
+  [ quota bytes 10726932480 flags 0 ]
+
+# quota 1023000 mbytes
+ip test-ip4 output 
+  [ quota bytes 1072693248000 flags 0 ]
+
+# quota over 1 kbytes
+ip test-ip4 output 
+  [ quota bytes 1024 flags 1 ]
+
+# quota over 2 kbytes
+ip test-ip4 output 
+  [ quota bytes 2048 flags 1 ]
+
+# quota over 1025 kbytes
+ip test-ip4 output 
+  [ quota bytes 1049600 flags 1 ]
+
+# quota over 1023 mbytes
+ip test-ip4 output 
+  [ quota bytes 1072693248 flags 1 ]
+
+# quota over 10230 mbytes
+ip test-ip4 output 
+  [ quota bytes 10726932480 flags 1 ]
+
+# quota over 1023000 mbytes
+ip test-ip4 output 
+  [ quota bytes 1072693248000 flags 1 ]
+