* [PATCH v2] conf: Unify several paths in conf_ports()
@ 2025-03-12 3:43 David Gibson
2025-03-14 23:50 ` Stefano Brivio
0 siblings, 1 reply; 4+ messages in thread
From: David Gibson @ 2025-03-12 3:43 UTC (permalink / raw)
To: passt-dev, Stefano Brivio; +Cc: David Gibson
In conf_ports() we have three different paths which actually do the setup
of an individual forwarded port: one for the "all" case, one for the
exclusions only case and one for the range of ports with possible
exclusions case.
We can unify those cases using a new helper which handles a single range
of ports, with a bitmap of exclusions. Although this is slightly longer
(largely due to the new helpers function comment), it reduces duplicated
logic. It will also make future improvements to the tracking of port
forwards easier.
The new conf_ports_range_except() function has a pretty prodigious
parameter list, but I still think it's an overall improvement in conceptual
complexity.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 173 ++++++++++++++++++++++++++++++---------------------------
1 file changed, 90 insertions(+), 83 deletions(-)
v2:
* Commit message updated slightly, but otherwise unmodified.
diff --git a/conf.c b/conf.c
index 065e7201..4e0099ba 100644
--- a/conf.c
+++ b/conf.c
@@ -123,6 +123,75 @@ static int parse_port_range(const char *s, char **endptr,
return 0;
}
+/**
+ * conf_ports_range_except() - Set up forwarding for a range of ports minus a
+ * bitmap of exclusions
+ * @c: Execution context
+ * @optname: Short option name, t, T, u, or U
+ * @optarg: Option argument (port specification)
+ * @fwd: Pointer to @fwd_ports to be updated
+ * @addr: Listening address
+ * @ifname: Listening interface
+ * @first: First port to forward
+ * @last: Last port to forward
+ * @exclude: Bitmap of ports to exclude
+ * @to: Port to translate @first to when forwarding
+ * @weak: Ignore errors, as long as at least one port is mapped
+ */
+static void conf_ports_range_except(const struct ctx *c, char optname,
+ const char *optarg, struct fwd_ports *fwd,
+ const union inany_addr *addr,
+ const char *ifname,
+ uint16_t first, uint16_t last,
+ const uint8_t *exclude, uint16_t to,
+ bool weak)
+{
+ bool bound_one = false;
+ unsigned i;
+ int ret;
+
+ if (first == 0) {
+ die("Can't forward port 0 for option '-%c %s'",
+ optname, optarg);
+ }
+
+ for (i = first; i <= last; i++) {
+ if (bitmap_isset(exclude, i))
+ continue;
+
+ if (bitmap_isset(fwd->map, i)) {
+ warn(
+"Altering mapping of already mapped port number: %s", optarg);
+ }
+
+ bitmap_set(fwd->map, i);
+ fwd->delta[i] = to - first;
+
+ if (optname == 't')
+ ret = tcp_sock_init(c, addr, ifname, i);
+ else if (optname == 'u')
+ ret = udp_sock_init(c, 0, addr, ifname, i);
+ else
+ /* No way to check in advance for -T and -U */
+ ret = 0;
+
+ if (ret == -ENFILE || ret == -EMFILE) {
+ die("Can't open enough sockets for port specifier: %s",
+ optarg);
+ }
+
+ if (!ret) {
+ bound_one = true;
+ } else if (!weak) {
+ die("Failed to bind port %u (%s) for option '-%c %s'",
+ i, strerror_(-ret), optname, optarg);
+ }
+ }
+
+ if (!bound_one)
+ die("Failed to bind any port for '-%c %s'", optname, optarg);
+}
+
/**
* conf_ports() - Parse port configuration options, initialise UDP/TCP sockets
* @c: Execution context
@@ -135,10 +204,9 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
{
union inany_addr addr_buf = inany_any6, *addr = &addr_buf;
char buf[BUFSIZ], *spec, *ifname = NULL, *p;
- bool exclude_only = true, bound_one = false;
uint8_t exclude[PORT_BITMAP_SIZE] = { 0 };
+ bool exclude_only = true;
unsigned i;
- int ret;
if (!strcmp(optarg, "none")) {
if (fwd->mode)
@@ -173,32 +241,15 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
fwd->mode = FWD_ALL;
- /* Skip port 0. It has special meaning for many socket APIs, so
- * trying to bind it is not really safe.
- */
- for (i = 1; i < NUM_PORTS; i++) {
+ /* Exclude ephemeral ports */
+ for (i = 0; i < NUM_PORTS; i++)
if (fwd_port_is_ephemeral(i))
- continue;
-
- bitmap_set(fwd->map, i);
- if (optname == 't') {
- ret = tcp_sock_init(c, NULL, NULL, i);
- if (ret == -ENFILE || ret == -EMFILE)
- goto enfile;
- if (!ret)
- bound_one = true;
- } else if (optname == 'u') {
- ret = udp_sock_init(c, 0, NULL, NULL, i);
- if (ret == -ENFILE || ret == -EMFILE)
- goto enfile;
- if (!ret)
- bound_one = true;
- }
- }
-
- if (!bound_one)
- goto bind_all_fail;
+ bitmap_set(exclude, i);
+ conf_ports_range_except(c, optname, optarg, fwd,
+ NULL, NULL,
+ 1, NUM_PORTS - 1, exclude,
+ 1, true);
return;
}
@@ -275,37 +326,15 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
} while ((p = next_chunk(p, ',')));
if (exclude_only) {
- /* Skip port 0. It has special meaning for many socket APIs, so
- * trying to bind it is not really safe.
- */
- for (i = 1; i < NUM_PORTS; i++) {
- if (fwd_port_is_ephemeral(i) ||
- bitmap_isset(exclude, i))
- continue;
-
- bitmap_set(fwd->map, i);
-
- if (optname == 't') {
- ret = tcp_sock_init(c, addr, ifname, i);
- if (ret == -ENFILE || ret == -EMFILE)
- goto enfile;
- if (!ret)
- bound_one = true;
- } else if (optname == 'u') {
- ret = udp_sock_init(c, 0, addr, ifname, i);
- if (ret == -ENFILE || ret == -EMFILE)
- goto enfile;
- if (!ret)
- bound_one = true;
- } else {
- /* No way to check in advance for -T and -U */
- bound_one = true;
- }
- }
-
- if (!bound_one)
- goto bind_all_fail;
+ /* Exclude ephemeral ports */
+ for (i = 0; i < NUM_PORTS; i++)
+ if (fwd_port_is_ephemeral(i))
+ bitmap_set(exclude, i);
+ conf_ports_range_except(c, optname, optarg, fwd,
+ addr, ifname,
+ 1, NUM_PORTS - 1, exclude,
+ 1, true);
return;
}
@@ -334,40 +363,18 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
if ((*p != '\0') && (*p != ',')) /* Garbage after the ranges */
goto bad;
- for (i = orig_range.first; i <= orig_range.last; i++) {
- if (bitmap_isset(fwd->map, i))
- warn(
-"Altering mapping of already mapped port number: %s", optarg);
-
- if (bitmap_isset(exclude, i))
- continue;
-
- bitmap_set(fwd->map, i);
-
- fwd->delta[i] = mapped_range.first - orig_range.first;
-
- ret = 0;
- if (optname == 't')
- ret = tcp_sock_init(c, addr, ifname, i);
- else if (optname == 'u')
- ret = udp_sock_init(c, 0, addr, ifname, i);
- if (ret)
- goto bind_fail;
- }
+ conf_ports_range_except(c, optname, optarg, fwd,
+ addr, ifname,
+ orig_range.first, orig_range.last,
+ exclude,
+ mapped_range.first, false);
} while ((p = next_chunk(p, ',')));
return;
-enfile:
- die("Can't open enough sockets for port specifier: %s", optarg);
bad:
die("Invalid port specifier %s", optarg);
mode_conflict:
die("Port forwarding mode '%s' conflicts with previous mode", optarg);
-bind_fail:
- die("Failed to bind port %u (%s) for option '-%c %s', exiting",
- i, strerror_(-ret), optname, optarg);
-bind_all_fail:
- die("Failed to bind any port for '-%c %s', exiting", optname, optarg);
}
/**
--
@@ -123,6 +123,75 @@ static int parse_port_range(const char *s, char **endptr,
return 0;
}
+/**
+ * conf_ports_range_except() - Set up forwarding for a range of ports minus a
+ * bitmap of exclusions
+ * @c: Execution context
+ * @optname: Short option name, t, T, u, or U
+ * @optarg: Option argument (port specification)
+ * @fwd: Pointer to @fwd_ports to be updated
+ * @addr: Listening address
+ * @ifname: Listening interface
+ * @first: First port to forward
+ * @last: Last port to forward
+ * @exclude: Bitmap of ports to exclude
+ * @to: Port to translate @first to when forwarding
+ * @weak: Ignore errors, as long as at least one port is mapped
+ */
+static void conf_ports_range_except(const struct ctx *c, char optname,
+ const char *optarg, struct fwd_ports *fwd,
+ const union inany_addr *addr,
+ const char *ifname,
+ uint16_t first, uint16_t last,
+ const uint8_t *exclude, uint16_t to,
+ bool weak)
+{
+ bool bound_one = false;
+ unsigned i;
+ int ret;
+
+ if (first == 0) {
+ die("Can't forward port 0 for option '-%c %s'",
+ optname, optarg);
+ }
+
+ for (i = first; i <= last; i++) {
+ if (bitmap_isset(exclude, i))
+ continue;
+
+ if (bitmap_isset(fwd->map, i)) {
+ warn(
+"Altering mapping of already mapped port number: %s", optarg);
+ }
+
+ bitmap_set(fwd->map, i);
+ fwd->delta[i] = to - first;
+
+ if (optname == 't')
+ ret = tcp_sock_init(c, addr, ifname, i);
+ else if (optname == 'u')
+ ret = udp_sock_init(c, 0, addr, ifname, i);
+ else
+ /* No way to check in advance for -T and -U */
+ ret = 0;
+
+ if (ret == -ENFILE || ret == -EMFILE) {
+ die("Can't open enough sockets for port specifier: %s",
+ optarg);
+ }
+
+ if (!ret) {
+ bound_one = true;
+ } else if (!weak) {
+ die("Failed to bind port %u (%s) for option '-%c %s'",
+ i, strerror_(-ret), optname, optarg);
+ }
+ }
+
+ if (!bound_one)
+ die("Failed to bind any port for '-%c %s'", optname, optarg);
+}
+
/**
* conf_ports() - Parse port configuration options, initialise UDP/TCP sockets
* @c: Execution context
@@ -135,10 +204,9 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
{
union inany_addr addr_buf = inany_any6, *addr = &addr_buf;
char buf[BUFSIZ], *spec, *ifname = NULL, *p;
- bool exclude_only = true, bound_one = false;
uint8_t exclude[PORT_BITMAP_SIZE] = { 0 };
+ bool exclude_only = true;
unsigned i;
- int ret;
if (!strcmp(optarg, "none")) {
if (fwd->mode)
@@ -173,32 +241,15 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
fwd->mode = FWD_ALL;
- /* Skip port 0. It has special meaning for many socket APIs, so
- * trying to bind it is not really safe.
- */
- for (i = 1; i < NUM_PORTS; i++) {
+ /* Exclude ephemeral ports */
+ for (i = 0; i < NUM_PORTS; i++)
if (fwd_port_is_ephemeral(i))
- continue;
-
- bitmap_set(fwd->map, i);
- if (optname == 't') {
- ret = tcp_sock_init(c, NULL, NULL, i);
- if (ret == -ENFILE || ret == -EMFILE)
- goto enfile;
- if (!ret)
- bound_one = true;
- } else if (optname == 'u') {
- ret = udp_sock_init(c, 0, NULL, NULL, i);
- if (ret == -ENFILE || ret == -EMFILE)
- goto enfile;
- if (!ret)
- bound_one = true;
- }
- }
-
- if (!bound_one)
- goto bind_all_fail;
+ bitmap_set(exclude, i);
+ conf_ports_range_except(c, optname, optarg, fwd,
+ NULL, NULL,
+ 1, NUM_PORTS - 1, exclude,
+ 1, true);
return;
}
@@ -275,37 +326,15 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
} while ((p = next_chunk(p, ',')));
if (exclude_only) {
- /* Skip port 0. It has special meaning for many socket APIs, so
- * trying to bind it is not really safe.
- */
- for (i = 1; i < NUM_PORTS; i++) {
- if (fwd_port_is_ephemeral(i) ||
- bitmap_isset(exclude, i))
- continue;
-
- bitmap_set(fwd->map, i);
-
- if (optname == 't') {
- ret = tcp_sock_init(c, addr, ifname, i);
- if (ret == -ENFILE || ret == -EMFILE)
- goto enfile;
- if (!ret)
- bound_one = true;
- } else if (optname == 'u') {
- ret = udp_sock_init(c, 0, addr, ifname, i);
- if (ret == -ENFILE || ret == -EMFILE)
- goto enfile;
- if (!ret)
- bound_one = true;
- } else {
- /* No way to check in advance for -T and -U */
- bound_one = true;
- }
- }
-
- if (!bound_one)
- goto bind_all_fail;
+ /* Exclude ephemeral ports */
+ for (i = 0; i < NUM_PORTS; i++)
+ if (fwd_port_is_ephemeral(i))
+ bitmap_set(exclude, i);
+ conf_ports_range_except(c, optname, optarg, fwd,
+ addr, ifname,
+ 1, NUM_PORTS - 1, exclude,
+ 1, true);
return;
}
@@ -334,40 +363,18 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
if ((*p != '\0') && (*p != ',')) /* Garbage after the ranges */
goto bad;
- for (i = orig_range.first; i <= orig_range.last; i++) {
- if (bitmap_isset(fwd->map, i))
- warn(
-"Altering mapping of already mapped port number: %s", optarg);
-
- if (bitmap_isset(exclude, i))
- continue;
-
- bitmap_set(fwd->map, i);
-
- fwd->delta[i] = mapped_range.first - orig_range.first;
-
- ret = 0;
- if (optname == 't')
- ret = tcp_sock_init(c, addr, ifname, i);
- else if (optname == 'u')
- ret = udp_sock_init(c, 0, addr, ifname, i);
- if (ret)
- goto bind_fail;
- }
+ conf_ports_range_except(c, optname, optarg, fwd,
+ addr, ifname,
+ orig_range.first, orig_range.last,
+ exclude,
+ mapped_range.first, false);
} while ((p = next_chunk(p, ',')));
return;
-enfile:
- die("Can't open enough sockets for port specifier: %s", optarg);
bad:
die("Invalid port specifier %s", optarg);
mode_conflict:
die("Port forwarding mode '%s' conflicts with previous mode", optarg);
-bind_fail:
- die("Failed to bind port %u (%s) for option '-%c %s', exiting",
- i, strerror_(-ret), optname, optarg);
-bind_all_fail:
- die("Failed to bind any port for '-%c %s', exiting", optname, optarg);
}
/**
--
2.48.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2] conf: Unify several paths in conf_ports()
2025-03-12 3:43 [PATCH v2] conf: Unify several paths in conf_ports() David Gibson
@ 2025-03-14 23:50 ` Stefano Brivio
2025-03-17 3:04 ` David Gibson
2025-03-17 11:50 ` Paul Holzinger
0 siblings, 2 replies; 4+ messages in thread
From: Stefano Brivio @ 2025-03-14 23:50 UTC (permalink / raw)
To: David Gibson; +Cc: passt-dev
On Wed, 12 Mar 2025 14:43:59 +1100
David Gibson <david@gibson.dropbear.id.au> wrote:
> In conf_ports() we have three different paths which actually do the setup
> of an individual forwarded port: one for the "all" case, one for the
> exclusions only case and one for the range of ports with possible
> exclusions case.
>
> We can unify those cases using a new helper which handles a single range
> of ports, with a bitmap of exclusions. Although this is slightly longer
> (largely due to the new helpers function comment), it reduces duplicated
> logic. It will also make future improvements to the tracking of port
> forwards easier.
>
> The new conf_ports_range_except() function has a pretty prodigious
> parameter list, but I still think it's an overall improvement in conceptual
> complexity.
>
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> ---
> conf.c | 173 ++++++++++++++++++++++++++++++---------------------------
> 1 file changed, 90 insertions(+), 83 deletions(-)
>
> v2:
> * Commit message updated slightly, but otherwise unmodified.
>
>
> diff --git a/conf.c b/conf.c
> index 065e7201..4e0099ba 100644
> --- a/conf.c
> +++ b/conf.c
> @@ -123,6 +123,75 @@ static int parse_port_range(const char *s, char **endptr,
> return 0;
> }
>
> +/**
> + * conf_ports_range_except() - Set up forwarding for a range of ports minus a
> + * bitmap of exclusions
> + * @c: Execution context
> + * @optname: Short option name, t, T, u, or U
> + * @optarg: Option argument (port specification)
> + * @fwd: Pointer to @fwd_ports to be updated
> + * @addr: Listening address
> + * @ifname: Listening interface
> + * @first: First port to forward
> + * @last: Last port to forward
> + * @exclude: Bitmap of ports to exclude
> + * @to: Port to translate @first to when forwarding
> + * @weak: Ignore errors, as long as at least one port is mapped
> + */
> +static void conf_ports_range_except(const struct ctx *c, char optname,
> + const char *optarg, struct fwd_ports *fwd,
> + const union inany_addr *addr,
> + const char *ifname,
> + uint16_t first, uint16_t last,
> + const uint8_t *exclude, uint16_t to,
> + bool weak)
> +{
> + bool bound_one = false;
> + unsigned i;
> + int ret;
> +
> + if (first == 0) {
> + die("Can't forward port 0 for option '-%c %s'",
> + optname, optarg);
> + }
This introduces two subtle functional changes that are a bit unexpected
given the commit message. Before:
$ ./pasta -t 0
$
$ ./pasta -t 0-1025
Failed to bind port 1 (Permission denied) for option '-t 0-1025', exiting
After:
$ ./pasta -t 0
Can't forward port 0 for option '-t 0'
$ ./pasta -t 0-1025
Can't forward port 0 for option '-t 0-1025'
...anyway, I doubt anybody would use -t 0 on purpose (to get a port
automatically assigned), and while it probably works for TCP (check
bound ports after starting pasta, use the assigned one), it wouldn't
necessarily work as expected for UDP if the application relies on our
flow tracking.
For TCP, actually, -t 0 might be useful, see e.g. random_free_port() in
Podman tests (/test/system/helpers.network.bash). We should print the
port number that was bound, though, and document the feature.
More than that: that could actually be the only race-free possibility
of picking and forwarding a port where the number doesn't matter.
In any case, given that it works by mistake now and it's undocumented,
let me go ahead and apply this for the moment. We can add the
"functionality" back later if it makes sense.
--
Stefano
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] conf: Unify several paths in conf_ports()
2025-03-14 23:50 ` Stefano Brivio
@ 2025-03-17 3:04 ` David Gibson
2025-03-17 11:50 ` Paul Holzinger
1 sibling, 0 replies; 4+ messages in thread
From: David Gibson @ 2025-03-17 3:04 UTC (permalink / raw)
To: Stefano Brivio; +Cc: passt-dev
[-- Attachment #1: Type: text/plain, Size: 4464 bytes --]
On Sat, Mar 15, 2025 at 12:50:28AM +0100, Stefano Brivio wrote:
> On Wed, 12 Mar 2025 14:43:59 +1100
> David Gibson <david@gibson.dropbear.id.au> wrote:
>
> > In conf_ports() we have three different paths which actually do the setup
> > of an individual forwarded port: one for the "all" case, one for the
> > exclusions only case and one for the range of ports with possible
> > exclusions case.
> >
> > We can unify those cases using a new helper which handles a single range
> > of ports, with a bitmap of exclusions. Although this is slightly longer
> > (largely due to the new helpers function comment), it reduces duplicated
> > logic. It will also make future improvements to the tracking of port
> > forwards easier.
> >
> > The new conf_ports_range_except() function has a pretty prodigious
> > parameter list, but I still think it's an overall improvement in conceptual
> > complexity.
> >
> > Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> > ---
> > conf.c | 173 ++++++++++++++++++++++++++++++---------------------------
> > 1 file changed, 90 insertions(+), 83 deletions(-)
> >
> > v2:
> > * Commit message updated slightly, but otherwise unmodified.
> >
> >
> > diff --git a/conf.c b/conf.c
> > index 065e7201..4e0099ba 100644
> > --- a/conf.c
> > +++ b/conf.c
> > @@ -123,6 +123,75 @@ static int parse_port_range(const char *s, char **endptr,
> > return 0;
> > }
> >
> > +/**
> > + * conf_ports_range_except() - Set up forwarding for a range of ports minus a
> > + * bitmap of exclusions
> > + * @c: Execution context
> > + * @optname: Short option name, t, T, u, or U
> > + * @optarg: Option argument (port specification)
> > + * @fwd: Pointer to @fwd_ports to be updated
> > + * @addr: Listening address
> > + * @ifname: Listening interface
> > + * @first: First port to forward
> > + * @last: Last port to forward
> > + * @exclude: Bitmap of ports to exclude
> > + * @to: Port to translate @first to when forwarding
> > + * @weak: Ignore errors, as long as at least one port is mapped
> > + */
> > +static void conf_ports_range_except(const struct ctx *c, char optname,
> > + const char *optarg, struct fwd_ports *fwd,
> > + const union inany_addr *addr,
> > + const char *ifname,
> > + uint16_t first, uint16_t last,
> > + const uint8_t *exclude, uint16_t to,
> > + bool weak)
> > +{
> > + bool bound_one = false;
> > + unsigned i;
> > + int ret;
> > +
> > + if (first == 0) {
> > + die("Can't forward port 0 for option '-%c %s'",
> > + optname, optarg);
> > + }
>
> This introduces two subtle functional changes that are a bit unexpected
> given the commit message. Before:
>
> $ ./pasta -t 0
> $
>
> $ ./pasta -t 0-1025
> Failed to bind port 1 (Permission denied) for option '-t 0-1025', exiting
>
> After:
>
> $ ./pasta -t 0
> Can't forward port 0 for option '-t 0'
>
> $ ./pasta -t 0-1025
> Can't forward port 0 for option '-t 0-1025'
I'd consider both those improvements, since we *aren't* able to
forward port 0.
>
> ...anyway, I doubt anybody would use -t 0 on purpose (to get a port
> automatically assigned), and while it probably works for TCP (check
> bound ports after starting pasta, use the assigned one), it wouldn't
> necessarily work as expected for UDP if the application relies on our
> flow tracking.
>
> For TCP, actually, -t 0 might be useful, see e.g. random_free_port() in
> Podman tests (/test/system/helpers.network.bash). We should print the
> port number that was bound, though, and document the feature.
I agree that could be a useful thing to do, but I don't think -t 0
would be a good syntax for it: if it's useful to get pasta to assign
one port it's probably useful to get it to assign multiple ports, and
repeating -t 0 doesn't really make sense for that.
> More than that: that could actually be the only race-free possibility
> of picking and forwarding a port where the number doesn't matter.
>
> In any case, given that it works by mistake now and it's undocumented,
> let me go ahead and apply this for the moment. We can add the
> "functionality" back later if it makes sense.
--
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
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] conf: Unify several paths in conf_ports()
2025-03-14 23:50 ` Stefano Brivio
2025-03-17 3:04 ` David Gibson
@ 2025-03-17 11:50 ` Paul Holzinger
1 sibling, 0 replies; 4+ messages in thread
From: Paul Holzinger @ 2025-03-17 11:50 UTC (permalink / raw)
To: Stefano Brivio, David Gibson; +Cc: passt-dev
On 15/03/2025 00:50, Stefano Brivio wrote:
> On Wed, 12 Mar 2025 14:43:59 +1100
> David Gibson <david@gibson.dropbear.id.au> wrote:
>
>> In conf_ports() we have three different paths which actually do the setup
>> of an individual forwarded port: one for the "all" case, one for the
>> exclusions only case and one for the range of ports with possible
>> exclusions case.
>>
>> We can unify those cases using a new helper which handles a single range
>> of ports, with a bitmap of exclusions. Although this is slightly longer
>> (largely due to the new helpers function comment), it reduces duplicated
>> logic. It will also make future improvements to the tracking of port
>> forwards easier.
>>
>> The new conf_ports_range_except() function has a pretty prodigious
>> parameter list, but I still think it's an overall improvement in conceptual
>> complexity.
>>
>> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
>> ---
>> conf.c | 173 ++++++++++++++++++++++++++++++---------------------------
>> 1 file changed, 90 insertions(+), 83 deletions(-)
>>
>> v2:
>> * Commit message updated slightly, but otherwise unmodified.
>>
>>
>> diff --git a/conf.c b/conf.c
>> index 065e7201..4e0099ba 100644
>> --- a/conf.c
>> +++ b/conf.c
>> @@ -123,6 +123,75 @@ static int parse_port_range(const char *s, char **endptr,
>> return 0;
>> }
>>
>> +/**
>> + * conf_ports_range_except() - Set up forwarding for a range of ports minus a
>> + * bitmap of exclusions
>> + * @c: Execution context
>> + * @optname: Short option name, t, T, u, or U
>> + * @optarg: Option argument (port specification)
>> + * @fwd: Pointer to @fwd_ports to be updated
>> + * @addr: Listening address
>> + * @ifname: Listening interface
>> + * @first: First port to forward
>> + * @last: Last port to forward
>> + * @exclude: Bitmap of ports to exclude
>> + * @to: Port to translate @first to when forwarding
>> + * @weak: Ignore errors, as long as at least one port is mapped
>> + */
>> +static void conf_ports_range_except(const struct ctx *c, char optname,
>> + const char *optarg, struct fwd_ports *fwd,
>> + const union inany_addr *addr,
>> + const char *ifname,
>> + uint16_t first, uint16_t last,
>> + const uint8_t *exclude, uint16_t to,
>> + bool weak)
>> +{
>> + bool bound_one = false;
>> + unsigned i;
>> + int ret;
>> +
>> + if (first == 0) {
>> + die("Can't forward port 0 for option '-%c %s'",
>> + optname, optarg);
>> + }
> This introduces two subtle functional changes that are a bit unexpected
> given the commit message. Before:
>
> $ ./pasta -t 0
> $
>
> $ ./pasta -t 0-1025
> Failed to bind port 1 (Permission denied) for option '-t 0-1025', exiting
>
> After:
>
> $ ./pasta -t 0
> Can't forward port 0 for option '-t 0'
>
> $ ./pasta -t 0-1025
> Can't forward port 0 for option '-t 0-1025'
>
> ...anyway, I doubt anybody would use -t 0 on purpose (to get a port
> automatically assigned), and while it probably works for TCP (check
> bound ports after starting pasta, use the assigned one), it wouldn't
> necessarily work as expected for UDP if the application relies on our
> flow tracking.
Why would this not work for UDP? bind() wise you can still bind 0 fine
and get a free port assigned?
>
> For TCP, actually, -t 0 might be useful, see e.g. random_free_port() in
> Podman tests (/test/system/helpers.network.bash). We should print the
> port number that was bound, though, and document the feature.
>
> More than that: that could actually be the only race-free possibility
> of picking and forwarding a port where the number doesn't matter.
Yes it could be useful for podman but then it should also work with udp.
I am less worried about the tests, this issue is in podman proper as you
can do "-p 80", then podman assigns a free host port. Except that this
is super broken in podman because we do this once when we create the
container so this is totally racy and non conflict free[1]. The thing of
course is for podman we have to deal with like 4 other port forwarder
implementations that we would need to support. As such I don't see us
ever finding time to properly fix it unless it magically gets a ton of
priority. So if pasta does not support for it I have no problems with
that, however maybe one day we like to reconsider.
[1]
https://github.com/containers/podman/issues/10205#issuecomment-1010055023
>
> In any case, given that it works by mistake now and it's undocumented,
> let me go ahead and apply this for the moment. We can add the
> "functionality" back later if it makes sense.
>
--
Paul Holzinger
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-03-17 11:50 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-12 3:43 [PATCH v2] conf: Unify several paths in conf_ports() David Gibson
2025-03-14 23:50 ` Stefano Brivio
2025-03-17 3:04 ` David Gibson
2025-03-17 11:50 ` Paul Holzinger
Code repositories for project(s) associated with this public inbox
https://passt.top/passt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).