@@ -144,6 +144,7 @@ struct nat_stmt {
enum nft_nat_etypes type;
struct expr *addr;
struct expr *proto;
+ struct expr *proto_base;
uint32_t flags;
uint8_t family;
uint32_t type_flags;
@@ -3772,6 +3772,16 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
return err;
stmt->nat.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+
+ if (stmt->nat.proto_base != NULL) {
+ err = stmt_evaluate_arg(ctx, stmt,
+ &inet_service_type,
+ sizeof(uint16_t) * BITS_PER_BYTE,
+ BYTEORDER_BIG_ENDIAN,
+ &stmt->nat.proto_base);
+ if (err < 0)
+ return err;
+ }
}
stmt->flags |= STMT_F_TERMINAL;
@@ -1257,7 +1257,7 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
{
struct stmt *stmt;
struct expr *addr, *proto;
- enum nft_registers reg1, reg2;
+ enum nft_registers reg1, reg2, reg3;
int family;
stmt = nat_stmt_alloc(loc,
@@ -1352,6 +1352,20 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
if (stmt->nat.proto != NULL)
proto = range_expr_alloc(loc, stmt->nat.proto, proto);
stmt->nat.proto = proto;
+
+ reg3 = netlink_parse_register(nle, NFTNL_EXPR_NAT_REG_PROTO_BASE);
+ if (reg3) {
+ proto = netlink_get_register(ctx, loc, reg3);
+ if (proto == NULL) {
+ netlink_error(ctx, loc,
+ "NAT statement has no proto offset expression");
+ goto out_err;
+ }
+
+ expr_set_type(proto, &inet_service_type,
+ BYTEORDER_BIG_ENDIAN);
+ stmt->nat.proto_base = proto;
+ }
}
ctx->stmt = stmt;
@@ -1195,11 +1195,11 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
{
struct nftnl_expr *nle;
enum nft_registers amin_reg, amax_reg;
- enum nft_registers pmin_reg, pmax_reg;
+ enum nft_registers pmin_reg, pmax_reg, pbase_reg;
uint8_t family = 0;
int registers = 0;
int nftnl_flag_attr;
- int nftnl_reg_pmin, nftnl_reg_pmax;
+ int nftnl_reg_pmin, nftnl_reg_pmax, nftnl_reg_pbase;
switch (stmt->nat.type) {
case NFT_NAT_SNAT:
@@ -1211,8 +1211,9 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_NAT_FAMILY, family);
nftnl_flag_attr = NFTNL_EXPR_NAT_FLAGS;
- nftnl_reg_pmin = NFTNL_EXPR_NAT_REG_PROTO_MIN;
- nftnl_reg_pmax = NFTNL_EXPR_NAT_REG_PROTO_MAX;
+ nftnl_reg_pmin = NFTNL_EXPR_NAT_REG_PROTO_MIN;
+ nftnl_reg_pmax = NFTNL_EXPR_NAT_REG_PROTO_MAX;
+ nftnl_reg_pbase = NFTNL_EXPR_NAT_REG_PROTO_BASE;
break;
case NFT_NAT_MASQ:
nle = alloc_nft_expr("masq");
@@ -1308,6 +1309,16 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
netlink_gen_expr(ctx, stmt->nat.proto->right, pmax_reg);
netlink_put_register(nle, nftnl_reg_pmin, pmin_reg);
netlink_put_register(nle, nftnl_reg_pmax, pmax_reg);
+
+ if (stmt->nat.proto_base) {
+ pbase_reg = get_register(ctx, NULL);
+ registers++;
+
+ netlink_gen_expr(ctx, stmt->nat.proto_base,
+ pbase_reg);
+ netlink_put_register(nle, nftnl_reg_pbase,
+ pbase_reg);
+ }
} else {
netlink_gen_expr(ctx, stmt->nat.proto, pmin_reg);
netlink_put_register(nle, nftnl_reg_pmin, pmin_reg);
@@ -3833,24 +3833,53 @@ nat_stmt_args : stmt_expr
$<stmt>0->nat.addr = $1;
$<stmt>0->nat.proto = $3;
}
+ | stmt_expr COLON range_stmt_expr SLASH primary_stmt_expr
+ {
+ $<stmt>0->nat.addr = $1;
+ $<stmt>0->nat.proto = $3;
+ $<stmt>0->nat.proto_base = $5;
+ }
| TO stmt_expr COLON stmt_expr
{
$<stmt>0->nat.addr = $2;
$<stmt>0->nat.proto = $4;
}
+ | TO stmt_expr COLON range_stmt_expr SLASH primary_stmt_expr
+ {
+ $<stmt>0->nat.addr = $2;
+ $<stmt>0->nat.proto = $4;
+ $<stmt>0->nat.proto_base = $6;
+ }
| nf_key_proto TO stmt_expr COLON stmt_expr
{
$<stmt>0->nat.family = $1;
$<stmt>0->nat.addr = $3;
$<stmt>0->nat.proto = $5;
}
- | COLON stmt_expr
+ | nf_key_proto TO stmt_expr COLON range_stmt_expr SLASH primary_stmt_expr
+ {
+ $<stmt>0->nat.family = $1;
+ $<stmt>0->nat.addr = $3;
+ $<stmt>0->nat.proto = $5;
+ $<stmt>0->nat.proto_base = $7;
+ }
+ | COLON stmt_expr
+ {
+ $<stmt>0->nat.proto = $2;
+ }
+ | COLON range_stmt_expr SLASH primary_stmt_expr
{
$<stmt>0->nat.proto = $2;
+ $<stmt>0->nat.proto_base = $4;
+ }
+ | TO COLON stmt_expr
+ {
+ $<stmt>0->nat.proto = $3;
}
- | TO COLON stmt_expr
+ | TO COLON range_stmt_expr SLASH primary_stmt_expr
{
$<stmt>0->nat.proto = $3;
+ $<stmt>0->nat.proto_base = $5;
}
| nat_stmt_args nf_nat_flags
{
@@ -733,6 +733,10 @@ static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, " ");
nft_print(octx, ":");
expr_print(stmt->nat.proto, octx);
+ if (stmt->nat.proto_base) {
+ nft_print(octx, "/");
+ expr_print(stmt->nat.proto_base, octx);
+ }
}
print_nf_nat_flags(stmt->nat.flags, octx);
Support for shifted port-ranges was added to iptables for DNAT in 2018. This allows one to redirect packets intended for one port to another in a range in such a way that the new port chosen has the same offset in the range as the original port had from a specified base value. For example, by using the base value 2000, one could redirect packets intended for 10.0.0.1:2000-3000 to 10.10.0.1:12000-13000 so that the old and new ports were at the same offset in their respective ranges, i.e.: 10.0.0.1:2345 -> 10.10.0.1:12345 Make this functionality available in nftables: add rule t c ip daddr 10.0.0.1 tcp dport 2000-3000 dnat to 10.10.0.1:12000-13000/2000 persistent In contrast to iptables, where shifting is only available for DNAT, both DNAT and SNAT are supported. Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=970672 Link: https://bugzilla.netfilter.org/show_bug.cgi?id=1501 Signed-off-by: Jeremy Sowden <jeremy@azazel.net> --- include/statement.h | 1 + src/evaluate.c | 10 ++++++++++ src/netlink_delinearize.c | 16 +++++++++++++++- src/netlink_linearize.c | 19 +++++++++++++++---- src/parser_bison.y | 33 +++++++++++++++++++++++++++++++-- src/statement.c | 4 ++++ 6 files changed, 76 insertions(+), 7 deletions(-)