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=202606 header.b=m9HkCgcv; dkim-atps=neutral Received: from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3]) by passt.top (Postfix) with ESMTPS id E1BC45A0271 for ; Wed, 01 Jul 2026 05:03:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202606; t=1782875034; bh=nAd4ChcGKD7f1BiI8CWwUi5MhnQa+SFTHbm+oZOQfIk=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=m9HkCgcvsJBbdv+vaGaPExLqL9JTqkkK3VeH0VGfn0PvyqKj3ZryWO2ITfvIzehOc OD7o+R2UWvKZwjApUg6nV4Xov+zyDjjePx/qIEIaV1gPNks501QdGcG0Vg261S0QIB QDQ67J15blpI5/Tc0Ry1z8ZrRbrybdzcZcWGTQH0aK/1dtMFuzHSe/6sowm63RkJVD f+1M30vEo0hOGOt9nCzLRdMjhy84ccPbdwDgE9/IoibEYQ7gs6u/zr0CAz6KUH2N80 hgsiLLVXMdZClpC1KmHslN673hw+GukUeNLKMxIiM/HU6lzp45vhzvfKDNy6S06mPJ rc+KdQXovIGeA== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4gqlGL3mT4z4wZj; Wed, 01 Jul 2026 13:03:54 +1000 (AEST) Date: Wed, 1 Jul 2026 11:44:08 +1000 From: David Gibson To: Stefano Brivio Subject: Re: [RESEND #2] Re: [PATCH 08/12] parse: Add helpers for parsing IP addresses Message-ID: References: <20260626071003.3472194-1-david@gibson.dropbear.id.au> <20260626071003.3472194-9-david@gibson.dropbear.id.au> <20260701020725.024edf81@elisabeth> <20260701021152.600ca7b0@elisabeth> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="geTAWwi7VpdE8ype" Content-Disposition: inline In-Reply-To: <20260701021152.600ca7b0@elisabeth> Message-ID-Hash: 77BEBFI6XOWM62C4ZKH2MJJNTWWNEKVN X-Message-ID-Hash: 77BEBFI6XOWM62C4ZKH2MJJNTWWNEKVN 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: passt-dev@passt.top, Jon Maloy 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: --geTAWwi7VpdE8ype Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Wed, Jul 01, 2026 at 02:11:52AM +0200, Stefano Brivio wrote: > *Actually* resending to jmaloy@redhat.com, the original email had: >=20 > On Wed, 1 Jul 2026 02:07:25 +0200 > Stefano Brivio wrote: >=20 > > On Fri, 26 Jun 2026 17:09:59 +1000 > > David Gibson wrote: > >=20 > > > parse_ipv[46]() are wrappers around inet_pton() that are more > > > convenient for use when the IP is part of a longer string, rather than > > > the entire string. parse_inany() replaces inany_pton() which again > > > will become more convenient for strings including IPs that aren't just > > > an IP. > > >=20 > > > For now we only have some simple and sometimes awkward use cases, > > > we'll replace these with more natural uses in future. > > >=20 > > > Cc: Jon Maloy >=20 > ...jamloy@redhat.com instead. Yeah, caught that just to late. Fixed. >=20 > > >=20 > > > Signed-off-by: David Gibson > > > --- > > > Makefile | 1 + > > > conf.c | 6 ++-- > > > fwd_rule.c | 21 ++++--------- > > > inany.c | 24 ++------------ > > > inany.h | 1 - > > > parse.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++= ++ > > > parse.h | 4 +++ > > > 7 files changed, 109 insertions(+), 39 deletions(-) > > >=20 > > > diff --git a/Makefile b/Makefile > > > index 85d279c0..e2b22ddf 100644 > > > --- a/Makefile > > > +++ b/Makefile > > > @@ -227,4 +227,5 @@ pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3D= unusedFunction:inany.c > > > pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3DunusedFunction:ip.h > > > pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3DunusedFunction:seri= alise.c > > > pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3DstaticFunction:fwd_= rule.c > > > +pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3DstaticFunction:pars= e.c > > > pesto.cppcheck: $(PESTO_SRCS) $(PESTO_HEADERS) seccomp_pesto.h > > > diff --git a/conf.c b/conf.c > > > index bd357117..2d1895d4 100644 > > > --- a/conf.c > > > +++ b/conf.c > > > @@ -357,7 +357,7 @@ static uint8_t conf_ip4_prefix(const char *arg) > > > struct in_addr mask; > > > unsigned long len; > > > =20 > > > - if (inet_pton(AF_INET, arg, &mask)) { > > > + if (parse_ipv4(&p, &mask) && parse_eoi(p)) { > > > in_addr_t hmask =3D ntohl(mask.s_addr); > > > len =3D __builtin_popcount(hmask); > > > if ((hmask << len) =3D=3D 0) > > > @@ -1577,7 +1577,9 @@ void conf(struct ctx *c, int argc, char **argv) > > > if (addr_has_prefix_len && prefix_len_from_opt) > > > die("Redundant prefix length specification"); > > > =20 > > > - if (!addr_has_prefix_len && !inany_pton(optarg, &addr)) > > > + p =3D optarg; > > > + if (!addr_has_prefix_len && > > > + !(parse_inany(&p, &addr) && parse_eoi(p))) =20 > >=20 > > parse_port_range() reports error if the input isn't NULL-terminated, > > but these new functions don't, and in all the usages at least > > introduced in this patch, you always need to couple that with a && > > parse_eoi(p) (or && !*p). > >=20 > > Is there a reason for this difference? > >=20 > > > die("Invalid address: %s", optarg); > > > =20 > > > if (prefix_len_from_opt && inany_v4(&addr)) > > > diff --git a/fwd_rule.c b/fwd_rule.c > > > index 0b85b17e..6d7ec2c5 100644 > > > --- a/fwd_rule.c > > > +++ b/fwd_rule.c > > > @@ -595,6 +595,8 @@ void fwd_rule_parse(char optname, bool del, const= char *optarg, > > > strncpy(buf, optarg, sizeof(buf) - 1); > > > =20 > > > if ((spec =3D strchr(buf, '/'))) { > > > + const char *p =3D buf; > > > + > > > *spec =3D 0; > > > spec++; > > > =20 > > > @@ -616,22 +618,11 @@ void fwd_rule_parse(char optname, bool del, con= st char *optarg, > > > } > > > } > > > =20 > > > - if (ifname =3D=3D buf + 1) { /* Interface without address */ > > > + if (ifname =3D=3D buf + 1 || /* Interface without address */ > > > + !strcmp(buf, "*")) /* Explicit wildcard address */ > > > addr =3D NULL; > > > - } else { > > > - char *p =3D buf; > > > - > > > - /* Allow square brackets for IPv4 too for convenience */ > > > - if (*p =3D=3D '[' && p[strlen(p) - 1] =3D=3D ']') { > > > - p[strlen(p) - 1] =3D '\0'; > > > - p++; > > > - } > > > - > > > - if (strcmp(p, "*") =3D=3D 0) > > > - addr =3D NULL; > > > - else if (!inany_pton(p, &addr_buf)) > > > - die("Bad forwarding address '%s'", p); > > > - } > > > + else if (!parse_inany(&p, &addr_buf) && parse_eoi(p)) > > > + die("Bad forwarding address '%s'", buf); > > > } else { > > > spec =3D buf; > > > =20 > > > diff --git a/inany.c b/inany.c > > > index 23faf3ff..154f08b5 100644 > > > --- a/inany.c > > > +++ b/inany.c > > > @@ -27,6 +27,7 @@ > > > #include "ip.h" > > > #include "inany.h" > > > #include "fwd.h" > > > +#include "parse.h" > > > =20 > > > const union inany_addr inany_loopback4 =3D INANY_INIT4(IN4ADDR_LOOPB= ACK_INIT); > > > const union inany_addr inany_any4 =3D INANY_INIT4(IN4ADDR_ANY_INIT); > > > @@ -70,26 +71,6 @@ const char *inany_ntop(const union inany_addr *src= , char *dst, socklen_t size) > > > return inet_ntop(AF_INET6, &src->a6, dst, size); > > > } > > > =20 > > > -/** inany_pton - Parse an IPv[46] address from text format > > > - * @src: IPv[46] address > > > - * @dst: output buffer, filled with parsed address > > > - * > > > - * Return: on success, 1, if no parseable address is found, 0 > > > - */ > > > -int inany_pton(const char *src, union inany_addr *dst) > > > -{ > > > - if (inet_pton(AF_INET, src, &dst->v4mapped.a4)) { > > > - memset(&dst->v4mapped.zero, 0, sizeof(dst->v4mapped.zero)); > > > - memset(&dst->v4mapped.one, 0xff, sizeof(dst->v4mapped.one)); > > > - return 1; > > > - } > > > - > > > - if (inet_pton(AF_INET6, src, &dst->a6)) > > > - return 1; > > > - > > > - return 0; > > > -} > > > - > > > /** > > > * inany_prefix_pton() - Parse an IPv[46] address with prefix length > > > * @src: IPv[46] address and prefix length string in CIDR format > > > @@ -104,6 +85,7 @@ int inany_prefix_pton(const char *src, union inany= _addr *dst, > > > char astr[INANY_ADDRSTRLEN] =3D { 0 }; > > > size_t alen =3D strcspn(src, "/"); > > > const char *pstr =3D &src[alen + 1]; > > > + const char *p =3D astr; > > > unsigned long plen; > > > char *end; > > > =20 > > > @@ -129,7 +111,7 @@ int inany_prefix_pton(const char *src, union inan= y_addr *dst, > > > return 1; > > > } > > > =20 > > > - if (inany_pton(astr, dst)) { > > > + if (parse_inany(&p, dst) && parse_eoi(p)) { > > > if (plen > 32) > > > return 0; > > > *prefix_len =3D plen + 96; > > > diff --git a/inany.h b/inany.h > > > index 6bf3ecaa..93d98368 100644 > > > --- a/inany.h > > > +++ b/inany.h > > > @@ -303,7 +303,6 @@ static inline int inany_from_sockaddr(union inany= _addr *dst, in_port_t *port, > > > =20 > > > bool inany_matches(const union inany_addr *a, const union inany_addr= *b); > > > const char *inany_ntop(const union inany_addr *src, char *dst, sockl= en_t size); > > > -int inany_pton(const char *src, union inany_addr *dst); > > > int inany_prefix_pton(const char *src, union inany_addr *dst, > > > uint8_t *prefix_len); > > > =20 > > > diff --git a/parse.c b/parse.c > > > index bd091fde..0349c5dc 100644 > > > --- a/parse.c > > > +++ b/parse.c > > > @@ -16,9 +16,12 @@ > > > #include > > > #include > > > #include > > > +#include > > > +#include > > > =20 > > > #include "common.h" > > > #include "parse.h" > > > +#include "inany.h" > > > =20 > > > /** > > > * DOC: Theory of Operation > > > @@ -110,3 +113,91 @@ bool parse_port_range(const char **cursor, struc= t port_range *range) > > > *cursor =3D p; > > > return true; > > > } > > > + > > > +/** > > > + * parse_ipv4() - Parse an IPv4 address from a string > > > + * @abuf: On success, updated with parsed address > > > + */ > > > +bool parse_ipv4(const char **cursor, struct in_addr *abuf) > > > +{ > > > + /* Brackets are not typical on IPv4, but allow for consistency */ > > > + const char *p =3D *cursor; > > > + bool bracket =3D parse_literal(&p, "["); > > > + char buf[INET_ADDRSTRLEN]; > > > + struct in_addr addr; > > > + size_t len; > > > + > > > + if (bracket) > > > + len =3D strcspn(p, "]"); > > > + else > > > + len =3D strspn(p, "0123456789."); > > > + > > > + if (len >=3D sizeof(buf)) > > > + return false; > > > + memcpy(buf, p, len); > > > + buf[len] =3D '\0'; > > > + p +=3D len; > > > + > > > + if (!inet_pton(AF_INET, buf, &addr)) > > > + return false; > > > + > > > + if (bracket && !parse_literal(&p, "]")) > > > + return false; > > > + > > > + *cursor =3D p; > > > + *abuf =3D addr; > > > + return true; > > > +} > > > + > > > +/** > > > + * parse_ipv6() - Parse an IPv6 address from a string > > > + * @abuf: On success, updated with parsed address > > > + */ > > > +static bool parse_ipv6(const char **cursor, struct in6_addr *abuf) > > > +{ > > > + const char *p =3D *cursor; > > > + bool bracket =3D parse_literal(&p, "["); > > > + char buf[INET6_ADDRSTRLEN]; > > > + struct in6_addr addr; > > > + size_t len; > > > + > > > + if (bracket) > > > + len =3D strcspn(p, "]"); > > > + else > > > + len =3D strspn(p, "0123456789aAbBcCdDeEfF:."); > > > + > > > + if (len >=3D sizeof(buf)) > > > + return false; > > > + memcpy(buf, p, len); > > > + buf[len] =3D '\0'; > > > + p +=3D len; > > > + > > > + if (!inet_pton(AF_INET6, buf, &addr)) > > > + return false; > > > + > > > + if (bracket && !parse_literal(&p, "]")) > > > + return false; > > > + > > > + *cursor =3D p; > > > + *abuf =3D addr; > > > + return true; > > > +} > > > + > > > +/** > > > + * parse_inany() - Parse an IPv4 or IPv6 address from a string > > > + * @addr: On success, updated with parsed address > > > + */ > > > +bool parse_inany(const char **cursor, union inany_addr *addr) > > > +{ > > > + struct in_addr a4; > > > + > > > + if (parse_ipv6(cursor, &addr->a6)) > > > + return true; > > > + > > > + if (parse_ipv4(cursor, &a4)) { > > > + *addr =3D inany_from_v4(a4); > > > + return true; > > > + } > > > + > > > + return false; > > > +} > > > diff --git a/parse.h b/parse.h > > > index 155b3995..2820a065 100644 > > > --- a/parse.h > > > +++ b/parse.h > > > @@ -9,6 +9,8 @@ > > > #include > > > #include > > > =20 > > > +union inany_addr; > > > + > > > /** > > > * port_range() - Represents a non-empty range of ports > > > * @first: First port number in the range > > > @@ -24,5 +26,7 @@ bool parse_literal(const char **cursor, const char = *lit); > > > bool parse_eoi(const char *cursor); > > > bool parse_unsigned(const char **cursor, int base, unsigned long *va= lp); > > > bool parse_port_range(const char **cursor, struct port_range *range); > > > +bool parse_ipv4(const char **cursor, struct in_addr *abuf); > > > +bool parse_inany(const char **cursor, union inany_addr *addr); > > > =20 > > > #endif /* _PARSE_H */ =20 >=20 > --=20 > Stefano >=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 --geTAWwi7VpdE8ype Content-Type: application/pgp-signature; name=signature.asc -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEO+dNsU4E3yXUXRK2zQJF27ox2GcFAmpEcOcACgkQzQJF27ox 2GcK6hAAiAfY9ZYBVgeAgU+CQEUWgiNrbqYYmb1RMlKd5EVC1Fp5PWZaqxTSWPS1 kaXRAtPWGQ2jrzVxnSpRU8oSsX8Rmd4K7eyVGsQNfnbCMV2bti99mYLNlzhcT2Ej B3KFmNVtdaKFHQgtjFOke80qs25ACShV3HS7vxZvedXoJgvw0L/ERNHanE1ccKls U6+JZ3aieKFQbiqFn0FO1FQ06ArF6CHtT+8mPQS81BczXo1TUH+h9PlSbjgP6t9e fN9DF7swRBDyu+OLL72XEgsTzmHxfGFolysSHfxJg6vAkKuHG10jPBDUw9ov2thu 468x0rw6LtMX5U+W3MU2ooCM0PzPSqrC15poLOJ+v6q7KsJ5W+j8ez+trnuo4423 n3NXBI/Rd5PQOXgvV03e8FNyuzIZTcq6fhI+fb2OqIPmAEQr7dSk6WXwpMQqhzep VOnPrmJau0QC51UqfPKf9klxDPs/pxSdQpQixw8/LCPLovxasc2gmcptePJT42b9 de9x7RNjAYB08/1yGDhsGhuFWiS5cCyMSxGx1HZIcO2yDKmjLtG5nkDOKhcLHk1F O4BpQpbj5IXhL3Ev35uPCGI8wA35/Dznx2+ZajsqB37eFFeZE+0KRRqWmZ4YX1M7 YrFCoKux/P6hfqzQUZbqM5Q+fUNcpWWxsOJ7MKPx1iLxBydxMBE= =BNn7 -----END PGP SIGNATURE----- --geTAWwi7VpdE8ype--