summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2017-11-09 03:42:55 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2018-06-06 19:18:43 +0200
commit30d45266bf38b209df33e4df1a116c60531ae3e5 (patch)
treeaf94699ae6d6a58edf84aabfff31bc82ff44e642
parent57e4a095edc4dab19e14fc8d1bca3febde1ca86c (diff)
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 <pablo@netfilter.org>
-rw-r--r--include/linux/netfilter/nf_tables.h4
-rw-r--r--include/statement.h4
-rw-r--r--src/evaluate.c27
-rw-r--r--src/json.c2
-rw-r--r--src/netlink_delinearize.c41
-rw-r--r--src/netlink_linearize.c19
-rw-r--r--src/parser_bison.y17
-rw-r--r--src/parser_json.c2
-rw-r--r--src/statement.c28
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 <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;
}
;
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 = {