]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
expr: extend fwd statement to support address and family
authorPablo Neira Ayuso <pablo@netfilter.org>
Thu, 9 Nov 2017 02:42:55 +0000 (03:42 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 6 Jun 2018 17:18:43 +0000 (19:18 +0200)
Allow to forward packets through to explicit destination and interface.

  nft add rule netdev x y fwd ip to 192.168.2.200 device eth0

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/nf_tables.h
include/statement.h
src/evaluate.c
src/json.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser_bison.y
src/parser_json.c
src/statement.c

index 40d4327151d97f2b3053bc7c214e30b1acc0d8dd..f46239ecdd1a5eafd047b9a10610e74426a874c1 100644 (file)
@@ -1253,10 +1253,14 @@ enum nft_dup_attributes {
  * enum nft_fwd_attributes - nf_tables fwd expression netlink attributes
  *
  * @NFTA_FWD_SREG_DEV: source register of output interface (NLA_U32: nft_register)
+ * @NFTA_FWD_SREG_ADDR: source register of destination address (NLA_U32: nft_register)
+ * @NFTA_FWD_NFPROTO: layer 3 family of source register address (NLA_U32: enum nfproto)
  */
 enum nft_fwd_attributes {
        NFTA_FWD_UNSPEC,
        NFTA_FWD_SREG_DEV,
+       NFTA_FWD_SREG_ADDR,
+       NFTA_FWD_NFPROTO,
        __NFTA_FWD_MAX
 };
 #define NFTA_FWD_MAX   (__NFTA_FWD_MAX - 1)
index d4bcaf3ae1452084c6f6a96461591955edcb2a51..5a907aa4dee48ddd07abef7c9e1998d62a0b7b7f 100644 (file)
@@ -164,7 +164,9 @@ struct stmt *dup_stmt_alloc(const struct location *loc);
 uint32_t dup_stmt_type(const char *type);
 
 struct fwd_stmt {
-       struct expr             *to;
+       uint8_t                 family;
+       struct expr             *addr;
+       struct expr             *dev;
 };
 
 struct stmt *fwd_stmt_alloc(const struct location *loc);
index 039e02dbaaaa1d151cae13d981e3b190277dfc26..ba218ecb5f574b863c8cdbedbc6ab2021bd1f973 100644 (file)
@@ -2512,19 +2512,40 @@ static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
 
 static int stmt_evaluate_fwd(struct eval_ctx *ctx, struct stmt *stmt)
 {
-       int err;
+       const struct datatype *dtype;
+       int err, len;
 
        switch (ctx->pctx.family) {
        case NFPROTO_NETDEV:
-               if (stmt->fwd.to == NULL)
+               if (stmt->fwd.dev == NULL)
                        return stmt_error(ctx, stmt,
                                          "missing destination interface");
 
                err = stmt_evaluate_arg(ctx, stmt, &ifindex_type,
                                        sizeof(uint32_t) * BITS_PER_BYTE,
-                                       BYTEORDER_HOST_ENDIAN, &stmt->fwd.to);
+                                       BYTEORDER_HOST_ENDIAN, &stmt->fwd.dev);
                if (err < 0)
                        return err;
+
+               if (stmt->fwd.addr != NULL) {
+                       switch (stmt->fwd.family) {
+                       case NFPROTO_IPV4:
+                               dtype = &ipaddr_type;
+                               len   = 4 * BITS_PER_BYTE;
+                               break;
+                       case NFPROTO_IPV6:
+                               dtype = &ip6addr_type;
+                               len   = 16 * BITS_PER_BYTE;
+                               break;
+                       default:
+                               return stmt_error(ctx, stmt, "missing family");
+                       }
+                       err = stmt_evaluate_arg(ctx, stmt, dtype, len,
+                                               BYTEORDER_BIG_ENDIAN,
+                                               &stmt->fwd.addr);
+                       if (err < 0)
+                               return err;
+               }
                break;
        default:
                return stmt_error(ctx, stmt, "unsupported family");
index 0777448298bedd7fcbded4cfe620a5463a859528..b6e6ca9c6c38338ffd879eb0c2d45531e189dcf9 100644 (file)
@@ -998,7 +998,7 @@ json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
 {
        json_t *root;
 
-       root = expr_print_json(stmt->fwd.to, octx);
+       root = expr_print_json(stmt->fwd.dev, octx);
        return json_pack("{s:o}", "fwd", root);
 }
 
index 7dbf596af36e882c94da7adba5e019d4db6b28d8..1c3a4fb72404d418cbfe8a34ed5808bd84ad917e 100644 (file)
@@ -1109,8 +1109,8 @@ static void netlink_parse_fwd(struct netlink_parse_ctx *ctx,
                              const struct location *loc,
                              const struct nftnl_expr *nle)
 {
-       enum nft_registers reg1;
-       struct expr *dev;
+       enum nft_registers reg1, reg2;
+       struct expr *dev, *addr;
        struct stmt *stmt;
 
        stmt = fwd_stmt_alloc(loc);
@@ -1125,7 +1125,37 @@ static void netlink_parse_fwd(struct netlink_parse_ctx *ctx,
                }
 
                expr_set_type(dev, &ifindex_type, BYTEORDER_HOST_ENDIAN);
-               stmt->fwd.to = dev;
+               stmt->fwd.dev = dev;
+       }
+
+       if (nftnl_expr_is_set(nle, NFTNL_EXPR_FWD_NFPROTO)) {
+               stmt->fwd.family =
+                       nftnl_expr_get_u32(nle, NFTNL_EXPR_FWD_NFPROTO);
+       }
+
+       if (nftnl_expr_is_set(nle, NFTNL_EXPR_FWD_SREG_ADDR)) {
+               reg2 = netlink_parse_register(nle, NFTNL_EXPR_FWD_SREG_ADDR);
+               if (reg2) {
+                       addr = netlink_get_register(ctx, loc, reg2);
+                       if (addr == NULL)
+                               return netlink_error(ctx, loc,
+                                                    "fwd statement has no output expression");
+
+                       switch (stmt->fwd.family) {
+                       case AF_INET:
+                               expr_set_type(addr, &ipaddr_type,
+                                             BYTEORDER_BIG_ENDIAN);
+                               break;
+                       case AF_INET6:
+                               expr_set_type(addr, &ip6addr_type,
+                                             BYTEORDER_BIG_ENDIAN);
+                               break;
+                       default:
+                               return netlink_error(ctx, loc,
+                                                    "fwd statement has no family");
+                       }
+                       stmt->fwd.addr = addr;
+               }
        }
 
        ctx->stmt = stmt;
@@ -2398,8 +2428,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
                                expr_postprocess(&rctx, &stmt->dup.dev);
                        break;
                case STMT_FWD:
-                       if (stmt->fwd.to != NULL)
-                               expr_postprocess(&rctx, &stmt->fwd.to);
+                       expr_postprocess(&rctx, &stmt->fwd.dev);
+                       if (stmt->fwd.addr != NULL)
+                               expr_postprocess(&rctx, &stmt->fwd.addr);
                        break;
                case STMT_XT:
                        stmt_xt_postprocess(&rctx, stmt, rule);
index 13c3564fb007fa91510bd9fe95fd6a0326376f8c..2aadc1ee856f23c7e93782d840b050fdd220c52d 100644 (file)
@@ -1091,15 +1091,26 @@ static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx,
 static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx,
                                 const struct stmt *stmt)
 {
-       enum nft_registers sreg1;
+       enum nft_registers sreg1, sreg2;
        struct nftnl_expr *nle;
 
        nle = alloc_nft_expr("fwd");
 
-       sreg1 = get_register(ctx, stmt->fwd.to);
-       netlink_gen_expr(ctx, stmt->fwd.to, sreg1);
+       sreg1 = get_register(ctx, stmt->fwd.dev);
+       netlink_gen_expr(ctx, stmt->fwd.dev, sreg1);
        netlink_put_register(nle, NFTNL_EXPR_FWD_SREG_DEV, sreg1);
-       release_register(ctx, stmt->fwd.to);
+
+       if (stmt->fwd.addr != NULL) {
+               sreg2 = get_register(ctx, stmt->fwd.addr);
+               netlink_gen_expr(ctx, stmt->fwd.addr, sreg2);
+               netlink_put_register(nle, NFTNL_EXPR_FWD_SREG_ADDR, sreg2);
+               release_register(ctx, stmt->fwd.addr);
+       }
+       release_register(ctx, stmt->fwd.dev);
+
+       if (stmt->fwd.family)
+               nftnl_expr_set_u32(nle, NFTNL_EXPR_FWD_NFPROTO,
+                                  stmt->fwd.family);
 
        nftnl_rule_add_expr(ctx->nlr, nle);
 }
index 5797ee766090a06e5fdfa4377a9d450b6bad390d..c6491a3b0aa1572514715e66dff402356dbf57d3 100644 (file)
@@ -698,6 +698,8 @@ int nft_lex(void *, void *, void *);
 %destructor { expr_free($$); } rt_expr
 %type <val>                    rt_key
 
+%type <val>                    fwd_key_proto
+
 %type <expr>                   ct_expr
 %destructor { expr_free($$); } ct_expr
 %type <val>                    ct_key          ct_dir  ct_key_dir_optional     ct_key_dir      ct_key_proto_field
@@ -2675,10 +2677,21 @@ dup_stmt                :       DUP     TO      stmt_expr
                        }
                        ;
 
-fwd_stmt               :       FWD     TO      expr
+fwd_key_proto          :       IP              { $$ = NFPROTO_IPV4; }
+                       |       IP6             { $$ = NFPROTO_IPV6; }
+                       ;
+
+fwd_stmt               :       FWD     TO      stmt_expr
+                       {
+                               $$ = fwd_stmt_alloc(&@$);
+                               $$->fwd.dev = $3;
+                       }
+                       |       FWD     fwd_key_proto   TO      stmt_expr       DEVICE  stmt_expr
                        {
                                $$ = fwd_stmt_alloc(&@$);
-                               $$->fwd.to = $3;
+                               $$->fwd.family = $2;
+                               $$->fwd.addr = $4;
+                               $$->fwd.dev = $6;
                        }
                        ;
 
index adf1564b9bfea5f7cd3b7c0757cc903bf3d6b273..6e14fb7278fb038f32700ce9263f6f7eb45c3e5b 100644 (file)
@@ -1563,7 +1563,7 @@ static struct stmt *json_parse_fwd_stmt(struct json_ctx *ctx,
 {
        struct stmt *stmt = fwd_stmt_alloc(int_loc);
 
-       stmt->fwd.to = json_parse_expr(ctx, value);
+       stmt->fwd.dev = json_parse_expr(ctx, value);
 
        return stmt;
 }
index 6f490132db2661a2a9b160b6d5d0fb5e9dc8d824..58e86f215d5aca10f512ab56adc34a52d1d271c0 100644 (file)
@@ -713,15 +713,37 @@ struct stmt *dup_stmt_alloc(const struct location *loc)
        return stmt_alloc(loc, &dup_stmt_ops);
 }
 
+static const char * const nfproto_family_name_array[NFPROTO_NUMPROTO] = {
+       [NFPROTO_IPV4]  = "ip",
+       [NFPROTO_IPV6]  = "ip6",
+};
+
+static const char *nfproto_family_name(uint8_t nfproto)
+{
+       if (nfproto >= NFPROTO_NUMPROTO || !nfproto_family_name_array[nfproto])
+               return "unknown";
+
+       return nfproto_family_name_array[nfproto];
+}
+
 static void fwd_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
 {
-       nft_print(octx, "fwd to ");
-       expr_print(stmt->fwd.to, octx);
+       if (stmt->fwd.addr) {
+               nft_print(octx, "fwd %s to ",
+                         nfproto_family_name(stmt->fwd.family));
+               expr_print(stmt->fwd.addr, octx);
+               nft_print(octx, " device ");
+               expr_print(stmt->fwd.dev, octx);
+       } else {
+               nft_print(octx, "fwd to ");
+               expr_print(stmt->fwd.dev, octx);
+       }
 }
 
 static void fwd_stmt_destroy(struct stmt *stmt)
 {
-       expr_free(stmt->fwd.to);
+       expr_free(stmt->fwd.addr);
+       expr_free(stmt->fwd.dev);
 }
 
 static const struct stmt_ops fwd_stmt_ops = {