From 57e4a095edc4dab19e14fc8d1bca3febde1ca86c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 31 May 2018 18:08:06 +0200 Subject: src: connlimit support This patch adds support for the new connlimit stateful expression, that provides a mapping with the connlimit iptables extension through meters. eg. nft add rule filter input tcp dport 22 \ meter test { ip saddr ct count over 2 } counter reject This limits the maximum amount incoming of SSH connections per source address up to 2 simultaneous connections. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_tables.h | 21 ++++++++++++++++++++- include/statement.h | 10 ++++++++++ src/evaluate.c | 1 + src/netlink_delinearize.c | 16 ++++++++++++++++ src/netlink_linearize.c | 18 ++++++++++++++++++ src/parser_bison.y | 18 ++++++++++++++++-- src/statement.c | 21 +++++++++++++++++++++ 7 files changed, 102 insertions(+), 3 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 51d54d67..40d43271 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -1018,6 +1018,24 @@ enum nft_limit_attributes { }; #define NFTA_LIMIT_MAX (__NFTA_LIMIT_MAX - 1) +enum nft_connlimit_flags { + NFT_CONNLIMIT_F_INV = (1 << 0), +}; + +/** + * enum nft_connlimit_attributes - nf_tables connlimit expression netlink attributes + * + * @NFTA_CONNLIMIT_COUNT: number of connections (NLA_U32) + * @NFTA_CONNLIMIT_FLAGS: flags (NLA_U32: enum nft_connlimit_flags) + */ +enum nft_connlimit_attributes { + NFTA_CONNLIMIT_UNSPEC, + NFTA_CONNLIMIT_COUNT, + NFTA_CONNLIMIT_FLAGS, + __NFTA_CONNLIMIT_MAX +}; +#define NFTA_CONNLIMIT_MAX (__NFTA_CONNLIMIT_MAX - 1) + /** * enum nft_counter_attributes - nf_tables counter expression netlink attributes * @@ -1328,7 +1346,8 @@ enum nft_ct_helper_attributes { #define NFT_OBJECT_QUOTA 2 #define NFT_OBJECT_CT_HELPER 3 #define NFT_OBJECT_LIMIT 4 -#define __NFT_OBJECT_MAX 5 +#define NFT_OBJECT_CONNLIMIT 5 +#define __NFT_OBJECT_MAX 6 #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) /** diff --git a/include/statement.h b/include/statement.h index de26549b..d4bcaf3a 100644 --- a/include/statement.h +++ b/include/statement.h @@ -25,6 +25,13 @@ struct objref_stmt { const char *objref_type_name(uint32_t type); struct stmt *objref_stmt_alloc(const struct location *loc); +struct connlimit_stmt { + uint32_t count; + uint32_t flags; +}; + +extern struct stmt *connlimit_stmt_alloc(const struct location *loc); + struct counter_stmt { uint64_t packets; uint64_t bytes; @@ -247,6 +254,7 @@ extern struct stmt *xt_stmt_alloc(const struct location *loc); * @STMT_OBJREF: stateful object reference statement * @STMT_EXTHDR: extension header statement * @STMT_FLOW_OFFLOAD: flow offload statement + * @STMT_CONNLIMIT: connection limit statement * @STMT_MAP: map statement */ enum stmt_types { @@ -272,6 +280,7 @@ enum stmt_types { STMT_OBJREF, STMT_EXTHDR, STMT_FLOW_OFFLOAD, + STMT_CONNLIMIT, STMT_MAP, }; @@ -318,6 +327,7 @@ struct stmt { struct expr *expr; struct exthdr_stmt exthdr; struct meter_stmt meter; + struct connlimit_stmt connlimit; struct counter_stmt counter; struct payload_stmt payload; struct meta_stmt meta; diff --git a/src/evaluate.c b/src/evaluate.c index ff75fc45..039e02db 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -2703,6 +2703,7 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) } switch (stmt->ops->type) { + case STMT_CONNLIMIT: case STMT_COUNTER: case STMT_LIMIT: case STMT_QUOTA: diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 2c938e52..7dbf596a 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -751,6 +751,21 @@ static void netlink_parse_ct(struct netlink_parse_ctx *ctx, netlink_parse_ct_stmt(ctx, loc, nle); } +static void netlink_parse_connlimit(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + + stmt = connlimit_stmt_alloc(loc); + stmt->connlimit.count = + nftnl_expr_get_u32(nle, NFTNL_EXPR_CONNLIMIT_COUNT); + stmt->connlimit.flags = + nftnl_expr_get_u32(nle, NFTNL_EXPR_CONNLIMIT_FLAGS); + + ctx->stmt = stmt; +} + static void netlink_parse_counter(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nftnl_expr *nle) @@ -1294,6 +1309,7 @@ static const struct { { .name = "meta", .parse = netlink_parse_meta }, { .name = "rt", .parse = netlink_parse_rt }, { .name = "ct", .parse = netlink_parse_ct }, + { .name = "connlimit", .parse = netlink_parse_connlimit }, { .name = "counter", .parse = netlink_parse_counter }, { .name = "log", .parse = netlink_parse_log }, { .name = "limit", .parse = netlink_parse_limit }, diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 2ab8accf..13c3564f 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -733,6 +733,21 @@ static void netlink_gen_objref_stmt(struct netlink_linearize_ctx *ctx, nftnl_rule_add_expr(ctx->nlr, nle); } +static struct nftnl_expr * +netlink_gen_connlimit_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("connlimit"); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CONNLIMIT_COUNT, + stmt->connlimit.count); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CONNLIMIT_FLAGS, + stmt->connlimit.flags); + + return nle; +} + static struct nftnl_expr * netlink_gen_counter_stmt(struct netlink_linearize_ctx *ctx, const struct stmt *stmt) @@ -789,6 +804,8 @@ netlink_gen_stmt_stateful(struct netlink_linearize_ctx *ctx, const struct stmt *stmt) { switch (stmt->ops->type) { + case STMT_CONNLIMIT: + return netlink_gen_connlimit_stmt(ctx, stmt); case STMT_COUNTER: return netlink_gen_counter_stmt(ctx, stmt); case STMT_LIMIT: @@ -1269,6 +1286,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, return netlink_gen_set_stmt(ctx, stmt); case STMT_FWD: return netlink_gen_fwd_stmt(ctx, stmt); + case STMT_CONNLIMIT: case STMT_COUNTER: case STMT_LIMIT: case STMT_QUOTA: diff --git a/src/parser_bison.y b/src/parser_bison.y index d13eaa66..5797ee76 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -560,8 +560,8 @@ int nft_lex(void *, void *, void *); %type log_stmt log_stmt_alloc %destructor { stmt_free($$); } log_stmt log_stmt_alloc %type level_type log_flags log_flags_tcp log_flag_tcp -%type limit_stmt quota_stmt -%destructor { stmt_free($$); } limit_stmt quota_stmt +%type limit_stmt quota_stmt connlimit_stmt +%destructor { stmt_free($$); } limit_stmt quota_stmt connlimit_stmt %type limit_burst limit_mode time_unit quota_mode %type reject_stmt reject_stmt_alloc %destructor { stmt_free($$); } reject_stmt reject_stmt_alloc @@ -2062,6 +2062,7 @@ stmt_list : stmt stmt : verdict_stmt | match_stmt | meter_stmt + | connlimit_stmt | counter_stmt | payload_stmt | meta_stmt @@ -2129,6 +2130,19 @@ verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_n } ; +connlimit_stmt : CT COUNT NUM + { + $$ = connlimit_stmt_alloc(&@$); + $$->connlimit.count = $3; + } + | CT COUNT OVER NUM + { + $$ = connlimit_stmt_alloc(&@$); + $$->connlimit.count = $4; + $$->connlimit.flags = NFT_CONNLIMIT_F_INV; + } + ; + counter_stmt : counter_stmt_alloc | counter_stmt_alloc counter_args diff --git a/src/statement.c b/src/statement.c index 4a646e06..6f490132 100644 --- a/src/statement.c +++ b/src/statement.c @@ -149,6 +149,27 @@ struct stmt *meter_stmt_alloc(const struct location *loc) return stmt_alloc(loc, &meter_stmt_ops); } +static void connlimit_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "ct count %s%u ", + stmt->connlimit.flags ? "over " : "", stmt->connlimit.count); +} + +static const struct stmt_ops connlimit_stmt_ops = { + .type = STMT_CONNLIMIT, + .name = "connlimit", + .print = connlimit_stmt_print, +}; + +struct stmt *connlimit_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &connlimit_stmt_ops); + stmt->flags |= STMT_F_STATEFUL; + return stmt; +} + static void counter_stmt_print(const struct stmt *stmt, struct output_ctx *octx) { nft_print(octx, "counter"); -- cgit v1.2.3