summaryrefslogtreecommitdiffstats
path: root/src/evaluate.c
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2019-02-02 00:36:51 +0100
committerFlorian Westphal <fw@strlen.de>2019-04-09 10:36:16 +0200
commitfbe27464dee4588d90649274925145421c84b449 (patch)
tree15433842e30b980c784b867cae558b46e109c6aa /src/evaluate.c
parent16ee51d84e0879b2bdc1135b75455f0cde3ed226 (diff)
src: add nat support for the inet family
consider a simple ip6 nat table: table ip6 nat { chain output { type nat hook output priority 0; policy accept; dnat to dead:2::99 } Now consider same ruleset, but using 'table inet nat': nft now lacks context to determine address family to parse 'to $address'. This adds code to make the following work: table inet nat { [ .. ] # detect af from network protocol context: ip6 daddr dead::2::1 dnat to dead:2::99 # use new dnat ip6 keyword: dnat ip6 to dead:2::99 } On list side, the keyword is only shown in the inet family, else the short version (dnat to ...) is used as the family is redundant when the table already mandates the ip protocol version supported. Address mismatches such as table ip6 { .. dnat ip to 1.2.3.4 are detected/handled during the evaluation phase. Signed-off-by: Florian Westphal <fw@strlen.de> Acked-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/evaluate.c')
-rw-r--r--src/evaluate.c124
1 files changed, 84 insertions, 40 deletions
diff --git a/src/evaluate.c b/src/evaluate.c
index 3a3f2468..3593eb80 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2507,9 +2507,28 @@ static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
{
+ const struct proto_desc *nproto;
+
switch (ctx->pctx.family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
+ if (stmt->nat.family == NFPROTO_UNSPEC)
+ stmt->nat.family = ctx->pctx.family;
+ return 0;
+ case NFPROTO_INET:
+ if (!stmt->nat.addr)
+ return 0;
+
+ if (stmt->nat.family != NFPROTO_UNSPEC)
+ return 0;
+
+ nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+
+ if (nproto == &proto_ip)
+ stmt->nat.family = NFPROTO_IPV4;
+ else if (nproto == &proto_ip6)
+ stmt->nat.family = NFPROTO_IPV6;
+
return 0;
default:
return stmt_error(ctx, stmt,
@@ -2551,6 +2570,55 @@ static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
BYTEORDER_BIG_ENDIAN, expr);
}
+static int stmt_evaluate_l3proto(struct eval_ctx *ctx,
+ struct stmt *stmt, uint8_t family)
+{
+ const struct proto_desc *nproto;
+
+ nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+
+ if ((nproto == &proto_ip && family != NFPROTO_IPV4) ||
+ (nproto == &proto_ip6 && family != NFPROTO_IPV6))
+ return stmt_binary_error(ctx, stmt,
+ &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ "conflicting protocols specified: %s vs. %s. You must specify ip or ip6 family in tproxy statement",
+ ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name,
+ family2str(stmt->tproxy.family));
+ return 0;
+}
+
+static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
+ uint8_t family,
+ struct expr **addr)
+{
+ const struct datatype *dtype;
+ unsigned int len;
+ int err;
+
+ if (ctx->pctx.family == NFPROTO_INET) {
+ switch (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,
+ "ip or ip6 must be specified with address for inet tables.");
+ }
+
+ err = stmt_evaluate_arg(ctx, stmt, dtype, len,
+ BYTEORDER_BIG_ENDIAN, addr);
+ } else {
+ err = evaluate_addr(ctx, stmt, addr);
+ }
+
+ return err;
+}
+
static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
{
int err;
@@ -2560,7 +2628,12 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
return err;
if (stmt->nat.addr != NULL) {
- err = evaluate_addr(ctx, stmt, &stmt->nat.addr);
+ err = stmt_evaluate_l3proto(ctx, stmt, stmt->nat.family);
+ if (err < 0)
+ return err;
+
+ err = stmt_evaluate_addr(ctx, stmt, stmt->nat.family,
+ &stmt->nat.addr);
if (err < 0)
return err;
}
@@ -2576,9 +2649,7 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
{
- const struct proto_desc *nproto;
- const struct datatype *dtype;
- int err, len;
+ int err;
switch (ctx->pctx.family) {
case NFPROTO_IPV4:
@@ -2600,46 +2671,19 @@ static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
if (!stmt->tproxy.addr && !stmt->tproxy.port)
return stmt_error(ctx, stmt, "Either address or port must be specified!");
- nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
- if ((nproto == &proto_ip && stmt->tproxy.family != NFPROTO_IPV4) ||
- (nproto == &proto_ip6 && stmt->tproxy.family != NFPROTO_IPV6))
- /* this prevents us from rules like
- * ip protocol tcp tproxy ip6 to [dead::beef]
- */
- return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
- "conflicting protocols specified: %s vs. %s. You must specify ip or ip6 family in tproxy statement",
- ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name,
- family2str(stmt->tproxy.family));
+ err = stmt_evaluate_l3proto(ctx, stmt, stmt->tproxy.family);
+ if (err < 0)
+ return err;
if (stmt->tproxy.addr != NULL) {
if (stmt->tproxy.addr->etype == EXPR_RANGE)
return stmt_error(ctx, stmt, "Address ranges are not supported for tproxy.");
- if (ctx->pctx.family == NFPROTO_INET) {
- switch (stmt->tproxy.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,
- "Family must be specified in tproxy statement with address for inet tables.");
- }
- err = stmt_evaluate_arg(ctx, stmt, dtype, len,
- BYTEORDER_BIG_ENDIAN,
- &stmt->tproxy.addr);
- if (err < 0)
- return err;
- }
- else {
- err = evaluate_addr(ctx, stmt, &stmt->tproxy.addr);
- if (err < 0)
- return err;
- }
+
+ err = stmt_evaluate_addr(ctx, stmt, stmt->tproxy.family,
+ &stmt->tproxy.addr);
+
+ if (err < 0)
+ return err;
}
if (stmt->tproxy.port != NULL) {