From 1ed9a3726c01fda218f37b7f4555c8b7106521ef Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 26 Aug 2016 11:19:18 +0200 Subject: src: add quota statement 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 --- src/evaluate.c | 1 + src/netlink_delinearize.c | 14 ++++++++++++++ src/netlink_linearize.c | 16 ++++++++++++++++ src/parser_bison.y | 38 +++++++++++++++++++++++++++++++++----- src/scanner.l | 2 ++ src/statement.c | 26 ++++++++++++++++++++++++++ 6 files changed, 92 insertions(+), 5 deletions(-) (limited to 'src') 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 @@ -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; 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 family_spec family_spec_explicit chain_policy prio_spec -%type dev_spec -%destructor { xfree($$); } dev_spec +%type dev_spec quota_unit +%destructor { xfree($$); } dev_spec quota_unit %type 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 log_stmt log_stmt_alloc %destructor { stmt_free($$); } log_stmt log_stmt_alloc %type level_type -%type limit_stmt -%destructor { stmt_free($$); } limit_stmt -%type limit_burst limit_mode time_unit +%type limit_stmt quota_stmt +%destructor { stmt_free($$); } limit_stmt quota_stmt +%type limit_burst limit_mode time_unit quota_mode %type reject_stmt reject_stmt_alloc %destructor { stmt_free($$); } reject_stmt reject_stmt_alloc %type 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"); -- cgit v1.2.3