From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: passt.top; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=cCjauOEd; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by passt.top (Postfix) with ESMTPS id CD14F5A0265 for ; Thu, 16 Apr 2026 00:04:56 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1776290695; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=7ec9PX/A0rgTb1wvHQs7eVm3yHfQivqR2HNCvegjU0w=; b=cCjauOEdIuhXYvolbWqC+FcAuGJ1k5Dq8BQJBvhnLaUlQZlBZ6kYkDU7tWEUnplkq6OOmA Q5egQkqYWcrRtkDt1YdY6BRV9zOn1iGWCrwhiU1pqQZE+6VDqHjqH8p7myxJ9bphxdNzX5 LbGBBKSudcOmnQXkCK/nTmcXxiiPPdc= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-262-N62mRxsmP1uwdV8MfnAsgw-1; Wed, 15 Apr 2026 18:04:54 -0400 X-MC-Unique: N62mRxsmP1uwdV8MfnAsgw-1 X-Mimecast-MFC-AGG-ID: N62mRxsmP1uwdV8MfnAsgw_1776290693 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-4839fc4cef6so274345e9.0 for ; Wed, 15 Apr 2026 15:04:54 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776290692; x=1776895492; h=date:content-transfer-encoding:mime-version:organization:references :in-reply-to:message-id:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=7ec9PX/A0rgTb1wvHQs7eVm3yHfQivqR2HNCvegjU0w=; b=s4U7+vvdeTPGGzCwG7OqWMoqd0jUgJgqywq4WsC8DQhP4jbradyYFtblJpItEUljk0 ifBvyiNRva+L8NEhT06rb4NjMJF4SyPyqcvcZEyIYUPJ8gSpFRb6kyfm/nh4JhrJDJkv Uq/hdJNGgGBhe71j6NurngF4z5/NMHtNjBI0ypQMr7r2yyHDMR9ZycvEH0y5q1A3jN2K jEjVvJE67dmHJ+GRZRfwz1nyjV/wRtC5UY1WJwbVXe1Il40+ETvsdvA3ddQ11WVilCr1 fEu7FJYULey4FXzfv3kRgoAN85MSfCqd3INT3bk8RmKCGWQ6qk/geWT0U5ABfoVis7Uc O2aw== X-Gm-Message-State: AOJu0YziTAl1Y0g4RgW2k4EJb52opWRr/8dEgEM7RQF7SkWjZrTEnQM+ oUf+EAx1tY5eEqV5XCmn1zc7wT7cbSNnzjc+i5ye6eE5lP6p6dRMU1Uj5jPKxApOmXVZQ/uvO7v V1l6HKh8p9TkGbr8iiFLMTMX7YaJnrFVBy0aE02T4/nrx3o3McKBX6SecdHJJRw== X-Gm-Gg: AeBDietMYdG7kVkIMteF5jeiQxrim8gD7CH6+mj4oFuInHt2QDz8Tx163dIErP3H2PV +WdvFKR0fsLVHiY7zqX2cbjqrvU1HDshpUPgpztJaaus9zpGmzEDvmdrJdb9N1+s42zjsDwtadR oJvR3kiXR4m2cBPgx6BU8i/S+dMRwDuPPmBCPZ5fTfySVb1ROeAvuRFyx4P0tw6cwzGIlV90Eeg Rw4xQ/PllJ6o8GZdt6WoF2QtFvXfeBaFM75B711CEE8p5ZlOkg8SOMfDBtY/Mo9sJMDLFGYAyUe DjEPYvJJPzOx/8qysQnXPFo+ifzY9FTzZ9+jmMx15uHW2rRF96b2uMrHrX3Wh3a0V7lhr6f9ZbS Mf3bGVrtq8Z+ymMj6hddW+U6PHY1oPNnJ X-Received: by 2002:a05:600c:6296:b0:485:3e00:944a with SMTP id 5b1f17b1804b1-488f47f2935mr15046615e9.9.1776290692412; Wed, 15 Apr 2026 15:04:52 -0700 (PDT) X-Received: by 2002:a05:600c:6296:b0:485:3e00:944a with SMTP id 5b1f17b1804b1-488f47f2935mr15046305e9.9.1776290691814; Wed, 15 Apr 2026 15:04:51 -0700 (PDT) Received: from maya.myfinge.rs (ifcgrfdd.trafficplex.cloud. [2a10:fc81:a806:d6a9::1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-488f582368dsm1211055e9.11.2026.04.15.15.04.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Apr 2026 15:04:51 -0700 (PDT) From: Stefano Brivio To: David Gibson Subject: Re: [PATCH v2 17/23] conf: Allow user-specified auto-scanned port forwarding ranges Message-ID: <20260416000450.56189eb6@elisabeth> In-Reply-To: <20260410010309.736855-18-david@gibson.dropbear.id.au> References: <20260410010309.736855-1-david@gibson.dropbear.id.au> <20260410010309.736855-18-david@gibson.dropbear.id.au> Organization: Red Hat X-Mailer: Claws Mail 4.2.0 (GTK 3.24.49; x86_64-pc-linux-gnu) MIME-Version: 1.0 Date: Thu, 16 Apr 2026 00:04:51 +0200 (CEST) X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: ReCDiDnlyvnWNxnfzBpU1Ni_h2xKRovYpfOeHYEg1LA_1776290693 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Message-ID-Hash: UDYGZ4R266HIMEWY45HOVM7556QTTYB7 X-Message-ID-Hash: UDYGZ4R266HIMEWY45HOVM7556QTTYB7 X-MailFrom: sbrivio@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: passt-dev@passt.top X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: 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. > : "", > - guest, guest, guest, fwd_default, guest, fwd_default); > + > + fwd_default, guest, fwd_default); > > if (strstr(name, "pasta")) > goto pasta_opts; > diff --git a/passt.1 b/passt.1 > index 3ba447d5..eeecc0fb 100644 > --- a/passt.1 > +++ b/passt.1 > @@ -434,12 +434,6 @@ Configure TCP port forwarding to guest or namespace. \fIspec\fR can be one of: > .BR none > Don't forward any ports > > -.TP > -.BR auto " " (\fBpasta\fR " " only) > -Dynamically forward ports bound in the namespace. The list of ports is > -periodically derived (every second) from listening sockets reported by > -\fI/proc/net/tcp\fR and \fI/proc/net/tcp6\fR, see \fBproc\fR(5). > - > .TP > [\fIaddress\fR[\fB%\fR\fIinterface\fR]\fB/\fR]\fIports\fR ... > Specific ports to forward. Optionally, a specific listening address > @@ -468,11 +462,20 @@ as \fIfirst\fR. > \fB~\fR\fIfirst\fR[\fB-\fR\fIlast\fR] > Exclude range. Exclude port numbers between \fIfirst\fR and > \fIlast\fR from. This takes precedences over include ranges. > + > +.TP > +.BR auto > +(\fBpasta\fR " " only). Only forward ports in the specified set if > +the target ports are bound in the namespace. The list of ports is > +periodically derived (every second) from listening sockets reported by > +\fI/proc/net/tcp\fR and \fI/proc/net/tcp6\fR, see \fBproc\fR(5). > .RE > > Specifying excluded ranges only implies that all other non-ephemeral > -ports are forwarded. In this case, no failures are reported for > -unavailable ports, unless no ports could be forwarded at all. > +ports are forwarded. Specifying no ranges at all implies forwarding > +all non-ephemeral ports permitted by current capabilities. In this > +case, no failures are reported for unavailable ports, unless no ports > +could be forwarded at all. > > Examples: > .RS > @@ -519,6 +522,17 @@ and 30 > .TP > -t ~20000-20010 > Forward all ports to the guest, except for the range from 20000 to 20010 > +.TP > +-t auto > +Automatically forward any ports which are bound in the namespace. > +.TP > +-t 192.0.2.2/auto > +Automatically forward any ports which are bound in the namespace, > +listening only on local port 192.0.2.2. > +.TP > +-t 8000-8010,auto > +Forward ports in the range 8000-8010 if and only if they are bound in > +the namespace. > .RE > > Default is \fBnone\fR for \fBpasst\fR and \fBauto\fR for \fBpasta\fR. -- Stefano