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=HVrecDeY; dkim-atps=neutral Received: from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3]) by passt.top (Postfix) with ESMTPS id 748A25A026E 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=o/fq0DKlSdalSJkXBoF9q5KBE0TyDHPXZ5JKuxstcms=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=HVrecDeYtoCvtikJmm0v8ANWiL9DqbljNq6e0HpR3GUz8cqiRqT6lBVG0aOsWWwsb WveV43f5EnwvOo8ihume6W0FqotrSzMiLtPBM3jAp9IFePHqF0smJrTbS+YHBM3F/t jEg82ahzcTnIbjcm+jj28s+oAVPc6vDVDh2ufTVL/CwUG9fW9PeCCiLCQ5pGE3J4RF ZYk2toNy1wWDbslDU3o5+ZCyEDxN4DoeZbb+++P5oD/CKMxZB6p1e969H4PNVtwTYk 03v39S34nwii0W9CHMvbvnRwZQp60ZZ0Oz8HD4KBmMp6mKfA7hZE2ddshsAq9IerQi 1tEEzwjBvfbBA== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4gqlGL3f68z4wTs; Wed, 01 Jul 2026 13:03:54 +1000 (AEST) Date: Wed, 1 Jul 2026 11:43:42 +1000 From: David Gibson To: Stefano Brivio Subject: 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> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="AiZEgxmJah9v6ZjQ" Content-Disposition: inline In-Reply-To: <20260701020725.024edf81@elisabeth> Message-ID-Hash: PM7PB5V6GGAP2PUOZZJTEBG5VXCL3GVY X-Message-ID-Hash: PM7PB5V6GGAP2PUOZZJTEBG5VXCL3GVY 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: --AiZEgxmJah9v6ZjQ Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Wed, Jul 01, 2026 at 02:07:26AM +0200, Stefano Brivio wrote: > 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 > > 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=3Dun= usedFunction:inany.c > > pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3DunusedFunction:ip.h > > pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3DunusedFunction:serial= ise.c > > pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3DstaticFunction:fwd_ru= le.c > > +pesto.cppcheck: CPPCHECK_FLAGS +=3D --suppress=3DstaticFunction:parse.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 > parse_port_range() reports error if the input isn't NULL-terminated, Not after 7/12 it doesn't. > 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? Checking for \0 within the function means it can't be used to parse an IP as part of something longer, they can only work at the end of a string. The simple users in this patch only need an IP at the end of a string, but the more complex uses for -[tu] don't and mustn't follow with parse_eoi(). Basically, checking for \0 within the parse function breaks composability, which is the whole point of this interface style. > > 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 c= har *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, const= 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_LOOPBAC= K_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_a= ddr *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 inany_= 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_a= ddr *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, socklen= _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, struct = 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 *l= it); > > bool parse_eoi(const char *cursor); > > bool parse_unsigned(const char **cursor, int base, unsigned long *valp= ); > > 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 > 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 --AiZEgxmJah9v6ZjQ Content-Type: application/pgp-signature; name=signature.asc -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEO+dNsU4E3yXUXRK2zQJF27ox2GcFAmpEcMMACgkQzQJF27ox 2GdyohAAnLnu9ttTmNGVyHsz9JdyJ8p3nx6jwnt905hZEoQFk2PHhv5lR24RzDh4 0VQvhcq85oBZjKtEyZOkruBvNPM/lF4y/Eju+c9BsEdKUkh4cnPec46eilPHNQ3N SBnKV+tqt12yELW4ZQXBSzWE7tc51DaPEWdcK1W4uPVdQNanlO1nHje1AhbEQzSp A6I8FE6z0Y02UVL7u+OzaK6X9u8wDKM+ALnxwETUFEnnAjxZlkocGz3zuxSJaVf5 axEe+dzNiLADkhfk5w0pEWJjprNqbvkoX6ZIfvFuEZ0Cin2t/4lCkdd5TgfN7AAM utqN1Pze/etKoaiBy/30TmhEzYDY/CCc2MTXEEMiz0PLIm2dYc6P8hMIhK8z+nRa cng1lQkhFLl78U3D2rW6CrS4LUqzr+Okghl86YATXa1d6jjbZvk868T+Z4SXELuT kNQSEYe+fZu9sdmetU7rTj2hBh4M6E2geWTnAl5pFetAwnMpjISMTkxblGSZLZwR hwztBtx2+ZsG8rNyb22T42xpARMj4kQ6qj2zEt97zkts+NjfTlE+dZrF7JwEzza1 +8/4+2s9Z7nfmfwCC71OumfITDuJgERc7vfa5ZHB+/O9GpwCh39qeHQ83JWQZZZF yqqnMhBcz0mD6qiMA1XbfSZQzdwDC9spUPYGfesMGqyCqff1KVg= =BWzw -----END PGP SIGNATURE----- --AiZEgxmJah9v6ZjQ--