From patchwork Wed Jun 20 10:41:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TcOhdMOpIEVja2w=?= X-Patchwork-Id: 932096 X-Patchwork-Delegate: pablo@netfilter.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="uuZxSZyp"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 419hGF18lJz9s7X for ; Wed, 20 Jun 2018 20:41:41 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752871AbeFTKlj (ORCPT ); Wed, 20 Jun 2018 06:41:39 -0400 Received: from mail-wr0-f196.google.com ([209.85.128.196]:43181 "EHLO mail-wr0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753266AbeFTKlh (ORCPT ); Wed, 20 Jun 2018 06:41:37 -0400 Received: by mail-wr0-f196.google.com with SMTP id d2-v6so2750685wrm.10 for ; Wed, 20 Jun 2018 03:41:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=zIHoz086l1r59cTX8oWM5LzWfLedn2K5YXO+0KNND70=; b=uuZxSZypYQ7B0xWp6k7ElQXR48KQfumIZZ3IZVFi1wco7fH4WTS2LZGFWYh2pZ4GxO U1NdAMWNFwATT7cQgNKyDaO+xrpdyXup/2JQLpih1l/wXlVk4wQXqmZdfSMML8yNDTHW BMnFgNLRLCRm+e3Mmb59lVFXY9sklxbry5Tpq3/vMx44iP5p8KCOgV62V6xrR3TV4D5U 6J0WZpL1ylmhueYAAlbcBUpjHiTd+jsuCSF8Lai5Jgs7//Bi0ul4GqALnyaNmV5KAWqp VaVPM0ls6puFh9EZFnBFQey+UUUaVTtiEf9IjO9rkLTsGE7LWcZ+itH6x9AjrK1llcAg Icxw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=zIHoz086l1r59cTX8oWM5LzWfLedn2K5YXO+0KNND70=; b=Ts83SZrotcM0uHF5TWxAnA/HhosNxOJKGKn3afiuXVpMKKaLl5356hGkQBFiha/Jgu SEiU2aGruBmjMCqcuD/XjcmTllkxz5pUX2OhsKWkzA/QcAoC34pR2paSsr/J3qUjMF8B 42XNMQYRKR5X6qMLz4o4sxdH4iZerU6jfLqTixnmKVkZeD2Qtly8roDD1N/PeYEas4zb SuVB+4evkfw5OfaHLM/ZSUN6QP9MfBUGWPLnyTF2VK866GPbw7YaazpZUCigRdTHDqdd bl0L1v7nim4xMAH1xORF697yYi1RUIzq8FIKcp4wtL/BghnZ5MbdBO6oJqW2gTtz3/QZ WSGg== X-Gm-Message-State: APt69E03k0oxKjKnWFGS6+vgnI5QsbPB9wuev8aTAuTFgFCTf7JrvkBV cCDYY+8ajVXCipNI2fGTkMkGnBrl X-Google-Smtp-Source: ADUXVKJSyvQzVgNw/OblbfmDiqfRDxDzfcWa4h3JgToKLypkSPgrbv7u3jC3Qd8Cr6LEzCnc5biVLA== X-Received: by 2002:adf:9302:: with SMTP id 2-v6mr17054543wro.175.1529491295737; Wed, 20 Jun 2018 03:41:35 -0700 (PDT) Received: from ecklm-lapos.sch.bme.hu (ecklm-pi.sch.bme.hu. [152.66.210.28]) by smtp.gmail.com with ESMTPSA id y18-v6sm2198918wrl.53.2018.06.20.03.41.35 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 20 Jun 2018 03:41:35 -0700 (PDT) From: =?utf-8?b?TcOhdMOpIEVja2w=?= To: netfilter-devel@vger.kernel.org Subject: [PATCH nft] Add tproxy support Date: Wed, 20 Jun 2018 12:41:30 +0200 Message-Id: <20180620104130.2217-3-ecklm94@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180620104048.mcqwv5xeugiedeyj@sch.bme.hu> References: <20180620104048.mcqwv5xeugiedeyj@sch.bme.hu> MIME-Version: 1.0 Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org This patch is built on the commit not applied yet with the title: evaluate: Detect address family in inet context -- 8< -- This patch adds support for transparent proxy functionality which is supported in ip, ip6 and inet tables. The syntax is the following: tproxy [to [][:]] Without arguments it looks for a socket listening on the ingress interface and a port with the same destination port as the packet destination port. With the arguments specified, it looks for a socket listening on the specified address or port. In an inet table, a packet matches for both families until address is specified. Example ruleset: table inet x { chain y { type filter hook prerouting priority -150; policy accept; socket transparent 1 mark set 0x00000001 accept tproxy mark set 0x00000001 counter packets 611 bytes 46181 meta l4proto tcp tproxy to :50080 mark set 0x00000001 counter packets 202 bytes 13600 } } Signed-off-by: Máté Eckl --- include/linux/netfilter/nf_tables.h | 16 +++++++++ include/statement.h | 10 ++++++ src/evaluate.c | 44 ++++++++++++++++++++++++ src/netlink_delinearize.c | 52 +++++++++++++++++++++++++++++ src/netlink_linearize.c | 48 ++++++++++++++++++++++++++ src/parser_bison.y | 27 +++++++++++++++ src/scanner.l | 2 ++ src/statement.c | 43 ++++++++++++++++++++++++ 8 files changed, 242 insertions(+) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 88e0ca1..d98cebb 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -1231,6 +1231,22 @@ enum nft_nat_attributes { }; #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) +/** + * enum nft_tproxy_attributes - nf_tables tproxy expression netlink attributes + * + * NFTA_TPROXY_FAMILY: Target address family (NLA_U32: nft_registers) + * NFTA_TPROXY_REG_ADDR: Target address register (NLA_U32: nft_registers) + * NFTA_TPROXY_REG_PORT: Target port register (NLA_U32: nft_registers) + */ +enum nft_tproxy_attributes { + NFTA_TPROXY_UNSPEC, + NFTA_TPROXY_FAMILY, + NFTA_TPROXY_REG_ADDR, + NFTA_TPROXY_REG_PORT, + __NFTA_TPROXY_MAX +}; +#define NFTA_TPROXY_MAX (__NFTA_TPROXY_MAX - 1) + /** * enum nft_masq_attributes - nf_tables masquerade expression attributes * diff --git a/include/statement.h b/include/statement.h index 5a907aa..edda98f 100644 --- a/include/statement.h +++ b/include/statement.h @@ -128,6 +128,14 @@ struct nat_stmt { extern struct stmt *nat_stmt_alloc(const struct location *loc, enum nft_nat_etypes type); +struct tproxy_stmt { + struct expr *addr; + struct expr *port; + uint8_t family; +}; + +extern struct stmt *tproxy_stmt_alloc(const struct location *loc); + struct queue_stmt { struct expr *queue; uint16_t flags; @@ -271,6 +279,7 @@ enum stmt_types { STMT_LOG, STMT_REJECT, STMT_NAT, + STMT_TPROXY, STMT_QUEUE, STMT_CT, STMT_SET, @@ -337,6 +346,7 @@ struct stmt { struct limit_stmt limit; struct reject_stmt reject; struct nat_stmt nat; + struct tproxy_stmt tproxy; struct queue_stmt queue; struct quota_stmt quota; struct ct_stmt ct; diff --git a/src/evaluate.c b/src/evaluate.c index 61b4697..f017ca8 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -2497,6 +2497,48 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt) return 0; } +static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt) +{ + int err; + + switch (ctx->pctx.family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return stmt_error(ctx, stmt, + "TPROXY is only supported for IPv4/IPv6/INET"); + } + stmt->tproxy.family = ctx->pctx.family; + + if (stmt->tproxy.addr != NULL) { + if (stmt->tproxy.addr->ops->type == EXPR_RANGE) + return -EOPNOTSUPP; + err = evaluate_addr(ctx, stmt, &stmt->tproxy.addr); + if (err < 0) + return err; + switch (stmt->tproxy.addr->dtype->type) { + case TYPE_IPADDR: + stmt->tproxy.family = NFPROTO_IPV4; + break; + case TYPE_IP6ADDR: + stmt->tproxy.family = NFPROTO_IPV6; + break; + } + } + + if (stmt->tproxy.port != NULL) { + if (stmt->tproxy.port->ops->type == EXPR_RANGE) + return -EOPNOTSUPP; + err = nat_evaluate_transport(ctx, stmt, &stmt->tproxy.port); + if (err < 0) + return err; + } + + return 0; +} + static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt) { int err; @@ -2780,6 +2822,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) return stmt_evaluate_reject(ctx, stmt); case STMT_NAT: return stmt_evaluate_nat(ctx, stmt); + case STMT_TPROXY: + return stmt_evaluate_tproxy(ctx, stmt); case STMT_QUEUE: return stmt_evaluate_queue(ctx, stmt); case STMT_DUP: diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 31d6242..6ede617 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -969,6 +969,49 @@ out_err: xfree(stmt); } +static void netlink_parse_tproxy(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + struct expr *addr, *port; + enum nft_registers reg; + + stmt = tproxy_stmt_alloc(loc); + stmt->tproxy.family = nftnl_expr_get_u32(nle, NFTNL_EXPR_TPROXY_FAMILY); + + reg = netlink_parse_register(nle, NFTNL_EXPR_TPROXY_REG_ADDR); + if (reg) { + addr = netlink_get_register(ctx, loc, reg); + + switch (stmt->tproxy.family) { + case NFPROTO_IPV4: + expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN); + break; + case NFPROTO_IPV6: + expr_set_type(addr, &ip6addr_type, BYTEORDER_BIG_ENDIAN); + break; + default: + netlink_error(ctx, loc, + "tproxy address must be IPv4 or IPv6"); + goto err; + } + stmt->tproxy.addr = addr; + } + + reg = netlink_parse_register(nle, NFTNL_EXPR_TPROXY_REG_PORT); + if (reg) { + port = netlink_get_register(ctx, loc, reg); + expr_set_type(port, &inet_service_type, BYTEORDER_BIG_ENDIAN); + stmt->tproxy.port = port; + } + + ctx->stmt = stmt; + return; +err: + xfree(stmt); +} + static void netlink_parse_masq(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nftnl_expr *nle) @@ -1362,6 +1405,7 @@ static const struct { { .name = "range", .parse = netlink_parse_range }, { .name = "reject", .parse = netlink_parse_reject }, { .name = "nat", .parse = netlink_parse_nat }, + { .name = "tproxy", .parse = netlink_parse_tproxy }, { .name = "notrack", .parse = netlink_parse_notrack }, { .name = "masq", .parse = netlink_parse_masq }, { .name = "redir", .parse = netlink_parse_redir }, @@ -2432,6 +2476,14 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r expr_postprocess(&rctx, &stmt->nat.proto); } break; + case STMT_TPROXY: + if (stmt->tproxy.addr) + expr_postprocess(&rctx, &stmt->tproxy.addr); + if (stmt->tproxy.port) { + payload_dependency_reset(&rctx.pdctx); + expr_postprocess(&rctx, &stmt->tproxy.port); + } + break; case STMT_REJECT: stmt_reject_postprocess(&rctx); break; diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 8471e83..33ec06d 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -1071,6 +1071,52 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, nftnl_rule_add_expr(ctx->nlr, nle); } +static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + enum nft_registers addr_reg; + enum nft_registers port_reg; + int registers = 0; + const int family = stmt->tproxy.family; + int nftnl_reg_port; + + nle = alloc_nft_expr("tproxy"); + + nftnl_expr_set_u32(nle, NFTNL_EXPR_TPROXY_FAMILY, family); + + nftnl_reg_port = NFTNL_EXPR_TPROXY_REG_PORT; + + if (stmt->tproxy.addr) { + addr_reg = get_register(ctx, NULL); + registers++; + + if (stmt->tproxy.addr->ops->type != EXPR_RANGE) { + netlink_gen_expr(ctx, stmt->tproxy.addr, addr_reg); + netlink_put_register(nle, NFTNL_EXPR_TPROXY_REG_ADDR, + addr_reg); + } + + } + + if (stmt->tproxy.port) { + port_reg = get_register(ctx, NULL); + registers++; + + if (stmt->tproxy.port->ops->type != EXPR_RANGE) { + netlink_gen_expr(ctx, stmt->tproxy.port, port_reg); + netlink_put_register(nle, nftnl_reg_port, port_reg); + } + } + + while (registers > 0) { + release_register(ctx, NULL); + registers--; + } + + nftnl_rule_add_expr(ctx->nlr, nle); +} + static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx, const struct stmt *stmt) { @@ -1301,6 +1347,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, return netlink_gen_reject_stmt(ctx, stmt); case STMT_NAT: return netlink_gen_nat_stmt(ctx, stmt); + case STMT_TPROXY: + return netlink_gen_tproxy_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 98bfeba..c45d6e4 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -192,6 +192,8 @@ int nft_lex(void *, void *, void *); %token SOCKET "socket" %token TRANSPARENT "transparent" +%token TPROXY "tproxy" + %token HOOK "hook" %token DEVICE "device" %token DEVICES "devices" @@ -572,6 +574,8 @@ int nft_lex(void *, void *, void *); %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 offset_opt +%type tproxy_stmt +%destructor { stmt_free($$); } tproxy_stmt %type queue_stmt queue_stmt_alloc %destructor { stmt_free($$); } queue_stmt queue_stmt_alloc %type queue_stmt_flags queue_stmt_flag @@ -2082,6 +2086,7 @@ stmt : verdict_stmt | quota_stmt | reject_stmt | nat_stmt + | tproxy_stmt | queue_stmt | ct_stmt | masq_stmt @@ -2477,6 +2482,28 @@ nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, NFT_NAT_SNAT); } | DNAT { $$ = nat_stmt_alloc(&@$, NFT_NAT_DNAT); } ; +tproxy_stmt : TPROXY + { + $$ = tproxy_stmt_alloc(&@$); + } + | TPROXY TO stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.addr = $3; + } + | TPROXY TO COLON stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.port = $4; + } + | TPROXY TO stmt_expr COLON stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.addr = $3; + $$->tproxy.port = $5; + } + ; + primary_stmt_expr : symbol_expr { $$ = $1; } | integer_expr { $$ = $1; } | boolean_expr { $$ = $1; } diff --git a/src/scanner.l b/src/scanner.l index ed01b5e..703700f 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -261,6 +261,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "socket" { return SOCKET; } "transparent" { return TRANSPARENT;} +"tproxy" { return TPROXY; } + "accept" { return ACCEPT; } "drop" { return DROP; } "continue" { return CONTINUE; } diff --git a/src/statement.c b/src/statement.c index 6f5e666..4537e45 100644 --- a/src/statement.c +++ b/src/statement.c @@ -619,6 +619,49 @@ struct stmt *nat_stmt_alloc(const struct location *loc, return stmt; } +static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "tproxy"); + + if (stmt->tproxy.addr || stmt->tproxy.port) + nft_print(octx, " to"); + if (stmt->tproxy.addr) { + nft_print(octx, " "); + if (stmt->tproxy.addr->ops->type == EXPR_VALUE && + stmt->tproxy.addr->dtype->type == TYPE_IP6ADDR) { + nft_print(octx, "["); + expr_print(stmt->tproxy.addr, octx); + nft_print(octx, "]"); + } else { + expr_print(stmt->tproxy.addr, octx); + } + } + if (stmt->tproxy.port && stmt->tproxy.port->ops->type == EXPR_VALUE) { + if (!stmt->tproxy.addr) + nft_print(octx, " "); + nft_print(octx, ":"); + expr_print(stmt->tproxy.port, octx); + } +} + +static void tproxy_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->tproxy.addr); + expr_free(stmt->tproxy.port); +} + +static const struct stmt_ops tproxy_stmt_ops = { + .type = STMT_TPROXY, + .name = "tproxy", + .print = tproxy_stmt_print, + .destroy = tproxy_stmt_destroy, +}; + +struct stmt *tproxy_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &tproxy_stmt_ops); +} + const char * const set_stmt_op_names[] = { [NFT_DYNSET_OP_ADD] = "add", [NFT_DYNSET_OP_UPDATE] = "update",