diff options
author | Thierry Du Tre <thierry@dtsystems.be> | 2018-04-03 18:39:22 +0200 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2018-04-29 10:16:47 +0200 |
commit | 36976c4b54061b0147d56892ac9d402dae3069df (patch) | |
tree | 96b100fb28b911125cd6b2c00fd7b63dc7011d20 /extensions/libip6t_DNAT.c | |
parent | d7ac61b58e789c8ef7a503edd43013fd8524099f (diff) |
extensions: libipt_DNAT: support shifted portmap ranges
This is a proposal patch for iptables DNAT extension to support shifted portmap
ranges. It is related to the kernel patch proposed in earlier message '[PATCH
v5] netfilter : add NAT support for shifted portmap ranges'.
A new struct nf_nat_range2 was added as extension for existing struct
nf_nat_range and is used by new revisions (2) for the DNAT target. Current DNAT
revisions for Ipv4 (rev 0) and IPv6 (rev 1) are kept so functionality with
older kernels is not impacted.
The syntax for shifted portmaps uses an extra value in '--to-destination' for
setting the base port which determines the offset in the redirect port range
for incoming connections. i.e. : iptables -t nat -A zone_wan_prerouting -p tcp
-m tcp --dport 5000:5100 -j DNAT --to-destination '192.168.1.2:2000-2100/5000'
The base port value is totally optional, so current behavior is not impacted in
any way. The use of slash '/' as separator is an arbitrary choice, all other
suggestions are valid of course (original proposal used semicolon but this was
not practical for commandline use) Another approach using an additional option
seems also possible (i.e. '--base-port 5000'). However, that would mean more
parsing logic with extra lines of code and thus increased risk for regression.
Signed-off-by: Thierry Du Tre <thierry@dtsystems.be>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'extensions/libip6t_DNAT.c')
-rw-r--r-- | extensions/libip6t_DNAT.c | 184 |
1 files changed, 147 insertions, 37 deletions
diff --git a/extensions/libip6t_DNAT.c b/extensions/libip6t_DNAT.c index 8e478b21..2a7574b0 100644 --- a/extensions/libip6t_DNAT.c +++ b/extensions/libip6t_DNAT.c @@ -34,6 +34,15 @@ static void DNAT_help(void) "[--random] [--persistent]\n"); } +static void DNAT_help_v2(void) +{ + printf( +"DNAT target options:\n" +" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port[/port]]]\n" +" Address to map destination to.\n" +"[--random] [--persistent]\n"); +} + static const struct xt_option_entry DNAT_opts[] = { {.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING, .flags = XTOPT_MAND | XTOPT_MULTI}, @@ -44,7 +53,7 @@ static const struct xt_option_entry DNAT_opts[] = { /* Ranges expected in network order. */ static void -parse_to(const char *orig_arg, int portok, struct nf_nat_range *range) +parse_to(const char *orig_arg, int portok, struct nf_nat_range2 *range, int rev) { char *arg, *start, *end = NULL, *colon = NULL, *dash, *error; const struct in6_addr *ip; @@ -109,6 +118,20 @@ parse_to(const char *orig_arg, int portok, struct nf_nat_range *range) "Port range `%s' funky\n", colon+1); range->min_proto.tcp.port = htons(port); range->max_proto.tcp.port = htons(maxport); + + if (rev >= 2) { + char *slash = strchr(dash, '/'); + if (slash) { + int baseport; + + baseport = atoi(slash + 1); + if (baseport <= 0 || baseport > 65535) + xtables_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", slash+1); + range->flags |= NF_NAT_RANGE_PROTO_OFFSET; + range->base_proto.tcp.port = htons(baseport); + } + } } /* Starts with colon or [] colon? No IP info...*/ if (colon == arg || colon == arg+2) { @@ -144,10 +167,10 @@ parse_to(const char *orig_arg, int portok, struct nf_nat_range *range) return; } -static void DNAT_parse(struct xt_option_call *cb) +static void _DNAT_parse(struct xt_option_call *cb, + struct nf_nat_range2 *range, int rev) { const struct ip6t_entry *entry = cb->xt_entry; - struct nf_nat_range *range = cb->data; int portok; if (entry->ipv6.proto == IPPROTO_TCP || @@ -166,7 +189,7 @@ static void DNAT_parse(struct xt_option_call *cb) xtables_error(PARAMETER_PROBLEM, "DNAT: Multiple --to-destination not supported"); } - parse_to(cb->arg, portok, range); + parse_to(cb->arg, portok, range, rev); cb->xflags |= F_X_TO_DEST; break; case O_PERSISTENT: @@ -175,16 +198,40 @@ static void DNAT_parse(struct xt_option_call *cb) } } -static void DNAT_fcheck(struct xt_fcheck_call *cb) +static void DNAT_parse(struct xt_option_call *cb) +{ + struct nf_nat_range *range_v1 = (void *)cb->data; + struct nf_nat_range2 range = {}; + + memcpy(&range, range_v1, sizeof(*range_v1)); + _DNAT_parse(cb, &range, 1); + memcpy(range_v1, &range, sizeof(*range_v1)); +} + +static void DNAT_parse_v2(struct xt_option_call *cb) +{ + _DNAT_parse(cb, (struct nf_nat_range2 *)cb->data, 2); +} + +static void _DNAT_fcheck(struct xt_fcheck_call *cb, unsigned int *flags) { static const unsigned int f = F_TO_DEST | F_RANDOM; - struct nf_nat_range *mr = cb->data; if ((cb->xflags & f) == f) - mr->flags |= NF_NAT_RANGE_PROTO_RANDOM; + *flags |= NF_NAT_RANGE_PROTO_RANDOM; +} + +static void DNAT_fcheck(struct xt_fcheck_call *cb) +{ + _DNAT_fcheck(cb, &((struct nf_nat_range *)cb->data)->flags); } -static void print_range(const struct nf_nat_range *range) +static void DNAT_fcheck_v2(struct xt_fcheck_call *cb) +{ + _DNAT_fcheck(cb, &((struct nf_nat_range2 *)cb->data)->flags); +} + +static void print_range(const struct nf_nat_range2 *range, int rev) { if (range->flags & NF_NAT_RANGE_MAP_IPS) { if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) @@ -201,36 +248,63 @@ static void print_range(const struct nf_nat_range *range) printf("%hu", ntohs(range->min_proto.tcp.port)); if (range->max_proto.tcp.port != range->min_proto.tcp.port) printf("-%hu", ntohs(range->max_proto.tcp.port)); + if (rev >= 2 && (range->flags & NF_NAT_RANGE_PROTO_OFFSET)) + printf("/%hu", ntohs(range->base_proto.tcp.port)); } } -static void DNAT_print(const void *ip, const struct xt_entry_target *target, - int numeric) +static void _DNAT_print(const struct nf_nat_range2 *range, int rev) { - const struct nf_nat_range *range = (const void *)target->data; - printf(" to:"); - print_range(range); + print_range(range, rev); if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) printf(" random"); if (range->flags & NF_NAT_RANGE_PERSISTENT) printf(" persistent"); } -static void DNAT_save(const void *ip, const struct xt_entry_target *target) +static void DNAT_print(const void *ip, const struct xt_entry_target *target, + int numeric) { - const struct nf_nat_range *range = (const void *)target->data; + const struct nf_nat_range *range_v1 = (const void *)target->data; + struct nf_nat_range2 range = {}; + memcpy(&range, range_v1, sizeof(*range_v1)); + _DNAT_print(&range, 1); +} + +static void DNAT_print_v2(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + _DNAT_print((const struct nf_nat_range2 *)target->data, 2); +} + +static void _DNAT_save(const struct nf_nat_range2 *range, int rev) +{ printf(" --to-destination "); - print_range(range); + print_range(range, rev); if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) printf(" --random"); if (range->flags & NF_NAT_RANGE_PERSISTENT) printf(" --persistent"); } -static void print_range_xlate(const struct nf_nat_range *range, - struct xt_xlate *xl) +static void DNAT_save(const void *ip, const struct xt_entry_target *target) +{ + const struct nf_nat_range *range_v1 = (const void *)target->data; + struct nf_nat_range2 range = {}; + + memcpy(&range, range_v1, sizeof(*range_v1)); + _DNAT_save(&range, 1); +} + +static void DNAT_save_v2(const void *ip, const struct xt_entry_target *target) +{ + _DNAT_save((const struct nf_nat_range2 *)target->data, 2); +} + +static void print_range_xlate(const struct nf_nat_range2 *range, + struct xt_xlate *xl, int rev) { bool proto_specified = range->flags & NF_NAT_RANGE_PROTO_SPECIFIED; @@ -257,15 +331,14 @@ static void print_range_xlate(const struct nf_nat_range *range, } } -static int DNAT_xlate(struct xt_xlate *xl, - const struct xt_xlate_tg_params *params) +static int _DNAT_xlate(struct xt_xlate *xl, + const struct nf_nat_range2 *range, int rev) { - const struct nf_nat_range *range = (const void *)params->target->data; bool sep_need = false; const char *sep = " "; xt_xlate_add(xl, "dnat to "); - print_range_xlate(range, xl); + print_range_xlate(range, xl, rev); if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) { xt_xlate_add(xl, " random"); sep_need = true; @@ -279,23 +352,60 @@ static int DNAT_xlate(struct xt_xlate *xl, return 1; } -static struct xtables_target dnat_tg_reg = { - .name = "DNAT", - .version = XTABLES_VERSION, - .family = NFPROTO_IPV6, - .revision = 1, - .size = XT_ALIGN(sizeof(struct nf_nat_range)), - .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)), - .help = DNAT_help, - .x6_parse = DNAT_parse, - .x6_fcheck = DNAT_fcheck, - .print = DNAT_print, - .save = DNAT_save, - .x6_options = DNAT_opts, - .xlate = DNAT_xlate, +static int DNAT_xlate(struct xt_xlate *xl, + const struct xt_xlate_tg_params *params) +{ + const struct nf_nat_range *range_v1 = (const void *)params->target->data; + struct nf_nat_range2 range = {}; + + memcpy(&range, range_v1, sizeof(*range_v1)); + _DNAT_xlate(xl, &range, 1); + + return 1; +} + +static int DNAT_xlate_v2(struct xt_xlate *xl, + const struct xt_xlate_tg_params *params) +{ + _DNAT_xlate(xl, (const struct nf_nat_range2 *)params->target->data, 2); + + return 1; +} + +static struct xtables_target dnat_tg_reg[] = { + { + .name = "DNAT", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .revision = 1, + .size = XT_ALIGN(sizeof(struct nf_nat_range)), + .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)), + .help = DNAT_help, + .print = DNAT_print, + .save = DNAT_save, + .x6_parse = DNAT_parse, + .x6_fcheck = DNAT_fcheck, + .x6_options = DNAT_opts, + .xlate = DNAT_xlate, + }, + { + .name = "DNAT", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .revision = 2, + .size = XT_ALIGN(sizeof(struct nf_nat_range)), + .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)), + .help = DNAT_help_v2, + .print = DNAT_print_v2, + .save = DNAT_save_v2, + .x6_parse = DNAT_parse_v2, + .x6_fcheck = DNAT_fcheck_v2, + .x6_options = DNAT_opts, + .xlate = DNAT_xlate_v2, + }, }; void _init(void) { - xtables_register_target(&dnat_tg_reg); + xtables_register_targets(dnat_tg_reg, ARRAY_SIZE(dnat_tg_reg)); } |