]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add redirect support
authorArturo Borrero <arturo.borrero.glez@gmail.com>
Mon, 3 Nov 2014 20:20:11 +0000 (21:20 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 4 Nov 2014 13:38:04 +0000 (14:38 +0100)
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 <arturo.borrero.glez@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/statement.h
src/evaluate.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser.y
src/scanner.l
src/statement.c
tests/regression/ip/redirect.t [new file with mode: 0644]
tests/regression/ip6/redirect.t [new file with mode: 0644]

index 35c1b7ae44304a02842a6109d325f4cb5809d5ec..d1431215c35ef2999286dc868d837400b01ed115 100644 (file)
@@ -79,6 +79,13 @@ struct masq_stmt {
 
 extern struct stmt *masq_stmt_alloc(const struct location *loc);
 
+struct redir_stmt {
+       struct expr             *proto;
+       uint32_t                flags;
+};
+
+extern struct stmt *redir_stmt_alloc(const struct location *loc);
+
 struct queue_stmt {
        struct expr             *queue;
        uint16_t                flags;
@@ -110,6 +117,7 @@ extern struct stmt *ct_stmt_alloc(const struct location *loc,
  * @STMT_REJECT:       REJECT statement
  * @STMT_NAT:          NAT statement
  * @STMT_MASQ:         masquerade statement
+ * @STMT_REDIR:                redirect statement
  * @STMT_QUEUE:                QUEUE statement
  * @STMT_CT:           conntrack statement
  */
@@ -124,6 +132,7 @@ enum stmt_types {
        STMT_REJECT,
        STMT_NAT,
        STMT_MASQ,
+       STMT_REDIR,
        STMT_QUEUE,
        STMT_CT,
 };
@@ -172,6 +181,7 @@ struct stmt {
                struct reject_stmt      reject;
                struct nat_stmt         nat;
                struct masq_stmt        masq;
+               struct redir_stmt       redir;
                struct queue_stmt       queue;
                struct ct_stmt          ct;
        };
index b722567ce102718b209be249dfeeb4e1bf904773..3eeb614cd4b0628735411040fbc385db6cb6ec88 100644 (file)
@@ -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:
index 8f90cc036b1e4d5a6c8c696c343c69f49e623515..1be409b18f8ac0a934c2a959226a178ef357a51f 100644 (file)
@@ -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;
index 62155cc3d453e4abfb2b6357c9798e24cd5a5aff..de338cb78c2d510c34a057ab5e09daaee600d83c 100644 (file)
@@ -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:
index 9e9a83935728f5ecb77645abf57bb8bc8837e0ed..6209e9eb526699af32fcb2e8c34c456c0f366a2f 100644 (file)
@@ -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 <val>                    time_unit
 %type <stmt>                   reject_stmt reject_stmt_alloc
 %destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
-%type <stmt>                   nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc
-%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc
+%type <stmt>                   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 <val>                    nf_nat_flags nf_nat_flag
 %type <stmt>                   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
+                       {
+                               $<stmt>0->redir.proto = $2;
+                       }
+                       |       nf_nat_flags
+                       {
+                               $<stmt>0->redir.flags = $1;
+                       }
+                       ;
+
 nf_nat_flags           :       nf_nat_flag
                        |       nf_nat_flags    COMMA   nf_nat_flag
                        {
index 32e59d98f2780912b73b02e47ddae143540a3725..e36c3b13785460aa817cefa103b83306b07d6d49 100644 (file)
@@ -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; }
index 0ae616a83a536ae3450e15a1aba385ffa06f01aa..2587d2754b1f07a861b241522ce37cb3f1fc19b6 100644 (file)
@@ -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);
+}
diff --git a/tests/regression/ip/redirect.t b/tests/regression/ip/redirect.t
new file mode 100644 (file)
index 0000000..8e0f783
--- /dev/null
@@ -0,0 +1,41 @@
+*ip;test-ip4
+:output;type nat hook output priority 0
+
+# without arguments
+udp dport 53 redirect ;ok
+
+# nf_nat flags combination
+udp dport 53 redirect random ;ok
+udp dport 53 redirect random,persistent ;ok
+udp dport 53 redirect random,persistent,random-fully ;ok ;udp dport 53 redirect random,random-fully,persistent
+udp dport 53 redirect random,random-fully ;ok
+udp dport 53 redirect random,random-fully,persistent ;ok
+udp dport 53 redirect persistent ;ok
+udp dport 53 redirect persistent,random ;ok ;udp dport 53 redirect random,persistent
+udp dport 53 redirect persistent,random,random-fully ;ok ;udp dport 53 redirect random,random-fully,persistent
+udp dport 53 redirect persistent,random-fully ;ok ;udp dport 53 redirect random-fully,persistent
+udp dport 53 redirect persistent,random-fully,random;ok ;udp dport 53 redirect random,random-fully,persistent
+
+# port specification
+tcp dport 22 redirect :22 ;ok
+udp dport 1234 redirect :4321 ;ok
+ip daddr 172.16.0.1 udp dport 9998 redirect :6515 ;ok
+tcp dport 39128 redirect :993 ;ok
+redirect :1234 ;nok
+redirect :12341111 ;nok
+
+# invalid arguments
+tcp dport 9128 redirect :993 random ;nok
+tcp dport 9128 redirect :993 random-fully ;nok
+tcp dport 9128 redirect persistent :123 ;nok
+tcp dport 9128 redirect random,persistent :123 ;nok
+
+# redirect is a terminal statement
+tcp dport 22 redirect counter packets 0 bytes 0 accept ;nok
+tcp sport 22 redirect accept ;nok
+ip saddr 10.1.1.1 redirect drop ;nok
+
+# redirect with sets
+tcp dport {1,2,3,4,5,6,7,8,101,202,303,1001,2002,3003} redirect ;ok
+ip daddr 10.0.0.0-10.2.3.4 udp dport 53 counter packets 0 bytes 0 redirect ;ok ;ip daddr >= 10.0.0.0 ip daddr <= 10.2.3.4 udp dport 53 counter packets 0 bytes 0 redirect
+iifname eth0 ct state new,established tcp dport vmap {22 : drop, 222 : drop } redirect ;ok
diff --git a/tests/regression/ip6/redirect.t b/tests/regression/ip6/redirect.t
new file mode 100644 (file)
index 0000000..84ed88f
--- /dev/null
@@ -0,0 +1,42 @@
+*ip6;test-ip6
+:output;type nat hook output priority 0
+
+# with no arguments
+redirect ;ok
+udp dport 954 redirect ;ok
+ip6 saddr fe00::cafe counter packets 0 bytes 0 redirect ;ok
+
+# nf_nat flags combination
+udp dport 53 redirect random ;ok
+udp dport 53 redirect random,persistent ;ok
+udp dport 53 redirect random,persistent,random-fully ;ok ;udp dport 53 redirect random,random-fully,persistent
+udp dport 53 redirect random,random-fully ;ok
+udp dport 53 redirect random,random-fully,persistent ;ok
+udp dport 53 redirect persistent ;ok
+udp dport 53 redirect persistent,random ;ok ;udp dport 53 redirect random,persistent
+udp dport 53 redirect persistent,random,random-fully ;ok ;udp dport 53 redirect random,random-fully,persistent
+udp dport 53 redirect persistent,random-fully ;ok ;udp dport 53 redirect random-fully,persistent
+udp dport 53 redirect persistent,random-fully,random;ok ;udp dport 53 redirect random,random-fully,persistent
+
+# port specification
+udp dport 1234 redirect :1234 ;ok
+ip6 daddr fe00::cafe udp dport 9998 redirect :6515 ;ok
+tcp dport 39128 redirect :993 ;ok
+redirect :1234 ;nok
+redirect :12341111 ;nok
+
+# invalid arguments
+tcp dport 9128 redirect :993 random ;nok
+tcp dport 9128 redirect :993 random-fully ;nok
+tcp dport 9128 redirect persistent :123 ;nok
+tcp dport 9128 redirect random,persistent :123 ;nok
+
+# redirect is a terminal statement
+tcp dport 22 redirect counter packets 0 bytes 0 accept ;nok
+tcp sport 22 redirect accept ;nok
+ip6 saddr ::1 redirect drop ;nok
+
+# redirect with sets
+tcp dport {1,2,3,4,5,6,7,8,101,202,303,1001,2002,3003} redirect ;ok
+ip6 daddr fe00::1-fe00::200 udp dport 53 counter packets 0 bytes 0 redirect ;ok ;ip6 daddr >= fe00::1 ip6 daddr <= fe00::200 udp dport 53 counter packets 0 bytes 0 redirect
+iifname eth0 ct state new,established tcp dport vmap {22 : drop, 222 : drop } redirect ;ok