diff --git a/conf.c b/conf.c index cff71ff..14356b7 100644 --- a/conf.c +++ b/conf.c @@ -124,12 +124,12 @@ enum conf_port_type { static int conf_ports(struct ctx *c, char optname, const char *optarg, enum conf_port_type *set) { - int start_src = -1, end_src = -1, start_dst = -1, end_dst = -1; + int start_src, end_src, start_dst, end_dst, exclude_only = 1, i, port; + char addr_buf[sizeof(struct in6_addr)] = { 0 }, *addr = addr_buf; void (*remap)(in_port_t port, in_port_t delta); - char addr_buf[sizeof(struct in6_addr)] = { 0 }; + uint8_t *map, exclude[USHRT_MAX / 8] = { 0 }; + char buf[BUFSIZ], *sep, *spec, *p; sa_family_t af = AF_UNSPEC; - char buf[BUFSIZ], *sep, *p, *addr = addr_buf; - uint8_t *map; if (optname == 't') { map = c->tcp.port_to_tap; @@ -186,9 +186,9 @@ static int conf_ports(struct ctx *c, char optname, const char *optarg, strncpy(buf, optarg, sizeof(buf) - 1); - if ((p = strchr(buf, '/'))) { - *p = 0; - p++; + if ((spec = strchr(buf, '/'))) { + *spec = 0; + spec++; if (optname != 't' && optname != 'u') goto bad; @@ -200,16 +200,97 @@ static int conf_ports(struct ctx *c, char optname, const char *optarg, else goto bad; } else { - p = buf; + spec = buf; addr = NULL; } - if (strspn(p, "0123456789-,:") != strlen(p)) + if (strspn(spec, "0123456789-,:~") != strlen(spec)) goto bad; + /* Mark all exclusions first, they might be given after base ranges */ + p = spec; + start_src = end_src = -1; do { - int i, port; + while (*p != '~' && start_src == -1) { + exclude_only = 0; + + if (!(p = strchr(p, ','))) + break; + + p++; + } + if (!p || !*p) + break; + + if (*p == '~') + p++; + + errno = 0; + port = strtol(p, &sep, 10); + if (sep == p) + break; + + if (port < 0 || port > USHRT_MAX || errno) + goto bad; + + switch (*sep) { + case '-': + if (start_src == -1) /* ~22-... */ + start_src = port; + break; + case ',': + case 0: + if (start_src == -1) /* ~80 */ + start_src = end_src = port; + else if (end_src == -1) /* ~22-25 */ + end_src = port; + else + goto bad; + + if (start_src > end_src) /* ~80-22 */ + goto bad; + + for (i = start_src; i <= end_src; i++) { + if (bitmap_isset(exclude, i)) + goto overlap; + + bitmap_set(exclude, i); + } + break; + default: + goto bad; + } + p = sep + 1; + } while (*sep); + + if (exclude_only) { + for (i = 0; i < PORT_EPHEMERAL_MIN; i++) { + if (bitmap_isset(exclude, i)) + continue; + + bitmap_set(map, i); + + if (optname == 't') + tcp_sock_init(c, 0, af, addr, i); + else if (optname == 'u') + udp_sock_init(c, 0, af, addr, i); + } + + return 0; + } + + /* Now process base ranges, skipping exclusions */ + start_src = end_src = start_dst = end_dst = -1; + p = spec; + do { + while (*p == '~') { + if (!(p = strchr(p, ','))) + break; + p++; + } + if (!p || !*p) + break; errno = 0; port = strtol(p, &sep, 10); @@ -281,6 +362,9 @@ static int conf_ports(struct ctx *c, char optname, const char *optarg, if (bitmap_isset(map, i)) goto overlap; + if (bitmap_isset(exclude, i)) + continue; + bitmap_set(map, i); if (start_dst != -1) { @@ -726,7 +810,9 @@ static void usage(const char *name) info( " 'all': forward all unbound, non-ephemeral ports"); info( " a comma-separated list, optionally ranged with '-'"); info( " and optional target ports after ':', with optional"); - info( " address specification suffixed by '/'. Examples:"); + info( " address specification suffixed by '/'. Ranges can be"); + info( " reduced by excluding ports or ranges prefixed by '~'"); + info( " Examples:"); info( " -t 22 Forward local port 22 to 22 on guest"); info( " -t 22:23 Forward local port 22 to 23 on guest"); info( " -t 22,25 Forward ports 22, 25 to ports 22, 25"); @@ -734,6 +820,8 @@ static void usage(const char *name) info( " -t 22-80:32-90 Forward ports 22 to 80 to"); info( " corresponding port numbers plus 10"); info( " -t 192.0.2.1/5 Bind port 5 of 192.0.2.1 to guest"); + info( " -t 5-25,~10-20 Forward ports 5 to 9, and 21 to 25"); + info( " -t ~25 Forward all ports except for 25"); info( " default: none"); info( " -u, --udp-ports SPEC UDP port forwarding to guest"); info( " SPEC is as described for TCP above"); @@ -757,6 +845,8 @@ pasta_opts: info( " -t 22-80:32-90 Forward ports 22 to 80 to"); info( " corresponding port numbers plus 10"); info( " -t 192.0.2.1/5 Bind port 5 of 192.0.2.1 to namespace"); + info( " -t 5-25,~10-20 Forward ports 5 to 9, and 21 to 25"); + info( " -t ~25 Forward all bound ports except for 25"); info( " default: auto"); info( " IPv6 bound ports are also forwarded for IPv4"); info( " -u, --udp-ports SPEC UDP port forwarding to namespace"); diff --git a/passt.1 b/passt.1 index c7c43be..4e06c0c 100644 --- a/passt.1 +++ b/passt.1 @@ -306,7 +306,10 @@ For low (< 1024) ports, see \fBNOTES\fR. .BR ports A comma-separated list of ports, optionally ranged with \fI-\fR, and, optionally, with target ports after \fI:\fR, if they differ. Specific addresses -can be bound as well, separated by \fI/\fR. Examples: +can be bound as well, separated by \fI/\fR. Within given ranges, selected ports +and ranges can be excluded by an additional specification prefixed by \fI~\fR. +Specifying excluded ranges only implies that all other ports are forwarded. +Examples: .RS .TP -t 22 @@ -326,6 +329,15 @@ Forward local ports 22 to 80 to corresponding ports on the guest plus 10 .TP -t 192.0.2.1/22 Forward local port 22, bound to 192.0.2.1, to port 22 on the guest +.TP +-t 2000-5000,~3000-3010 +Forward local ports 2000 to 5000, but not 3000 to 3010 +.TP +-t 192.0.2.1/20-30,~25 +Forward local ports 20 to 24, and 26 to 30, bound to 192.0.2.1 +.TP +-t ~20000-20010 +Forward all ports to the guest, except for the range from 20000 to 20010 .RE Default is \fBnone\fR. @@ -368,7 +380,10 @@ periodically derived (every second) from listening sockets reported by .BR ports A comma-separated list of ports, optionally ranged with \fI-\fR, and, optionally, with target ports after \fI:\fR, if they differ. Specific addresses -can be bound as well, separated by \fI/\fR. Examples: +can be bound as well, separated by \fI/\fR. Within given ranges, selected ports +and ranges can be excluded by an additional specification prefixed by \fI~\fR. +Specifying excluded ranges only implies that all other ports are forwarded. +Examples: .RS .TP -t 22 @@ -389,6 +404,15 @@ namespace .TP -t 192.0.2.1/22 Forward local port 22, bound to 192.0.2.1, to port 22 in the target namespace +.TP +-t 2000-5000,~3000-3010 +Forward local ports 2000 to 5000, but not 3000 to 3010 +.TP +-t 192.0.2.1/20-30,~25 +Forward local ports 20 to 24, and 26 to 30, bound to 192.0.2.1 +.TP +-t ~20000-20010 +Forward all ports to the namespace, except for the range from 20000 to 20010 .RE IPv6 bound ports are also forwarded for IPv4.