From a7469ab47400bf4add8269a2908965e82ceefc48 Mon Sep 17 00:00:00 2001 From: Arturo Borrero Date: Mon, 3 Nov 2014 21:20:11 +0100 Subject: src: add redirect support This patch adds redirect support for nft. The syntax is: % nft add rule nat prerouting redirect [port] [nat_flags] Signed-off-by: Arturo Borrero Gonzalez Signed-off-by: Pablo Neira Ayuso --- src/evaluate.c | 40 ++++++++++++++++++++++++++++++++++++ src/netlink_delinearize.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ src/netlink_linearize.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/parser.y | 23 +++++++++++++++++++-- src/scanner.l | 1 + src/statement.c | 29 ++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/evaluate.c b/src/evaluate.c index b722567c..3eeb614c 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -1546,6 +1546,44 @@ out: return 0; } +static int stmt_evaluate_redir(struct eval_ctx *ctx, struct stmt *stmt) +{ + int err; + struct proto_ctx *pctx = &ctx->pctx; + + if (!pctx) + goto out; + + switch (pctx->family) { + case AF_INET: + expr_set_context(&ctx->ectx, &ipaddr_type, + 4 * BITS_PER_BYTE); + break; + case AF_INET6: + expr_set_context(&ctx->ectx, &ip6addr_type, + 16 * BITS_PER_BYTE); + break; + default: + return stmt_error(ctx, stmt, "ip and ip6 support only"); + } + + if (stmt->redir.proto != NULL) { + if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL) + return stmt_binary_error(ctx, stmt->redir.proto, stmt, + "missing transport protocol match"); + + expr_set_context(&ctx->ectx, &inet_service_type, + 2 * BITS_PER_BYTE); + err = expr_evaluate(ctx, &stmt->redir.proto); + if (err < 0) + return err; + } + +out: + stmt->flags |= STMT_F_TERMINAL; + return 0; +} + static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt) { expr_set_context(&ctx->ectx, stmt->ct.tmpl->dtype, @@ -1608,6 +1646,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) return stmt_evaluate_nat(ctx, stmt); case STMT_MASQ: return stmt_evaluate_masq(ctx, stmt); + case STMT_REDIR: + return stmt_evaluate_redir(ctx, stmt); case STMT_QUEUE: return stmt_evaluate_queue(ctx, stmt); case STMT_CT: diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 8f90cc03..1be409b1 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -583,6 +583,52 @@ static void netlink_parse_masq(struct netlink_parse_ctx *ctx, list_add_tail(&stmt->list, &ctx->rule->stmts); } +static void netlink_parse_redir(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nft_rule_expr *nle) +{ + struct stmt *stmt; + struct expr *proto; + enum nft_registers reg1, reg2; + uint32_t flags; + + stmt = redir_stmt_alloc(loc); + + if (nft_rule_expr_is_set(nle, NFT_EXPR_REDIR_FLAGS)) { + flags = nft_rule_expr_get_u32(nle, NFT_EXPR_REDIR_FLAGS); + stmt->redir.flags = flags; + } + + reg1 = nft_rule_expr_get_u32(nle, NFT_EXPR_REDIR_REG_PROTO_MIN); + if (reg1) { + proto = netlink_get_register(ctx, loc, reg1); + if (proto == NULL) + return netlink_error(ctx, loc, + "redirect statement has no proto " + "expression"); + + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + stmt->redir.proto = proto; + } + + reg2 = nft_rule_expr_get_u32(nle, NFT_EXPR_REDIR_REG_PROTO_MAX); + if (reg2 && reg2 != reg1) { + proto = netlink_get_register(ctx, loc, reg2); + if (proto == NULL) + return netlink_error(ctx, loc, + "redirect statement has no proto " + "expression"); + + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + if (stmt->redir.proto != NULL) + proto = range_expr_alloc(loc, stmt->redir.proto, + proto); + stmt->redir.proto = proto; + } + + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + static void netlink_parse_queue(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nft_rule_expr *nle) @@ -630,6 +676,7 @@ static const struct { { .name = "reject", .parse = netlink_parse_reject }, { .name = "nat", .parse = netlink_parse_nat }, { .name = "masq", .parse = netlink_parse_masq }, + { .name = "redir", .parse = netlink_parse_redir }, { .name = "queue", .parse = netlink_parse_queue }, }; @@ -1014,6 +1061,11 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r if (stmt->nat.proto != NULL) expr_postprocess(&rctx, stmt, &stmt->nat.proto); break; + case STMT_REDIR: + if (stmt->redir.proto != NULL) + expr_postprocess(&rctx, stmt, + &stmt->redir.proto); + break; case STMT_REJECT: stmt_reject_postprocess(rctx, stmt); break; diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 62155cc3..de338cb7 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -701,6 +701,53 @@ static void netlink_gen_masq_stmt(struct netlink_linearize_ctx *ctx, nft_rule_add_expr(ctx->nlr, nle); } +static void netlink_gen_redir_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nft_rule_expr *nle; + enum nft_registers pmin_reg, pmax_reg; + int registers = 0; + + nle = alloc_nft_expr("redir"); + + if (stmt->redir.flags != 0) + nft_rule_expr_set_u32(nle, NFT_EXPR_REDIR_FLAGS, + stmt->redir.flags); + + if (stmt->redir.proto) { + pmin_reg = get_register(ctx); + registers++; + + if (stmt->redir.proto->ops->type == EXPR_RANGE) { + pmax_reg = get_register(ctx); + registers++; + + netlink_gen_expr(ctx, stmt->redir.proto->left, + pmin_reg); + netlink_gen_expr(ctx, stmt->redir.proto->right, + pmax_reg); + nft_rule_expr_set_u32(nle, + NFT_EXPR_REDIR_REG_PROTO_MIN, + pmin_reg); + nft_rule_expr_set_u32(nle, + NFT_EXPR_REDIR_REG_PROTO_MAX, + pmax_reg); + } else { + netlink_gen_expr(ctx, stmt->redir.proto, pmin_reg); + nft_rule_expr_set_u32(nle, + NFT_EXPR_REDIR_REG_PROTO_MIN, + pmin_reg); + } + } + + while (registers > 0) { + release_register(ctx); + registers--; + } + + nft_rule_add_expr(ctx->nlr, nle); +} + static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx, const struct stmt *stmt) { @@ -767,6 +814,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, return netlink_gen_nat_stmt(ctx, stmt); case STMT_MASQ: return netlink_gen_masq_stmt(ctx, stmt); + case STMT_REDIR: + return netlink_gen_redir_stmt(ctx, stmt); case STMT_QUEUE: return netlink_gen_queue_stmt(ctx, stmt); case STMT_CT: diff --git a/src/parser.y b/src/parser.y index 9e9a8393..6209e9eb 100644 --- a/src/parser.y +++ b/src/parser.y @@ -375,6 +375,7 @@ static void location_update(struct location *loc, struct location *rhs, int n) %token SNAT "snat" %token DNAT "dnat" %token MASQUERADE "masquerade" +%token REDIRECT "redirect" %token RANDOM "random" %token RANDOM_FULLY "random-fully" %token PERSISTENT "persistent" @@ -440,8 +441,8 @@ static void location_update(struct location *loc, struct location *rhs, int n) %type time_unit %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 -%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc +%type nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc +%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc %type nf_nat_flags nf_nat_flag %type queue_stmt queue_stmt_alloc %destructor { stmt_free($$); } queue_stmt queue_stmt_alloc @@ -1186,6 +1187,7 @@ stmt : verdict_stmt | queue_stmt | ct_stmt | masq_stmt + | redir_stmt ; verdict_stmt : verdict_expr @@ -1420,6 +1422,23 @@ masq_stmt : masq_stmt_alloc masq_stmt_alloc : MASQUERADE { $$ = masq_stmt_alloc(&@$); } ; +redir_stmt : redir_stmt_alloc redir_stmt_arg + | redir_stmt_alloc + ; + +redir_stmt_alloc : REDIRECT { $$ = redir_stmt_alloc(&@$); } + ; + +redir_stmt_arg : COLON expr + { + $0->redir.proto = $2; + } + | nf_nat_flags + { + $0->redir.flags = $1; + } + ; + nf_nat_flags : nf_nat_flag | nf_nat_flags COMMA nf_nat_flag { diff --git a/src/scanner.l b/src/scanner.l index 32e59d98..e36c3b13 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -317,6 +317,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "snat" { return SNAT; } "dnat" { return DNAT; } "masquerade" { return MASQUERADE; } +"redirect" { return REDIRECT; } "random" { return RANDOM; } "random-fully" { return RANDOM_FULLY; } "persistent" { return PERSISTENT; } diff --git a/src/statement.c b/src/statement.c index 0ae616a8..2587d275 100644 --- a/src/statement.c +++ b/src/statement.c @@ -348,3 +348,32 @@ struct stmt *masq_stmt_alloc(const struct location *loc) { return stmt_alloc(loc, &masq_stmt_ops); } + +static void redir_stmt_print(const struct stmt *stmt) +{ + printf("redirect"); + + if (stmt->redir.proto) { + printf(" :"); + expr_print(stmt->redir.proto); + } + + print_nf_nat_flags(stmt->redir.flags); +} + +static void redir_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->redir.proto); +} + +static const struct stmt_ops redir_stmt_ops = { + .type = STMT_REDIR, + .name = "redir", + .print = redir_stmt_print, + .destroy = redir_stmt_destroy, +}; + +struct stmt *redir_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &redir_stmt_ops); +} -- cgit v1.2.3