From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=gibson.dropbear.id.au Authentication-Results: passt.top; dkim=pass (2048-bit key; secure) header.d=gibson.dropbear.id.au header.i=@gibson.dropbear.id.au header.a=rsa-sha256 header.s=202512 header.b=bzFPHh75; dkim-atps=neutral Received: from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3]) by passt.top (Postfix) with ESMTPS id 2F61D5A065D for ; Sun, 21 Dec 2025 13:11:36 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202512; t=1766319092; bh=Tz4M7OXtvh2Dw+4CP/a3fUCERA8VSaQ7lHsQMFshuQU=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=bzFPHh75I7z3QNMZeyzJ2k9MRXV/oUXtt3eqKYJ1eUfhoqfsydz3YUVHjYgWTAHeO tzVsG8Ysqwat+f2XTUDFbjlno/1tdXD6Ep7Dv60wEudn+ttIvVbIVPmbmMlfdKU2dM pvroopYy9aduFHcfV+Zs0nkJxdxDWU6ouHmezZ1LX1cK2Yxgra6ux25Bkn3qUbbJrb /0uCxj506nauTlCbPWwKhLsNZk3b1Dx0dugGz6zymuHN3kcDYurzUzkFAiTdHcIzBe tg5pC1oslRvdQHbXl+GCOrmes50rLyZnvBDQeSA5Ltx1npZ4xsmcXZ+Em2cNlUC+to 2RExgwWcHBqkg== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4dZ0Vr2MRJz4wCx; Sun, 21 Dec 2025 23:11:32 +1100 (AEDT) Date: Sun, 21 Dec 2025 23:06:47 +1100 From: David Gibson To: Jon Maloy Subject: Re: [PATCH v3 1/2] conf: Support CIDR notation for -a/--address option Message-ID: References: <20251218222213.703693-1-jmaloy@redhat.com> <20251218222213.703693-2-jmaloy@redhat.com> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="QNFTjkzSZP/ExJP0" Content-Disposition: inline In-Reply-To: <20251218222213.703693-2-jmaloy@redhat.com> Message-ID-Hash: 3CRNVNXM33R3CHZ3VI3ARBNVG7O6PLXW X-Message-ID-Hash: 3CRNVNXM33R3CHZ3VI3ARBNVG7O6PLXW X-MailFrom: dgibson@gandalf.ozlabs.org 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: sbrivio@redhat.com, dgibson@redhat.com, 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: --QNFTjkzSZP/ExJP0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Thu, Dec 18, 2025 at 05:22:12PM -0500, Jon Maloy wrote: > Extend the -a/--address option to accept addresses in CIDR notation > (e.g., 192.168.1.1/24 or 2001:db8::1/64) as an alternative to using > separate -a and -n options. >=20 > Add conf_addr_prefix_len() helper function that: > - Parses address strings with optional /prefix_len suffix using inany_pto= n() > - Validates prefix length based on address family (0-32 for IPv4, > 0-128 for IPv6) > - Returns address family via union inany_addr output parameter >=20 > For IPv4, the prefix length is stored in ip4.prefix_len when provided. > Mixing -n and CIDR notation results in an error to catch likely user > mistakes. >=20 > Also fix a bug in conf_ip4_prefix() that was incorrectly using the > global 'optarg' instead of its 'arg' parameter. >=20 > Signed-off-by: Jon Maloy > --- > conf.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++---------- > 1 file changed, 79 insertions(+), 15 deletions(-) >=20 > diff --git a/conf.c b/conf.c > index 2942c8c..81a6ca3 100644 > --- a/conf.c > +++ b/conf.c > @@ -682,7 +682,7 @@ static int conf_ip4_prefix(const char *arg) > return -1; > } else { > errno =3D 0; > - len =3D strtoul(optarg, NULL, 0); > + len =3D strtoul(arg, NULL, 0); > if (len > 32 || errno) > return -1; > } > @@ -690,6 +690,50 @@ static int conf_ip4_prefix(const char *arg) > return len; > } > =20 > +/** > + * conf_addr_prefix_len() - Parse address with optional prefix length > + * @arg: Address string, optionally with /prefix_len suffix (modified) > + * @addr: Output for parsed address > + * @prefix_len: Output for prefix length (0 if not specified) This makes me slightly nervous, because a 0-length prefix is theoretically valid. Telling the guest that the entire internet is on it's LAN is pretty weird, but it does potentially have a use: it means the guest can reach the world without having a default gateway (at the cost of requiring many ARP entries in the guest). > + * > + * Return: AF_INET for IPv4, AF_INET6 for IPv6, -1 on error > + */ > +static int conf_addr_prefix_len(char *arg, union inany_addr *addr, > + int *prefix_len) > +{ > + char *slash; > + > + *prefix_len =3D 0; > + > + /* Check for /prefix_len suffix */ > + slash =3D strchr(arg, '/'); > + if (slash) { > + unsigned long len; > + char *end; > + > + *slash =3D '\0'; > + errno =3D 0; > + len =3D strtoul(slash + 1, &end, 10); > + if (errno || *end) > + return -1; > + > + *prefix_len =3D len; > + } > + > + if (!inany_pton(arg, addr)) > + return -1; > + > + if (inany_v4(addr)) { > + if (*prefix_len > 32) > + return -1; > + return AF_INET; Ah... sorry.. I just realised there's a subtle problem using inany_pton() here. The above is correct if the user entered an IPv4 address. However, it's possible they could have explicitly entered a v4-mapped IPv6 address. inany_pton() will interpret that like a an IPv4 address, which I think is correct, *except* that ::ffff:192.168.1.1/112 should be interpreted like: 192.168.1.1/16 not like: 192.168.1.1/112 Maybe we actually want to create an inany_prefix_pton() or something. > + } > + > + if (*prefix_len > 128) > + return -1; > + return AF_INET6; > +} > + > /** > * conf_ip4() - Verify or detect IPv4 support, get relevant addresses > * @ifi: Host interface to attempt (0 to determine one) > @@ -896,7 +940,7 @@ static void usage(const char *name, FILE *f, int stat= us) > " a zero value disables assignment\n" > " default: 65520: maximum 802.3 MTU minus 802.3 header\n" > " length, rounded to 32 bits (IPv4 words)\n" > - " -a, --address ADDR Assign IPv4 or IPv6 address ADDR\n" > + " -a, --address ADDR Assign IPv4 or IPv6 address ADDR[/PREFIXLEN]\n" > " can be specified zero to two times (for IPv4 and IPv6)\n" > " default: use addresses from interface with default route\n" > " -n, --netmask MASK Assign IPv4 MASK, dot-decimal or bits\n" > @@ -1499,6 +1543,7 @@ void conf(struct ctx *c, int argc, char **argv) > const char *logname =3D (c->mode =3D=3D MODE_PASTA) ? "pasta" : "passt"; > char userns[PATH_MAX] =3D { 0 }, netns[PATH_MAX] =3D { 0 }; > bool copy_addrs_opt =3D false, copy_routes_opt =3D false; > + bool prefix_from_cidr =3D false, prefix_from_opt =3D false; > enum fwd_ports_mode fwd_default =3D FWD_NONE; > bool v4_only =3D false, v6_only =3D false; > unsigned dns4_idx =3D 0, dns6_idx =3D 0; > @@ -1808,23 +1853,39 @@ void conf(struct ctx *c, int argc, char **argv) > c->mtu =3D mtu; > break; > } > - case 'a': > - 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) && > - !IN6_IS_ADDR_V4COMPAT(&c->ip6.addr) && > - !IN6_IS_ADDR_MULTICAST(&c->ip6.addr)) { > + case 'a': { > + union inany_addr addr; > + const struct in_addr *a4; > + int prefix_len =3D 0; > + int af; > + > + af =3D conf_addr_prefix_len(optarg, &addr, &prefix_len); > + > + if (af =3D=3D AF_INET6 && > + !IN6_IS_ADDR_UNSPECIFIED(&addr.a6) && > + !IN6_IS_ADDR_LOOPBACK(&addr.a6) && > + !IN6_IS_ADDR_V4MAPPED(&addr.a6) && The V4MAPPED test is no longer relevant, since if it was true, conf_addr_prefix_len() could not return AF_INET6. > + !IN6_IS_ADDR_V4COMPAT(&addr.a6) && > + !IN6_IS_ADDR_MULTICAST(&addr.a6)) { > + c->ip6.addr =3D addr.a6; > if (c->mode =3D=3D MODE_PASTA) > c->ip6.no_copy_addrs =3D true; > break; > } Note that we do have inany_is_unicast(), inany_is_loopback() and inany_is_unspecified() helpers that might allow us to simplify this. > - 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) && > - !IN4_IS_ADDR_MULTICAST(&c->ip4.addr)) { > + a4 =3D inany_v4(&addr); > + if (af =3D=3D AF_INET && a4 && > + !IN4_IS_ADDR_UNSPECIFIED(a4) && > + !IN4_IS_ADDR_BROADCAST(a4) && > + !IN4_IS_ADDR_LOOPBACK(a4) && > + !IN4_IS_ADDR_MULTICAST(a4)) { > + c->ip4.addr =3D *a4; > + if (prefix_len) { > + if (prefix_from_opt) > + die("Can't use both -n and CIDR prefix length"); > + c->ip4.prefix_len =3D prefix_len; > + prefix_from_cidr =3D true; > + } > if (c->mode =3D=3D MODE_PASTA) > c->ip4.no_copy_addrs =3D true; > break; > @@ -1832,11 +1893,14 @@ void conf(struct ctx *c, int argc, char **argv) > =20 > die("Invalid address: %s", optarg); > break; > + } > case 'n': > + if (prefix_from_cidr) > + die("Can't use both -n and CIDR prefix length"); > c->ip4.prefix_len =3D conf_ip4_prefix(optarg); > if (c->ip4.prefix_len < 0) > die("Invalid netmask: %s", optarg); > - > + prefix_from_opt =3D true; > break; > case 'M': > parse_mac(c->our_tap_mac, optarg); > --=20 > 2.52.0 >=20 --=20 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 --QNFTjkzSZP/ExJP0 Content-Type: application/pgp-signature; name=signature.asc -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEO+dNsU4E3yXUXRK2zQJF27ox2GcFAmlH4scACgkQzQJF27ox 2GcmIQ//cW6tdNshX3SxI+TQw/ODGVciNRMsmnN4aio86a5yUyclanL4aKWlm9WD JG9bnu3eXfRgYKlEi9snp5TXsdZq86cGmIpiRnhxQWB3nbIyaplQ+X9EYJ//VZqW hChy1mCZgCHOKuRZvuD9x5lyFIKdtsUhZmAkK9o6P8f11lQkSQ8nd8o8KZsnb3q+ UTvI32ET9SiTfZo5/S4J0WhrJxQLr/S5rbK4oNfU5aOGDtBTpVt8UFhvCf5O48GL GXhi0tp/Rymf5UhJHUnvBeK6qI6pnqT4BMIzDx60+kMU24sb+5g10y9WaHfbH/nt EOxwjtXmD6QO40caRfhJ9VtVImbYDgvDbYEy/aoZ9oGw1FH2EjTOwHDQKujnGubz 8goP9EvphS/YgcdrHkJYegT5zkF/milmfcTXeNmGBHCLrwirlaIksiVSsatNlTIf 20ujSxvUroklHsJ/Bh8GXGsbPIZ5/QZ4Y+vc7LtY8qm3qLe5/aUohclMDcRrwY9j riVqUB9f4Hz+rmDqXJwrf7dffSMScM0tH78nbzUyVG+9/AwahMKfeTt8axGThuOl f9jVIoC69qVEYj2PX9na8Oa9LL+NtUwWqwysgm0KttWx+NtFryaAO72qHKUrvQrz RiGJRmAB/NDeX6pHkIMfzSG8t8mYhgEYd1ZK9ca2JoM2WSh4VNY= =8P3h -----END PGP SIGNATURE----- --QNFTjkzSZP/ExJP0--