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=A0PdpLp7; dkim-atps=neutral Received: from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3]) by passt.top (Postfix) with ESMTPS id 063BC5A0624 for ; Fri, 26 Jun 2026 09:10:19 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202606; t=1782457808; bh=HdxSFE1f20O7zv8TyDTaWfQeOH/pSq0OexDORxB0Vgk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=A0PdpLp7gmO0BC2829CWfWR4bOQTd6RsO8fQQruuPoJZWD8raQdhjBoOSBHmZKv1t qmMD5Yi+QfHgYMtBCc3rZJzalD4gYQ+g0QPgxVtuUBh3fsUfwY/4krOr3koXNu2gHi shfNepbxrOTJSSszgiYH6bmS18V8Q5M4bTbsiIsUmo7iQEc/+eybIPPvBknOCM74Pz tqQtVh0wDELJ1NzNe8Ljk5pufvRqCPHr+xU1l4CKMsWkwrp4l2nloOe9QhnlNeAYzH V/88dTIRcv70RRBLZ1rYIHuL35pH552zpujTu88UPwMQUb12GJeX1soBKce7dBk75A ECQXsePG937XA== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4gmmym694Yz4wxB; Fri, 26 Jun 2026 17:10:08 +1000 (AEST) From: David Gibson To: passt-dev@passt.top, Stefano Brivio Subject: [PATCH 10/12] conf: Use new parsing tools to handle -a option Date: Fri, 26 Jun 2026 17:10:01 +1000 Message-ID: <20260626071003.3472194-11-david@gibson.dropbear.id.au> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260626071003.3472194-1-david@gibson.dropbear.id.au> References: <20260626071003.3472194-1-david@gibson.dropbear.id.au> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: M23CUAVGH5TVKBNNKEEV7S7BT2BKTVF7 X-Message-ID-Hash: M23CUAVGH5TVKBNNKEEV7S7BT2BKTVF7 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: David Gibson , 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: The -a command line option can take either an address prefix, or a bare address. Current parsing of this is pretty awkward, using the special purpose helper inany_prefix_pton(). With the new incremental parsing helpers this can be done more naturally. Rework it to use them. This does requiring extending parse_inany() to parse_inany_() which also reports the format of the address as parse, as opposed to the family of the resulting address. This is so that ::ffff:192.0.1.1/112 will be correctly interpreted the same as 192.0.1.1/16, rather than the nonsensical 192.0.0.1/112. Cc: Jon Maloy Signed-off-by: David Gibson --- Makefile | 1 - conf.c | 63 +++++++++++++++++++++++++++++++++++--------------------- inany.c | 50 -------------------------------------------- inany.h | 2 -- parse.c | 17 ++++++++++++--- parse.h | 5 ++++- 6 files changed, 58 insertions(+), 80 deletions(-) diff --git a/Makefile b/Makefile index e2b22ddf..5757aeff 100644 --- a/Makefile +++ b/Makefile @@ -223,7 +223,6 @@ passt-repair.cppcheck: $(PASST_REPAIR_SRCS) $(PASST_REPAIR_HEADERS) seccomp_repa pesto.cppcheck: BASE_CPPFLAGS += -DPESTO pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:bitmap.c pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:inany.h -pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:inany.c pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:ip.h pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:serialise.c pesto.cppcheck: CPPCHECK_FLAGS += --suppress=staticFunction:fwd_rule.c diff --git a/conf.c b/conf.c index 3614776c..ff7ca5c7 100644 --- a/conf.c +++ b/conf.c @@ -1176,43 +1176,60 @@ int conf_tap_fd(const char *arg) */ static bool conf_addr(struct ctx *c, char *arg, uint8_t opt_n) { + unsigned long prefix_len; + const struct in_addr *a4; union inany_addr addr; + sa_family_t parseaf; const char *p = arg; - uint8_t prefix_len; bool is_prefix; - is_prefix = inany_prefix_pton(arg, &addr, &prefix_len); - - if (is_prefix && opt_n) - die("Redundant prefix length specification"); - - if (!is_prefix && - !(parse_inany(&p, &addr) && parse_eoi(p))) - die("Invalid address: %s", arg); - - if (opt_n && inany_v4(&addr)) - prefix_len = opt_n; - else if (!is_prefix) - prefix_len = inany_default_prefix_len(&addr); + if (!parse_inany_(&p, &addr, &parseaf)) + goto bad; + a4 = inany_v4(&addr); + + if ((is_prefix = parse_literal(&p, "/"))) { + /* Prefix length included in -a option */ + if (!parse_unsigned(&p, 10, &prefix_len)) + goto bad; + if (opt_n) + die("Redundant prefix length specification"); + if (parseaf == AF_INET) { + if (prefix_len > 32) + goto bad_prefix; + prefix_len += 96; + } else if (prefix_len > 128) { + goto bad_prefix; + } + } else { + /* Get prefix length from elsewhere */ + if (opt_n && a4) + prefix_len = opt_n; + else + prefix_len = inany_default_prefix_len(&addr); + } - if (inany_is_unspecified(&addr) || inany_is_multicast(&addr) || - inany_is_loopback(&addr) || IN6_IS_ADDR_V4COMPAT(&addr.a6)) - die("Invalid address: %s", arg); + if (!parse_eoi(p) || + !inany_is_unicast(&addr) || + inany_is_loopback(&addr)) + goto bad; - if (inany_v4(&addr)) { - c->ip4.addr = *inany_v4(&addr); + if (a4) { + c->ip4.addr = *a4; c->ip4.prefix_len = prefix_len - 96; c->ip4.addr_fixed = true; - if (c->mode == MODE_PASTA) - c->ip4.no_copy_addrs = true; + c->ip4.no_copy_addrs = true; } else { c->ip6.addr = addr.a6; c->ip6.addr_fixed = true; - if (c->mode == MODE_PASTA) - c->ip6.no_copy_addrs = true; + c->ip6.no_copy_addrs = true; } return is_prefix; + +bad_prefix: + die("Invalid prefix length: %s", arg); +bad: + die("Invalid guest address: %s", arg); } /** diff --git a/inany.c b/inany.c index 154f08b5..120c9387 100644 --- a/inany.c +++ b/inany.c @@ -70,53 +70,3 @@ const char *inany_ntop(const union inany_addr *src, char *dst, socklen_t size) return inet_ntop(AF_INET6, &src->a6, dst, size); } - -/** - * inany_prefix_pton() - Parse an IPv[46] address with prefix length - * @src: IPv[46] address and prefix length string in CIDR format - * @dst: Output buffer, filled with parsed address - * @prefix_len: Prefix length, to be filled in IPv6 format - * - * Return: 1 on success, 0 if no parseable address or prefix is found - */ -int inany_prefix_pton(const char *src, union inany_addr *dst, - uint8_t *prefix_len) -{ - char astr[INANY_ADDRSTRLEN] = { 0 }; - size_t alen = strcspn(src, "/"); - const char *pstr = &src[alen + 1]; - const char *p = astr; - unsigned long plen; - char *end; - - if (alen >= INANY_ADDRSTRLEN) - return 0; - - if (src[alen] != '/') - return 0; - - strncpy(astr, src, alen); - - /* Read prefix length */ - errno = 0; - plen = strtoul(pstr, &end, 10); - if (errno || *end || plen > 128) - return 0; - - /* Read address */ - if (inet_pton(AF_INET6, astr, dst)) { - if (inany_v4(dst) && plen < 96) - return 0; - *prefix_len = plen; - return 1; - } - - if (parse_inany(&p, dst) && parse_eoi(p)) { - if (plen > 32) - return 0; - *prefix_len = plen + 96; - return 1; - } - - return 0; -} diff --git a/inany.h b/inany.h index 93d98368..5b176ccf 100644 --- a/inany.h +++ b/inany.h @@ -303,7 +303,5 @@ static inline int inany_from_sockaddr(union inany_addr *dst, in_port_t *port, 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_prefix_pton(const char *src, union inany_addr *dst, - uint8_t *prefix_len); #endif /* INANY_H */ diff --git a/parse.c b/parse.c index 0349c5dc..3e0dbd45 100644 --- a/parse.c +++ b/parse.c @@ -184,18 +184,29 @@ static bool parse_ipv6(const char **cursor, struct in6_addr *abuf) } /** - * parse_inany() - Parse an IPv4 or IPv6 address from a string + * parse_inany_() - Parse an IPv4 or IPv6 address from a string * @addr: On success, updated with parsed address + * @parseaf: On success, updated with the format of the parsed address + * + * @parseaf is updated to reflect the string format, not the final address + * family. So "::ffff:192.0.1.1", will set @parseaf to AF_INET6, despite being + * a IPv4-mapped address. */ -bool parse_inany(const char **cursor, union inany_addr *addr) +bool parse_inany_(const char **cursor, union inany_addr *addr, + sa_family_t *parseaf) { struct in_addr a4; - if (parse_ipv6(cursor, &addr->a6)) + if (parse_ipv6(cursor, &addr->a6)) { + if (parseaf) + *parseaf = AF_INET6; return true; + } if (parse_ipv4(cursor, &a4)) { *addr = inany_from_v4(a4); + if (parseaf) + *parseaf = AF_INET; return true; } diff --git a/parse.h b/parse.h index 2820a065..08b038cf 100644 --- a/parse.h +++ b/parse.h @@ -27,6 +27,9 @@ 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); +bool parse_inany_(const char **cursor, union inany_addr *addr, + sa_family_t *parseaf); + +#define parse_inany(cursor, addr) parse_inany_((cursor), (addr), NULL) #endif /* _PARSE_H */ -- 2.54.0