On Thu, Apr 16, 2026 at 12:04:51AM +0200, Stefano Brivio wrote: > On Fri, 10 Apr 2026 11:03:03 +1000 > David Gibson wrote: > > > The forwarding table now allows for arbitrary port ranges to be marked as > > FWD_SCAN, meaning we don't open sockets for every port, but only those we > > scan as listening on the target side. However, there's currently no way > > to create such rules, except -[tTuU] auto which always scans every port > > with an unspecified listening address and interface. > > > > Allow user-specified "auto" ranges by moving the parsing of the "auto" > > keyword from conf_ports(), to conf_ports_spec() as part of the port > > specified. "auto" can be combined freely with other port ranges, e.g. > > -t 127.0.0.1/auto > > -u %lo/5000-7000,auto > > -T auto,12345 > > -U auto,~1-9000 > > > > Note that any address and interface given only affects where the automatic > > forwards listen, not what addresses we consider when scanning. That is, > > if the target side is listening on *any* address, we will create a forward > > on the specified address. > > > > Link: https://bugs.passt.top/show_bug.cgi?id=180 > > > > Signed-off-by: David Gibson > > --- > > conf.c | 85 ++++++++++++++++++++++++++++++++++++++++++--------------- > > passt.1 | 30 ++++++++++++++------ > > 2 files changed, 85 insertions(+), 30 deletions(-) > > > > diff --git a/conf.c b/conf.c > > index f62109b5..8e3b4b20 100644 > > --- a/conf.c > > +++ b/conf.c > > @@ -13,6 +13,7 @@ > > */ > > > > #include > > +#include > > #include > > #include > > #include > > @@ -112,6 +113,28 @@ static int parse_port_range(const char *s, const char **endptr, > > return 0; > > } > > > > +/** > > + * parse_keyword() - Parse a literal keyword > > + * @s: String to parse > > + * @endptr: Update to the character after the keyword > > + * @kw: Keyword to accept > > + * > > + * Return: 0, if @s starts with @kw, -EINVAL if it does not > > + */ > > +static int parse_keyword(const char *s, const char **endptr, const char *kw) > > +{ > > + size_t len = strlen(kw); > > + > > + if (strlen(s) < len) > > + return -EINVAL; > > + > > + if (memcmp(s, kw, len)) > > + return -EINVAL; > > + > > + *endptr = s + len; > > + return 0; > > +} > > + > > /** > > * conf_ports_range_except() - Set up forwarding for a range of ports minus a > > * bitmap of exclusions > > @@ -249,6 +272,7 @@ static void conf_ports_spec(const struct ctx *c, > > uint8_t exclude[PORT_BITMAP_SIZE] = { 0 }; > > bool exclude_only = true; > > const char *p, *ep; > > + uint8_t flags = 0; > > unsigned i; > > > > if (!strcmp(spec, "all")) { > > @@ -256,15 +280,32 @@ static void conf_ports_spec(const struct ctx *c, > > spec = ""; > > } > > > > - /* Mark all exclusions first, they might be given after base ranges */ > > + /* Parse excluded ranges and "auto" in the first pass */ > > for_each_chunk(p, ep, spec, ",") { > > struct port_range xrange; > > > > - if (*p != '~') { > > - /* Not an exclude range, parse later */ > > + if (isdigit(*p)) { > > + /* Include range, parse later */ > > exclude_only = false; > > continue; > > } > > + > > + if (parse_keyword(p, &p, "auto") == 0) { > > + if (p != ep) /* Garbage after the keyword */ > > + goto bad; > > + > > + if (c->mode != MODE_PASTA) { > > + die( > > +"'auto' port forwarding is only allowed for pasta"); > > + } > > + > > + flags |= FWD_SCAN; > > + continue; > > + } > > + > > + /* Should be an exclude range */ > > + if (*p != '~') > > + goto bad; > > p++; > > > > if (parse_port_range(p, &p, &xrange)) > > @@ -283,7 +324,7 @@ static void conf_ports_spec(const struct ctx *c, > > conf_ports_range_except(c, optname, optarg, fwd, > > proto, addr, ifname, > > 1, NUM_PORTS - 1, exclude, > > - 1, FWD_WEAK); > > + 1, flags | FWD_WEAK); > > return; > > } > > > > @@ -291,8 +332,8 @@ static void conf_ports_spec(const struct ctx *c, > > for_each_chunk(p, ep, spec, ",") { > > struct port_range orig_range, mapped_range; > > > > - if (*p == '~') > > - /* Exclude range, already parsed */ > > + if (!isdigit(*p)) > > + /* Already parsed */ > > continue; > > > > if (parse_port_range(p, &p, &orig_range)) > > @@ -320,7 +361,7 @@ static void conf_ports_spec(const struct ctx *c, > > proto, addr, ifname, > > orig_range.first, orig_range.last, > > exclude, > > - mapped_range.first, 0); > > + mapped_range.first, flags); > > } > > > > return; > > @@ -366,17 +407,6 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, > > if (proto == IPPROTO_UDP && c->no_udp) > > die("UDP port forwarding requested but UDP is disabled"); > > > > - if (!strcmp(optarg, "auto")) { > > - if (c->mode != MODE_PASTA) > > - die("'auto' port forwarding is only allowed for pasta"); > > - > > - conf_ports_range_except(c, optname, optarg, fwd, > > - proto, NULL, NULL, > > - 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN); > > - > > - return; > > - } > > - > > strncpy(buf, optarg, sizeof(buf) - 1); > > > > if ((spec = strchr(buf, '/'))) { > > @@ -1031,13 +1061,13 @@ static void usage(const char *name, FILE *f, int status) > > " can be specified multiple times\n" > > " SPEC can be:\n" > > " 'none': don't forward any ports\n" > > - "%s" > > " [ADDR[%%IFACE]/]PORTS: forward specific ports\n" > > " PORTS is either 'all' (forward all unbound, non-ephemeral\n" > > " ports), or a comma-separated list of ports, optionally\n" > > " ranged with '-' and optional target ports after ':'.\n" > > " Ranges can be reduced by excluding ports or ranges\n" > > - " prefixed by '~'\n" > > + " prefixed by '~'.\n" > > + "%s" > > " Examples:\n" > > " -t all Forward all ports\n" > > " -t 127.0.0.1/all Forward all ports from local address\n" > > @@ -1051,15 +1081,26 @@ static void usage(const char *name, FILE *f, int status) > > " -t 192.0.2.1/5 Bind port 5 of 192.0.2.1 to %s\n" > > " -t 5-25,~10-20 Forward ports 5 to 9, and 21 to 25\n" > > " -t ~25 Forward all ports except for 25\n" > > + "%s" > > " default: %s\n" > > " -u, --udp-ports SPEC UDP port forwarding to %s\n" > > " SPEC is as described for TCP above\n" > > " default: %s\n", > > guest, > > strstr(name, "pasta") ? > > - " 'auto': forward all ports currently bound in namespace\n" > > + " The 'auto' keyword may be given to only forward\n" > > + " ports which are bound in the target namespace\n" > > + : "", > > + guest, guest, guest, > > + strstr(name, "pasta") ? > > + " -t auto Forward all ports bound in namespace\n" > > + " -t 192.0.2.2/auto Forward ports from 192.0.2.2 if\n" > > + " they are bound in the namespace\n" > > + " -t 8000-8010,auto Forward ports 8000-8010 if they\n" > > + " are bound in the namespace\n" > > The whole thing is now rendered as: > > Examples: > -t all Forward all ports > -t 127.0.0.1/all Forward all ports from local address > 127.0.0.1 > -t 22 Forward local port 22 to 22 on namespace > -t 22:23 Forward local port 22 to 23 on namespace > -t 22,25 Forward ports 22, 25 to ports 22, 25 > -t 22-80 Forward ports 22 to 80 > -t 22-80:32-90 Forward ports 22 to 80 to > corresponding port numbers plus 10 > -t 192.0.2.1/5 Bind port 5 of 192.0.2.1 to namespace > -t 5-25,~10-20 Forward ports 5 to 9, and 21 to 25 > -t ~25 Forward all ports except for 25 > -t auto Forward all ports bound in namespace > -t 192.0.2.2/auto Forward ports from 192.0.2.2 if > they are bound in the namespace > -t 8000-8010,auto Forward ports 8000-8010 if they > are bound in the namespace > > I think this could be: > > " -t auto\t Forward all ports bound in namespace\n" > " -t ::1/auto Forward ports from ::1 if bound in\n" > " namespace\n" > " -t 80-82,auto Forward ports 80-82 if bound in\n" > " namespace\n" > > instead. Good ideas, done. -- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson