From 1188a69604c3df2a63daca9e735fdb535e8f6b63 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Sat, 22 Jun 2019 19:12:08 +0200 Subject: src: introduce SYNPROXY matching Add support for "synproxy" statement. For example (for TCP port 8888): table ip x { chain y { type filter hook prerouting priority raw; policy accept; tcp dport 8888 tcp flags syn notrack } chain z { type filter hook input priority filter; policy accept; tcp dport 8888 ct state invalid,untracked synproxy mss 1460 wscale 7 timestamp sack-perm ct state invalid drop } } Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso --- src/evaluate.c | 15 ++++++++ src/json.c | 29 +++++++++++++++ src/netlink_delinearize.c | 17 +++++++++ src/netlink_linearize.c | 17 +++++++++ src/parser_bison.y | 47 ++++++++++++++++++++++++ src/parser_json.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++ src/scanner.l | 5 +++ src/statement.c | 51 +++++++++++++++++++++++++ 8 files changed, 275 insertions(+) (limited to 'src') diff --git a/src/evaluate.c b/src/evaluate.c index 864d3daf..55cd9d00 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -2746,6 +2747,18 @@ static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt) return 0; } +static int stmt_evaluate_synproxy(struct eval_ctx *ctx, struct stmt *stmt) +{ + if (stmt->synproxy.flags != 0 && + !(stmt->synproxy.flags & (NF_SYNPROXY_OPT_MSS | + NF_SYNPROXY_OPT_WSCALE | + NF_SYNPROXY_OPT_TIMESTAMP | + NF_SYNPROXY_OPT_SACK_PERM))) + return stmt_error(ctx, stmt, "This flags are not supported for SYNPROXY"); + + return 0; +} + static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt) { int err; @@ -3090,6 +3103,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) return stmt_evaluate_objref(ctx, stmt); case STMT_MAP: return stmt_evaluate_map(ctx, stmt); + case STMT_SYNPROXY: + return stmt_evaluate_synproxy(ctx, stmt); default: BUG("unknown statement type %s\n", stmt->ops->name); } diff --git a/src/json.c b/src/json.c index 47543768..96ba557a 100644 --- a/src/json.c +++ b/src/json.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1466,6 +1467,34 @@ json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) return json_pack("{s:o}", "tproxy", root); } +json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root = json_object(), *flags = json_array(); + + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_MSS) + json_object_set_new(root, "mss", + json_integer(stmt->synproxy.mss)); + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_WSCALE) + json_object_set_new(root, "wscale", + json_integer(stmt->synproxy.wscale)); + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP) + json_array_append_new(flags, json_string("timestamp")); + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM) + json_array_append_new(flags, json_string("sack-perm")); + + if (json_array_size(flags) > 0) + json_object_set_new(root, "flags", flags); + else + json_decref(flags); + + if (!json_object_size(root)) { + json_decref(root); + root = json_null(); + } + + return json_pack("{s:o}", "synproxy", root); +} + static json_t *table_print_json_full(struct netlink_ctx *ctx, struct table *table) { diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 1dd3ffd1..fc2574b1 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -1010,6 +1010,22 @@ out_err: xfree(stmt); } +static void netlink_parse_synproxy(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + + stmt = synproxy_stmt_alloc(loc); + stmt->synproxy.mss = nftnl_expr_get_u16(nle, NFTNL_EXPR_SYNPROXY_MSS); + stmt->synproxy.wscale = nftnl_expr_get_u8(nle, + NFTNL_EXPR_SYNPROXY_WSCALE); + stmt->synproxy.flags = nftnl_expr_get_u32(nle, + NFTNL_EXPR_SYNPROXY_FLAGS); + + ctx->stmt = stmt; +} + static void netlink_parse_tproxy(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nftnl_expr *nle) @@ -1476,6 +1492,7 @@ static const struct { { .name = "tcpopt", .parse = netlink_parse_exthdr }, { .name = "flow_offload", .parse = netlink_parse_flow_offload }, { .name = "xfrm", .parse = netlink_parse_xfrm }, + { .name = "synproxy", .parse = netlink_parse_synproxy }, }; static int netlink_parse_expr(const struct nftnl_expr *nle, diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 2c6aa64d..498326d0 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -1141,6 +1141,21 @@ static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx, nftnl_rule_add_expr(ctx->nlr, nle); } +static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("synproxy"); + nftnl_expr_set_u16(nle, NFTNL_EXPR_SYNPROXY_MSS, stmt->synproxy.mss); + nftnl_expr_set_u8(nle, NFTNL_EXPR_SYNPROXY_WSCALE, + stmt->synproxy.wscale); + nftnl_expr_set_u32(nle, NFTNL_EXPR_SYNPROXY_FLAGS, + stmt->synproxy.flags); + + nftnl_rule_add_expr(ctx->nlr, nle); +} + static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx, const struct stmt *stmt) { @@ -1382,6 +1397,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, return netlink_gen_nat_stmt(ctx, stmt); case STMT_TPROXY: return netlink_gen_tproxy_stmt(ctx, stmt); + case STMT_SYNPROXY: + return netlink_gen_synproxy_stmt(ctx, stmt); case STMT_DUP: return netlink_gen_dup_stmt(ctx, stmt); case STMT_QUEUE: diff --git a/src/parser_bison.y b/src/parser_bison.y index c7591bc2..53e66952 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -200,6 +201,11 @@ int nft_lex(void *, void *, void *); %token OSF "osf" +%token SYNPROXY "synproxy" +%token MSS "mss" +%token WSCALE "wscale" +%token SACKPERM "sack-perm" + %token HOOK "hook" %token DEVICE "device" %token DEVICES "devices" @@ -611,6 +617,9 @@ int nft_lex(void *, void *, void *); %type nf_nat_flags nf_nat_flag offset_opt %type tproxy_stmt %destructor { stmt_free($$); } tproxy_stmt +%type synproxy_stmt synproxy_stmt_alloc +%destructor { stmt_free($$); } synproxy_stmt synproxy_stmt_alloc + %type queue_stmt queue_stmt_alloc %destructor { stmt_free($$); } queue_stmt queue_stmt_alloc @@ -2289,6 +2298,7 @@ stmt : verdict_stmt | fwd_stmt | set_stmt | map_stmt + | synproxy_stmt ; verdict_stmt : verdict_expr @@ -2719,6 +2729,43 @@ tproxy_stmt : TPROXY TO stmt_expr } ; +synproxy_stmt : synproxy_stmt_alloc + | synproxy_stmt_alloc synproxy_args + ; + +synproxy_stmt_alloc : SYNPROXY + { + $$ = synproxy_stmt_alloc(&@$); + } + ; + +synproxy_args : synproxy_arg + { + $$ = $0; + } + | synproxy_args synproxy_arg + ; + +synproxy_arg : MSS NUM + { + $0->synproxy.mss = $2; + $0->synproxy.flags |= NF_SYNPROXY_OPT_MSS; + } + | WSCALE NUM + { + $0->synproxy.wscale = $2; + $0->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; + } + | TIMESTAMP + { + $0->synproxy.flags |= NF_SYNPROXY_OPT_TIMESTAMP; + } + | SACKPERM + { + $0->synproxy.flags |= NF_SYNPROXY_OPT_SACK_PERM; + } + ; + primary_stmt_expr : symbol_expr { $$ = $1; } | integer_expr { $$ = $1; } | boolean_expr { $$ = $1; } diff --git a/src/parser_json.c b/src/parser_json.c index 79f5865c..42893c2f 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -2180,6 +2181,98 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx, return stmt; } +static int json_parse_synproxy_flag(struct json_ctx *ctx, + json_t *root, int *flags) +{ + const struct { + const char *flag; + int val; + } flag_tbl[] = { + { "timestamp", NF_SYNPROXY_OPT_TIMESTAMP }, + { "sack-perm", NF_SYNPROXY_OPT_SACK_PERM }, + }; + const char *flag; + unsigned int i; + + assert(flags); + + if (!json_is_string(root)) { + json_error(ctx, "Invalid log flag type %s, expected string.", + json_typename(root)); + return 1; + } + flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { + if (!strcmp(flag, flag_tbl[i].flag)) { + *flags |= flag_tbl[i].val; + return 0; + } + } + json_error(ctx, "Unknown log flag '%s'.", flag); + return 1; +} + +static int json_parse_synproxy_flags(struct json_ctx *ctx, json_t *root) +{ + int flags = 0; + json_t *value; + size_t index; + + if (json_is_string(root)) { + json_parse_synproxy_flag(ctx, root, &flags); + return flags; + } else if (!json_is_array(root)) { + json_error(ctx, "Invalid log flags type %s.", + json_typename(root)); + return -1; + } + json_array_foreach(root, index, value) { + if (json_parse_synproxy_flag(ctx, value, &flags)) + json_error(ctx, "Parsing log flag at index %zu failed.", + index); + } + return flags; +} + +static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt; + json_t *jflags; + int tmp, flags; + + stmt = synproxy_stmt_alloc(int_loc); + + if (!json_unpack(value, "{s:i}", "mss", &tmp)) { + if (tmp < 0) { + json_error(ctx, "Invalid synproxy mss value '%d'", tmp); + stmt_free(stmt); + return NULL; + } + stmt->synproxy.mss = tmp; + stmt->synproxy.flags |= NF_SYNPROXY_OPT_MSS; + } + if (!json_unpack(value, "{s:i}", "wscale", &tmp)) { + if (tmp < 0) { + json_error(ctx, "Invalid synproxy wscale value '%d'", tmp); + stmt_free(stmt); + return NULL; + } + stmt->synproxy.wscale = tmp; + stmt->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; + } + if (!json_unpack(value, "{s:o}", "flags", &jflags)) { + flags = json_parse_synproxy_flags(ctx, jflags); + + if (flags < 0) { + stmt_free(stmt); + return NULL; + } + stmt->synproxy.flags |= flags; + } + return stmt; +} + static struct stmt *json_parse_cthelper_stmt(struct json_ctx *ctx, const char *key, json_t *value) { @@ -2375,6 +2468,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root) { "queue", json_parse_queue_stmt }, { "ct count", json_parse_connlimit_stmt }, { "tproxy", json_parse_tproxy_stmt }, + { "synproxy", json_parse_synproxy_stmt }, }; const char *type; unsigned int i; diff --git a/src/scanner.l b/src/scanner.l index 51bf64f4..4ed5f924 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -553,6 +553,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "osf" { return OSF; } +"synproxy" { return SYNPROXY; } +"mss" { return MSS; } +"wscale" { return WSCALE; } +"sack-perm" { return SACKPERM; } + "notrack" { return NOTRACK; } "options" { return OPTIONS; } diff --git a/src/statement.c b/src/statement.c index a82c1b39..a9e72de3 100644 --- a/src/statement.c +++ b/src/statement.c @@ -29,6 +29,7 @@ #include #include #include +#include struct stmt *stmt_alloc(const struct location *loc, const struct stmt_ops *ops) @@ -883,3 +884,53 @@ struct stmt *xt_stmt_alloc(const struct location *loc) { return stmt_alloc(loc, &xt_stmt_ops); } + +static const char *synproxy_sack_to_str(const uint32_t flags) +{ + if (flags & NF_SYNPROXY_OPT_SACK_PERM) + return " sack-perm"; + + return ""; +} + +static const char *synproxy_timestamp_to_str(const uint32_t flags) +{ + if (flags & NF_SYNPROXY_OPT_TIMESTAMP) + return " timestamp"; + + return ""; +} + +static void synproxy_stmt_print(const struct stmt *stmt, + struct output_ctx *octx) +{ + uint32_t flags = stmt->synproxy.flags; + const char *ts_str = synproxy_timestamp_to_str(flags); + const char *sack_str = synproxy_sack_to_str(flags); + + if (flags & (NF_SYNPROXY_OPT_MSS | NF_SYNPROXY_OPT_WSCALE)) + nft_print(octx, "synproxy mss %u wscale %u%s%s", + stmt->synproxy.mss, stmt->synproxy.wscale, + ts_str, sack_str); + else if (flags & NF_SYNPROXY_OPT_MSS) + nft_print(octx, "synproxy mss %u%s%s", stmt->synproxy.mss, + ts_str, sack_str); + else if (flags & NF_SYNPROXY_OPT_WSCALE) + nft_print(octx, "synproxy wscale %u%s%s", stmt->synproxy.wscale, + ts_str, sack_str); + else + nft_print(octx, "synproxy%s%s", ts_str, sack_str); + +} + +static const struct stmt_ops synproxy_stmt_ops = { + .type = STMT_SYNPROXY, + .name = "synproxy", + .print = synproxy_stmt_print, + .json = synproxy_stmt_json, +}; + +struct stmt *synproxy_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &synproxy_stmt_ops); +} -- cgit v1.2.3