From: David Gibson <david@gibson.dropbear.id.au>
To: Stefano Brivio <sbrivio@redhat.com>
Cc: passt-dev@passt.top, Paul Holzinger <pholzing@redhat.com>
Subject: Re: [PATCH v3] conf: Accept duplicate and conflicting options, the last one wins
Date: Fri, 21 Jun 2024 10:53:26 +1000 [thread overview]
Message-ID: <ZnTPBnmQptXys1_R@zatzit> (raw)
In-Reply-To: <20240620171921.179930-1-sbrivio@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 17319 bytes --]
On Thu, Jun 20, 2024 at 07:19:21PM +0200, Stefano Brivio wrote:
> In multiple occasions, especially when passt(1) and pasta(1) are used
> in integrations such as the one with Podman, the ability to override
> earlier options on the command line with later one would have been
> convenient.
>
> Recently, to debug a number of issues happening with Podman, I would
> have liked to ask users to share a debug log by passing --debug as
> additional option, but pasta refuses --quiet (always passed by Podman)
> and --debug at the same time.
>
> On top of this, Podman lets users specify other pasta options in its
> containers.conf(5) file, as well as on the command line.
>
> The options from the configuration files are appended together with
> the ones from the command line, which makes it impossible for users to
> override options from the configuration file, if duplicated options
> are refused, unless Podman takes care of sorting them, which is
> clearly not sustainable.
>
> For --debug and --trace, somebody took care of this on Podman side at:
> https://github.com/containers/common/pull/2052
>
> but this doesn't fix the issue with other options, and we'll have
> anyway older versions of Podman around, too.
>
> I think there's some value in telling users about duplicated or
> conflicting options, because that might reveal issues in integrations
> or accidental misconfigurations, but by now I'm fairly convinced that
> the downsides outweigh this.
>
> Drop checks about duplicate options and mutually exclusive ones. In
> some cases, we need to also undo a couple of initialisations caused
> by earlier options, but this looks like a simplification, overall.
>
> Notable exception: --stderr still conflicts with --log-file, because
> users might have the expectation that they don't actually conflict.
> But they do conflict in the existing implementation, so it's safer
> to make sure that the users notice that.
>
> Suggested-by: Paul Holzinger <pholzing@redhat.com>
> Suggested-by: David Gibson <david@gibson.dropbear.id.au>
> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
> ---
> v3:
> - override --gateway and --dns-forward with the last given option
> for a given address family, too
>
> v2:
> - report --log-file and --stderr as conflicting, but accept
> multiple options if they don't conflict
> - for --log-file and --pcap, make it clear in the man page that
> the general principle still applies: only the last one (of each)
> takes effect
> - add some more context about Podman and how the list of pasta
> options is built there, in the commit message
>
> conf.c | 146 ++++++++++++++++----------------------------------------
> passt.1 | 17 +++++++
> 2 files changed, 59 insertions(+), 104 deletions(-)
>
> diff --git a/conf.c b/conf.c
> index 94b3ed6..4fff2b1 100644
> --- a/conf.c
> +++ b/conf.c
> @@ -517,9 +517,6 @@ static void conf_netns_opt(char *netns, const char *arg)
> static void conf_pasta_ns(int *netns_only, char *userns, char *netns,
> int optind, int argc, char *argv[])
> {
> - if (*netns_only && *userns)
> - die("Both --userns and --netns-only given");
> -
> if (*netns && optind != argc)
> die("Both --netns and PID or command given");
>
> @@ -1204,7 +1201,6 @@ void conf(struct ctx *c, int argc, char **argv)
> {"udp-ns", required_argument, NULL, 'U' },
> {"userns", required_argument, NULL, 2 },
> {"netns", required_argument, NULL, 3 },
> - {"netns-only", no_argument, &netns_only, 1 },
> {"ns-mac-addr", required_argument, NULL, 4 },
> {"dhcp-dns", no_argument, NULL, 5 },
> {"no-dhcp-dns", no_argument, NULL, 6 },
> @@ -1221,6 +1217,7 @@ void conf(struct ctx *c, int argc, char **argv)
> {"config-net", no_argument, NULL, 17 },
> {"no-copy-routes", no_argument, NULL, 18 },
> {"no-copy-addrs", no_argument, NULL, 19 },
> + {"netns-only", no_argument, NULL, 20 },
> { 0 },
> };
> char userns[PATH_MAX] = { 0 }, netns[PATH_MAX] = { 0 };
> @@ -1265,6 +1262,8 @@ void conf(struct ctx *c, int argc, char **argv)
> if (ret <= 0 || ret >= (int)sizeof(userns))
> die("Invalid userns: %s", optarg);
>
> + netns_only = 0;
> +
> break;
> case 3:
> if (c->mode != MODE_PASTA)
> @@ -1303,14 +1302,12 @@ void conf(struct ctx *c, int argc, char **argv)
> c->no_dhcp_dns_search = 1;
> break;
> case 9:
> - if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match) &&
> - inet_pton(AF_INET6, optarg, &c->ip6.dns_match) &&
> + if (inet_pton(AF_INET6, optarg, &c->ip6.dns_match) &&
> !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match) &&
> !IN6_IS_ADDR_LOOPBACK(&c->ip6.dns_match))
> break;
>
> - if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match) &&
> - inet_pton(AF_INET, optarg, &c->ip4.dns_match) &&
> + if (inet_pton(AF_INET, optarg, &c->ip4.dns_match) &&
> !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match) &&
> !IN4_IS_ADDR_BROADCAST(&c->ip4.dns_match) &&
> !IN4_IS_ADDR_LOOPBACK(&c->ip4.dns_match))
> @@ -1325,24 +1322,13 @@ void conf(struct ctx *c, int argc, char **argv)
> c->no_netns_quit = 1;
> break;
> case 11:
> - if (c->trace)
> - die("Multiple --trace options given");
> -
> - if (c->quiet)
> - die("Either --trace or --quiet");
> -
> c->trace = c->debug = 1;
> + c->quiet = 0;
> break;
> case 12:
> - if (runas)
> - die("Multiple --runas options given");
> -
> runas = optarg;
> break;
> case 13:
> - if (logsize)
> - die("Multiple --log-size options given");
> -
> errno = 0;
> logsize = strtol(optarg, NULL, 0);
>
> @@ -1356,9 +1342,6 @@ void conf(struct ctx *c, int argc, char **argv)
> fprintf(stdout, VERSION_BLOB);
> exit(EXIT_SUCCESS);
> case 15:
> - if (*c->ip4.ifname_out)
> - die("Redundant outbound interface: %s", optarg);
> -
> ret = snprintf(c->ip4.ifname_out,
> sizeof(c->ip4.ifname_out), "%s", optarg);
> if (ret <= 0 || ret >= (int)sizeof(c->ip4.ifname_out))
> @@ -1366,9 +1349,6 @@ void conf(struct ctx *c, int argc, char **argv)
>
> break;
> case 16:
> - if (*c->ip6.ifname_out)
> - die("Redundant outbound interface: %s", optarg);
> -
> ret = snprintf(c->ip6.ifname_out,
> sizeof(c->ip6.ifname_out), "%s", optarg);
> if (ret <= 0 || ret >= (int)sizeof(c->ip6.ifname_out))
> @@ -1395,62 +1375,47 @@ void conf(struct ctx *c, int argc, char **argv)
> warn("--no-copy-addrs will be dropped soon");
> c->no_copy_addrs = copy_addrs_opt = true;
> break;
> - case 'd':
> - if (c->debug)
> - die("Multiple --debug options given");
> -
> - if (c->quiet)
> - die("Either --debug or --quiet");
> + case 20:
> + if (c->mode != MODE_PASTA)
> + die("--netns-only is for pasta mode only");
>
> + netns_only = 1;
> + *userns = 0;
> + break;
> + case 'd':
> c->debug = 1;
> + c->quiet = 0;
> break;
> case 'e':
> if (logfile)
> die("Can't log to both file and stderr");
>
> - if (c->force_stderr)
> - die("Multiple --stderr options given");
> -
> c->force_stderr = 1;
> + logfile = NULL;
> break;
> case 'l':
> if (c->force_stderr)
> die("Can't log to both stderr and file");
>
> - if (logfile)
> - die("Multiple --log-file options given");
> -
> logfile = optarg;
> + c->force_stderr = 0;
> break;
> case 'q':
> - if (c->quiet)
> - die("Multiple --quiet options given");
> -
> - if (c->debug)
> - die("Either --debug or --quiet");
> -
> c->quiet = 1;
> + c->debug = c->trace = 0;
> break;
> case 'f':
> - if (c->foreground)
> - die("Multiple --foreground options given");
> -
> c->foreground = 1;
> break;
> case 's':
> - if (*c->sock_path)
> - die("Multiple --socket options given");
> -
> ret = snprintf(c->sock_path, UNIX_SOCK_MAX - 1, "%s",
> optarg);
> if (ret <= 0 || ret >= (int)sizeof(c->sock_path))
> die("Invalid socket path: %s", optarg);
>
> + c->fd_tap = -1;
> break;
> case 'F':
> - if (c->fd_tap >= 0)
> - die("Multiple --fd options given");
> -
> errno = 0;
> c->fd_tap = strtol(optarg, NULL, 0);
>
> @@ -1458,12 +1423,9 @@ void conf(struct ctx *c, int argc, char **argv)
> die("Invalid --fd: %s", optarg);
>
> c->one_off = true;
> -
> + *c->sock_path = 0;
> break;
> case 'I':
> - if (*c->pasta_ifn)
> - die("Multiple --ns-ifname options given");
> -
> ret = snprintf(c->pasta_ifn, IFNAMSIZ, "%s",
> optarg);
> if (ret <= 0 || ret >= IFNAMSIZ)
> @@ -1471,18 +1433,12 @@ void conf(struct ctx *c, int argc, char **argv)
>
> break;
> case 'p':
> - if (*c->pcap)
> - die("Multiple --pcap options given");
> -
> ret = snprintf(c->pcap, sizeof(c->pcap), "%s", optarg);
> if (ret <= 0 || ret >= (int)sizeof(c->pcap))
> die("Invalid pcap path: %s", optarg);
>
> break;
> case 'P':
> - if (*c->pidfile)
> - die("Multiple --pid options given");
> -
> ret = snprintf(c->pidfile, sizeof(c->pidfile), "%s",
> optarg);
> if (ret <= 0 || ret >= (int)sizeof(c->pidfile))
> @@ -1490,9 +1446,6 @@ void conf(struct ctx *c, int argc, char **argv)
>
> break;
> case 'm':
> - if (c->mtu)
> - die("Multiple --mtu options given");
> -
> errno = 0;
> c->mtu = strtol(optarg, NULL, 0);
>
> @@ -1510,8 +1463,7 @@ void conf(struct ctx *c, int argc, char **argv)
> if (c->mode == MODE_PASTA)
> c->no_copy_addrs = 1;
>
> - if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr) &&
> - inet_pton(AF_INET6, optarg, &c->ip6.addr) &&
> + if (inet_pton(AF_INET6, optarg, &c->ip6.addr) &&
> !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr) &&
> !IN6_IS_ADDR_LOOPBACK(&c->ip6.addr) &&
> !IN6_IS_ADDR_V4MAPPED(&c->ip6.addr) &&
> @@ -1519,8 +1471,7 @@ void conf(struct ctx *c, int argc, char **argv)
> !IN6_IS_ADDR_MULTICAST(&c->ip6.addr))
> break;
>
> - if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr) &&
> - inet_pton(AF_INET, optarg, &c->ip4.addr) &&
> + if (inet_pton(AF_INET, optarg, &c->ip4.addr) &&
> !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr) &&
> !IN4_IS_ADDR_BROADCAST(&c->ip4.addr) &&
> !IN4_IS_ADDR_LOOPBACK(&c->ip4.addr) &&
> @@ -1542,14 +1493,12 @@ void conf(struct ctx *c, int argc, char **argv)
> if (c->mode == MODE_PASTA)
> c->no_copy_routes = 1;
>
> - if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.gw) &&
> - inet_pton(AF_INET6, optarg, &c->ip6.gw) &&
> + if (inet_pton(AF_INET6, optarg, &c->ip6.gw) &&
> !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.gw) &&
> !IN6_IS_ADDR_LOOPBACK(&c->ip6.gw))
> break;
>
> - if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.gw) &&
> - inet_pton(AF_INET, optarg, &c->ip4.gw) &&
> + if (inet_pton(AF_INET, optarg, &c->ip4.gw) &&
> !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.gw) &&
> !IN4_IS_ADDR_BROADCAST(&c->ip4.gw) &&
> !IN4_IS_ADDR_LOOPBACK(&c->ip4.gw))
> @@ -1558,16 +1507,12 @@ void conf(struct ctx *c, int argc, char **argv)
> die("Invalid gateway address: %s", optarg);
> break;
> case 'i':
> - if (ifi4 || ifi6)
> - die("Redundant interface: %s", optarg);
> -
> if (!(ifi4 = ifi6 = if_nametoindex(optarg)))
> die("Invalid interface name %s: %s", optarg,
> strerror(errno));
> break;
> case 'o':
> - if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) &&
> - inet_pton(AF_INET6, optarg, &c->ip6.addr_out) &&
> + if (inet_pton(AF_INET6, optarg, &c->ip6.addr_out) &&
> !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) &&
> !IN6_IS_ADDR_LOOPBACK(&c->ip6.addr_out) &&
> !IN6_IS_ADDR_V4MAPPED(&c->ip6.addr_out) &&
> @@ -1575,8 +1520,7 @@ void conf(struct ctx *c, int argc, char **argv)
> !IN6_IS_ADDR_MULTICAST(&c->ip6.addr_out))
> break;
>
> - if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) &&
> - inet_pton(AF_INET, optarg, &c->ip4.addr_out) &&
> + if (inet_pton(AF_INET, optarg, &c->ip4.addr_out) &&
> !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) &&
> !IN4_IS_ADDR_BROADCAST(&c->ip4.addr_out) &&
> !IN4_IS_ADDR_MULTICAST(&c->ip4.addr_out))
> @@ -1587,18 +1531,23 @@ void conf(struct ctx *c, int argc, char **argv)
> break;
> case 'D':
> if (!strcmp(optarg, "none")) {
> - if (c->no_dns)
> - die("Redundant DNS options");
> + c->no_dns = 1;
>
> - if (dns4 - c->ip4.dns || dns6 - c->ip6.dns)
> - die("Conflicting DNS options");
> + dns4 = &c->ip4.dns[0];
> + memset(c->ip4.dns, 0, sizeof(c->ip4.dns));
> + c->ip4.dns[0] = (struct in_addr){ 0 };
> + c->ip4.dns_match = (struct in_addr){ 0 };
> + c->ip4.dns_host = (struct in_addr){ 0 };
> +
> + dns6 = &c->ip6.dns[0];
> + memset(c->ip6.dns, 0, sizeof(c->ip6.dns));
> + c->ip6.dns_match = (struct in6_addr){ 0 };
> + c->ip6.dns_host = (struct in6_addr){ 0 };
>
> - c->no_dns = 1;
> break;
> }
>
> - if (c->no_dns)
> - die("Conflicting DNS options");
> + c->no_dns = 0;
>
> if (dns4 - &c->ip4.dns[0] < ARRAY_SIZE(c->ip4.dns) &&
> inet_pton(AF_INET, optarg, &dns4_tmp)) {
> @@ -1616,18 +1565,14 @@ void conf(struct ctx *c, int argc, char **argv)
> break;
> case 'S':
> if (!strcmp(optarg, "none")) {
> - if (c->no_dns_search)
> - die("Redundant DNS search options");
> + c->no_dns_search = 1;
>
> - if (dnss != c->dns_search)
> - die("Conflicting DNS search options");
> + memset(c->dns_search, 0, sizeof(c->dns_search));
>
> - c->no_dns_search = 1;
> break;
> }
>
> - if (c->no_dns_search)
> - die("Conflicting DNS search options");
> + c->no_dns_search = 0;
>
> if (dnss - c->dns_search < ARRAY_SIZE(c->dns_search)) {
> ret = snprintf(dnss->n, sizeof(*c->dns_search),
> @@ -1643,17 +1588,16 @@ void conf(struct ctx *c, int argc, char **argv)
> break;
> case '4':
> v4_only = true;
> + v6_only = false;
> break;
> case '6':
> v6_only = true;
> + v4_only = false;
> break;
> case '1':
> if (c->mode == MODE_PASTA)
> die("--one-off is for passt mode only");
>
> - if (c->one_off)
> - die("Redundant --one-off option");
> -
> c->one_off = true;
> break;
> case 't':
> @@ -1672,12 +1616,6 @@ void conf(struct ctx *c, int argc, char **argv)
> }
> } while (name != -1);
>
> - if (v4_only && v6_only)
> - die("Options ipv4-only and ipv6-only are mutually exclusive");
> -
> - if (*c->sock_path && c->fd_tap >= 0)
> - die("Options --socket and --fd are mutually exclusive");
> -
> if (c->mode == MODE_PASTA && !c->pasta_conf_ns) {
> if (copy_routes_opt)
> die("--no-copy-routes needs --config-net");
> diff --git a/passt.1 b/passt.1
> index 7676fe3..7b9915b 100644
> --- a/passt.1
> +++ b/passt.1
> @@ -73,6 +73,9 @@ for performance reasons.
>
> .SH OPTIONS
>
> +Unless otherwise noted below, \fBif conflicting or multiple options are given,
> +the last one takes effect.\fR
> +
> .TP
> .BR \-d ", " \-\-debug
> Be verbose, don't log to the system logger.
> @@ -97,10 +100,21 @@ Log to standard error too.
> Default is to log to the system logger only, if started from an interactive
> terminal, and to both system logger and standard error otherwise.
>
> +This option cannot be specified together with \fB--log-file\fR, because there
> +might be a reasonable expectation that messages are logged to standard error as
> +well as to a file, which is however not supported.
> +
> .TP
> .BR \-l ", " \-\-log-file " " \fIPATH\fR
> Log to file \fIPATH\fR, not to standard error, and not to the system logger.
>
> +This option cannot be specified together with \fB--stderr\fR, because there
> +might be a reasonable expectation that messages are logged to a file as well as
> +to standard error, which is however not supported.
> +
> +Specifying this option multiple times does \fInot\fR lead to multiple log files:
> +the last given option takes effect.
> +
> .TP
> .BR \-\-log-size " " \fISIZE\fR
> Limit log file size to \fISIZE\fR bytes. When the log file is full, make room
> @@ -128,6 +142,9 @@ Show version and exit.
> Capture tap-facing (that is, guest-side or namespace-side) network packets to
> \fIfile\fR in \fBpcap\fR format.
>
> +Specifying this option multiple times does \fInot\fR lead to multiple capture
> +files: the last given option takes effect.
> +
> .TP
> .BR \-P ", " \-\-pid " " \fIfile
> Write own PID to \fIfile\fR once initialisation is done, before forking to
--
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 --]
next prev parent reply other threads:[~2024-06-21 0:56 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-20 17:19 [PATCH v3] conf: Accept duplicate and conflicting options, the last one wins Stefano Brivio
2024-06-21 0:53 ` David Gibson [this message]
2024-06-21 9:52 ` Paul Holzinger
2024-06-21 9:55 ` Stefano Brivio
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZnTPBnmQptXys1_R@zatzit \
--to=david@gibson.dropbear.id.au \
--cc=passt-dev@passt.top \
--cc=pholzing@redhat.com \
--cc=sbrivio@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).