From 30d45266bf38b209df33e4df1a116c60531ae3e5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 9 Nov 2017 03:42:55 +0100 Subject: expr: extend fwd statement to support address and family 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 --- include/linux/netfilter/nf_tables.h | 4 ++++ include/statement.h | 4 +++- src/evaluate.c | 27 +++++++++++++++++++++--- src/json.c | 2 +- src/netlink_delinearize.c | 41 ++++++++++++++++++++++++++++++++----- src/netlink_linearize.c | 19 +++++++++++++---- src/parser_bison.y | 17 +++++++++++++-- src/parser_json.c | 2 +- src/statement.c | 28 ++++++++++++++++++++++--- 9 files changed, 124 insertions(+), 20 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 40d43271..f46239ec 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -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) diff --git a/include/statement.h b/include/statement.h index d4bcaf3a..5a907aa4 100644 --- a/include/statement.h +++ b/include/statement.h @@ -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); diff --git a/src/evaluate.c b/src/evaluate.c index 039e02db..ba218ecb 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -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"); diff --git a/src/json.c b/src/json.c index 07774482..b6e6ca9c 100644 --- a/src/json.c +++ b/src/json.c @@ -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); } diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 7dbf596a..1c3a4fb7 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -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); diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 13c3564f..2aadc1ee 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -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); } diff --git a/src/parser_bison.y b/src/parser_bison.y index 5797ee76..c6491a3b 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -698,6 +698,8 @@ int nft_lex(void *, void *, void *); %destructor { expr_free($$); } rt_expr %type rt_key +%type fwd_key_proto + %type ct_expr %destructor { expr_free($$); } ct_expr %type 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; } ; diff --git a/src/parser_json.c b/src/parser_json.c index adf1564b..6e14fb72 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -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; } diff --git a/src/statement.c b/src/statement.c index 6f490132..58e86f21 100644 --- a/src/statement.c +++ b/src/statement.c @@ -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 = { -- cgit v1.2.3