From 3f4f1cf075572045a910b5c4d625602b9ba3c349 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Mon, 10 Jan 2022 16:39:53 +0100 Subject: extensions: ipt_DNAT: Merge v1 and v2 parsers Use v2 parser for both and copy field values into v1 data structure if needed. While being at it: * Introduce parse_ports() function similar to the one in libipt_REDIRECT.c. * Use xtables_strtoui() in the above instead of atoi() for integrated range checking. * Parse IP addresses using inet_pton(), writing directly into struct nf_nat_range2 fields. Signed-off-by: Phil Sutter --- extensions/libipt_DNAT.c | 290 ++++++++++++++++++----------------------------- 1 file changed, 111 insertions(+), 179 deletions(-) diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c index e93ab695..2a7b1bc4 100644 --- a/extensions/libipt_DNAT.c +++ b/extensions/libipt_DNAT.c @@ -5,6 +5,7 @@ #include #include /* get_kernel_version */ #include /* INT_MAX in ip_tables.h */ +#include #include #include @@ -42,54 +43,83 @@ static const struct xt_option_entry DNAT_opts[] = { XTOPT_TABLEEND, }; +/* Parses ports */ +static void +parse_ports(const char *arg, bool portok, struct nf_nat_range2 *range) +{ + unsigned int port, maxport, baseport; + char *end = NULL; + + if (!portok) + xtables_error(PARAMETER_PROBLEM, + "Need TCP, UDP, SCTP or DCCP with port specification"); + + range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + + if (!xtables_strtoui(arg, &end, &port, 1, UINT16_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Port `%s' not valid", arg); + + switch (*end) { + case '\0': + range->min_proto.tcp.port + = range->max_proto.tcp.port + = htons(port); + return; + case '-': + arg = end + 1; + break; + case ':': + xtables_error(PARAMETER_PROBLEM, + "Invalid port:port syntax - use dash"); + default: + xtables_error(PARAMETER_PROBLEM, + "Garbage after port value: `%s'", end); + } + + if (!xtables_strtoui(arg, &end, &maxport, 1, UINT16_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Port `%s' not valid", arg); + + if (maxport < port) + /* People are stupid. */ + xtables_error(PARAMETER_PROBLEM, + "Port range `%s' funky", arg); + + range->min_proto.tcp.port = htons(port); + range->max_proto.tcp.port = htons(maxport); + + switch (*end) { + case '\0': + return; + case '/': + arg = end + 1; + break; + default: + xtables_error(PARAMETER_PROBLEM, + "Garbage after port range: `%s'", end); + } + + if (!xtables_strtoui(arg, &end, &baseport, 1, UINT16_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Port `%s' not valid", arg); + + range->flags |= NF_NAT_RANGE_PROTO_OFFSET; + range->base_proto.tcp.port = htons(baseport); +} + /* Ranges expected in network order. */ static void -parse_to(const char *orig_arg, int portok, struct nf_nat_ipv4_range *range) +parse_to(const char *orig_arg, bool portok, struct nf_nat_range2 *range) { - char *arg, *colon, *dash, *error; - const struct in_addr *ip; + char *arg, *colon, *dash; arg = xtables_strdup(orig_arg); colon = strchr(arg, ':'); if (colon) { - int port; - - if (!portok) - xtables_error(PARAMETER_PROBLEM, - "Need TCP, UDP, SCTP or DCCP with port specification"); - - range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + parse_ports(colon + 1, portok, range); - port = atoi(colon+1); - if (port <= 0 || port > 65535) - xtables_error(PARAMETER_PROBLEM, - "Port `%s' not valid\n", colon+1); - - error = strchr(colon+1, ':'); - if (error) - xtables_error(PARAMETER_PROBLEM, - "Invalid port:port syntax - use dash\n"); - - dash = strchr(colon, '-'); - if (!dash) { - range->min.tcp.port - = range->max.tcp.port - = htons(port); - } else { - int maxport; - - maxport = atoi(dash + 1); - if (maxport <= 0 || maxport > 65535) - xtables_error(PARAMETER_PROBLEM, - "Port `%s' not valid\n", dash+1); - if (maxport < port) - /* People are stupid. */ - xtables_error(PARAMETER_PROBLEM, - "Port range `%s' funky\n", colon+1); - range->min.tcp.port = htons(port); - range->max.tcp.port = htons(maxport); - } /* Starts with a colon? No IP info...*/ if (colon == arg) { free(arg); @@ -106,46 +136,57 @@ parse_to(const char *orig_arg, int portok, struct nf_nat_ipv4_range *range) if (dash) *dash = '\0'; - ip = xtables_numeric_to_ipaddr(arg); - if (!ip) - xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", - arg); - range->min_ip = ip->s_addr; + if (!inet_pton(AF_INET, arg, &range->min_addr)) + xtables_error(PARAMETER_PROBLEM, + "Bad IP address \"%s\"\n", arg); if (dash) { - ip = xtables_numeric_to_ipaddr(dash+1); - if (!ip) - xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", - dash+1); - range->max_ip = ip->s_addr; - } else - range->max_ip = range->min_ip; - + if (!inet_pton(AF_INET, dash + 1, &range->max_addr)) + xtables_error(PARAMETER_PROBLEM, + "Bad IP address \"%s\"\n", dash + 1); + } else { + range->max_addr = range->min_addr; + } free(arg); return; } +static void __DNAT_parse(struct xt_option_call *cb, __u16 proto, + struct nf_nat_range2 *range) +{ + bool portok = proto == IPPROTO_TCP || + proto == IPPROTO_UDP || + proto == IPPROTO_SCTP || + proto == IPPROTO_DCCP || + proto == IPPROTO_ICMP; + + xtables_option_parse(cb); + switch (cb->entry->id) { + case O_TO_DEST: + parse_to(cb->arg, portok, range); + break; + case O_PERSISTENT: + range->flags |= NF_NAT_RANGE_PERSISTENT; + break; + } +} + static void DNAT_parse(struct xt_option_call *cb) { struct nf_nat_ipv4_multi_range_compat *mr = (void *)cb->data; const struct ipt_entry *entry = cb->xt_entry; - int portok; + struct nf_nat_range2 range = {}; - if (entry->ip.proto == IPPROTO_TCP - || entry->ip.proto == IPPROTO_UDP - || entry->ip.proto == IPPROTO_SCTP - || entry->ip.proto == IPPROTO_DCCP - || entry->ip.proto == IPPROTO_ICMP) - portok = 1; - else - portok = 0; + __DNAT_parse(cb, entry->ip.proto, &range); - xtables_option_parse(cb); switch (cb->entry->id) { case O_TO_DEST: - parse_to(cb->arg, portok, mr->range); - break; + mr->range->min_ip = range.min_addr.ip; + mr->range->max_ip = range.max_addr.ip; + mr->range->min = range.min_proto; + mr->range->max = range.max_proto; + /* fall through */ case O_PERSISTENT: - mr->range->flags |= NF_NAT_RANGE_PERSISTENT; + mr->range->flags |= range.flags; break; } } @@ -159,6 +200,10 @@ static void DNAT_fcheck(struct xt_fcheck_call *cb) mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM; mr->rangesize = 1; + + if (mr->range[0].flags & NF_NAT_RANGE_PROTO_OFFSET) + xtables_error(PARAMETER_PROBLEM, + "Shifted portmap ranges not supported with this kernel"); } static void print_range(const struct nf_nat_ipv4_range *r) @@ -251,124 +296,11 @@ static int DNAT_xlate(struct xt_xlate *xl, return 1; } -static void -parse_to_v2(const char *orig_arg, int portok, struct nf_nat_range2 *range) -{ - char *arg, *colon, *dash, *error; - const struct in_addr *ip; - - arg = xtables_strdup(orig_arg); - - colon = strchr(arg, ':'); - if (colon) { - int port; - - if (!portok) - xtables_error(PARAMETER_PROBLEM, - "Need TCP, UDP, SCTP or DCCP with port specification"); - - range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; - - port = atoi(colon+1); - if (port <= 0 || port > 65535) - xtables_error(PARAMETER_PROBLEM, - "Port `%s' not valid\n", colon+1); - - error = strchr(colon+1, ':'); - if (error) - xtables_error(PARAMETER_PROBLEM, - "Invalid port:port syntax - use dash\n"); - - dash = strchr(colon, '-'); - if (!dash) { - range->min_proto.tcp.port - = range->max_proto.tcp.port - = htons(port); - } else { - int maxport; - char *slash; - - maxport = atoi(dash + 1); - if (maxport <= 0 || maxport > 65535) - xtables_error(PARAMETER_PROBLEM, - "Port `%s' not valid\n", dash+1); - if (maxport < port) - /* People are stupid. */ - xtables_error(PARAMETER_PROBLEM, - "Port range `%s' funky\n", colon+1); - range->min_proto.tcp.port = htons(port); - range->max_proto.tcp.port = htons(maxport); - - 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 a colon? No IP info...*/ - if (colon == arg) { - free(arg); - return; - } - *colon = '\0'; - } - - range->flags |= NF_NAT_RANGE_MAP_IPS; - dash = strchr(arg, '-'); - if (colon && dash && dash > colon) - dash = NULL; - - if (dash) - *dash = '\0'; - - ip = xtables_numeric_to_ipaddr(arg); - if (!ip) - xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", - arg); - range->min_addr.in = *ip; - if (dash) { - ip = xtables_numeric_to_ipaddr(dash+1); - if (!ip) - xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", - dash+1); - range->max_addr.in = *ip; - } else - range->max_addr = range->min_addr; - - free(arg); - return; -} - static void DNAT_parse_v2(struct xt_option_call *cb) { const struct ipt_entry *entry = cb->xt_entry; - struct nf_nat_range2 *range = cb->data; - int portok; - - if (entry->ip.proto == IPPROTO_TCP - || entry->ip.proto == IPPROTO_UDP - || entry->ip.proto == IPPROTO_SCTP - || entry->ip.proto == IPPROTO_DCCP - || entry->ip.proto == IPPROTO_ICMP) - portok = 1; - else - portok = 0; - xtables_option_parse(cb); - switch (cb->entry->id) { - case O_TO_DEST: - parse_to_v2(cb->arg, portok, range); - break; - case O_PERSISTENT: - range->flags |= NF_NAT_RANGE_PERSISTENT; - break; - } + __DNAT_parse(cb, entry->ip.proto, cb->data); } static void DNAT_fcheck_v2(struct xt_fcheck_call *cb) -- cgit v1.2.3