diff options
-rw-r--r-- | include/statement.h | 10 | ||||
-rw-r--r-- | src/evaluate.c | 1 | ||||
-rw-r--r-- | src/netlink_delinearize.c | 14 | ||||
-rw-r--r-- | src/netlink_linearize.c | 16 | ||||
-rw-r--r-- | src/parser_bison.y | 38 | ||||
-rw-r--r-- | src/scanner.l | 2 | ||||
-rw-r--r-- | src/statement.c | 26 | ||||
-rw-r--r-- | tests/py/any/quota.t | 24 | ||||
-rw-r--r-- | tests/py/any/quota.t.payload | 52 |
9 files changed, 178 insertions, 5 deletions
diff --git a/include/statement.h b/include/statement.h index 1b215517..e278b706 100644 --- a/include/statement.h +++ b/include/statement.h @@ -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; diff --git a/src/evaluate.c b/src/evaluate.c index 2f94ac6e..d669b85b 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -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); diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 12d0b4a2..e9e0a823 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -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, diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index f4db685e..a14d0ff9 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -657,6 +657,19 @@ netlink_gen_limit_stmt(struct netlink_linearize_ctx *ctx, } 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; diff --git a/src/parser_bison.y b/src/parser_bison.y index 8c0f625c..6b58fe77 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -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; } diff --git a/src/scanner.l b/src/scanner.l index e9384fd6..53b79aa5 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -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; } diff --git a/src/statement.c b/src/statement.c index 59b133c2..8ccd4891 100644 --- a/src/statement.c +++ b/src/statement.c @@ -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, "a_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 index 00000000..9a8db114 --- /dev/null +++ b/tests/py/any/quota.t @@ -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 index 00000000..519db2c7 --- /dev/null +++ b/tests/py/any/quota.t.payload @@ -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 ] + |