From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: passt.top; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=KAw1o9oz; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by passt.top (Postfix) with ESMTPS id 42E4F5A0271 for ; Tue, 10 Feb 2026 18:40:46 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770745245; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=t5/AQFIferhVrc28M9BSoyCd7uyHhLfNlgPRW+BbD3k=; b=KAw1o9ozFnEjiMrnz7Y4rRBIhEF/nOjHgZKvW8Crq6sUVA7IhpMaHXj9kZ4hnGHLmf/N58 nNYnr2uTIiX/Dbe7CU4mMUNgxfP/xhuWyMKNNDi1PNvu+kuE+nTpjsWqFW3YPRSqzgyg7g 4hDB1xpqyHCzfS9vmePGIWrODepd9Sc= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-653-5oXH1fd9O1WfoXTw9gBCdw-1; Tue, 10 Feb 2026 12:40:42 -0500 X-MC-Unique: 5oXH1fd9O1WfoXTw9gBCdw-1 X-Mimecast-MFC-AGG-ID: 5oXH1fd9O1WfoXTw9gBCdw_1770745241 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-4806b12ad3fso59147185e9.0 for ; Tue, 10 Feb 2026 09:40:41 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770745241; x=1771350041; h=date:content-transfer-encoding:mime-version:organization:references :in-reply-to:message-id:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=t5/AQFIferhVrc28M9BSoyCd7uyHhLfNlgPRW+BbD3k=; b=Mcefduv7/G3QIQA6pPle1LEY2XrXgIL8+L5DRFWmhGQgC4apVpFsZK9cO2lbZ3mhTX r+Ba5Ltz08w+gyAFZAC26lncf5MOTowE/Pht0CzNMq8mbqt4wkj7Jic7y6EGwhuaf7gv bS0EId9T/KF51h6SV+Cg/U08Np8/KplZEyhitbOQMXIciQ2QOZXiDkYgdixWpHiYJZzl +yK8gYpZjCQ3DgyZ1HJixKCptZrJfJtQcmPlQQTSOiRh9B5hCafU49bas3eBTQYe5uNk x1bjbmPCph5QE93mUDSPnrV/rnr5jSzjSQkudQ6Ii7GgnKp2C1k2io4rdLzUE+jmgBYz JgVA== X-Forwarded-Encrypted: i=1; AJvYcCU8TVHuL4qF30z9xwatmeLaSZybudsYWngY1e0SlydXOsAcsd5h1Bkc+PLMTcS6mxv6MZ/5indyuT8=@passt.top X-Gm-Message-State: AOJu0Yyk3Pa+hFC+uXcOkeFEcivoOiYtUdd52FH5oRJ1kM4Qj38EKW1+ 2FiFSXvMQ7FlQCtFbqhMgPxS4l77wFCa8ON25TTndMXTQIMzrhCZNkqhQbw4dLrboTUVUQ+iQvO ga5ECWWgLwF7CR/soYCcTi9oc3cQhtOi+XgSmvgZBykb0k0hBBAUPyA== X-Gm-Gg: AZuq6aJtCUeSU5zxBQHOs3/d9TYxCzV506Mb/0I1uWrsqGOPx7WH1AtVYDhsYS7d/oS YXRTI7un1cPIiKJhwbB9EOZriQUgJOKaTDjy5HnLvG3ZEjLrnfP33Nn26gtKO2NbkRS2ud8kk+O aR3YLbgc9Vmb53ziha7w+on8v+rMt3FMech1dcEYSQBXNpUMb/ceJxS2bYR6E3VSgC2FmA3xOEQ st5xuPm0hazXl/mHuHJ8AyMe9Tp8S4/T73aCj/H4YiOORxxz/Ph68XY+3HgqHy50HYrc6UfxeuK LD22rjz75UEPu6y1ohQDc1DY/nRPmJWMsnzbYXAtUm6eAhIr/bu2SOGLaq1HKWPh2ZMHGTtWuYY gSrWt4AT01v0g+/mZMEKAgB5d2YmsekL9 X-Received: by 2002:a05:600c:35d2:b0:477:afc5:fb02 with SMTP id 5b1f17b1804b1-4835081e900mr52361165e9.21.1770745240490; Tue, 10 Feb 2026 09:40:40 -0800 (PST) X-Received: by 2002:a05:600c:35d2:b0:477:afc5:fb02 with SMTP id 5b1f17b1804b1-4835081e900mr52360725e9.21.1770745239910; Tue, 10 Feb 2026 09:40:39 -0800 (PST) Received: from maya.myfinge.rs (ifcgrfdd.trafficplex.cloud. [2a10:fc81:a806:d6a9::1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4834d5d77b3sm83011285e9.2.2026.02.10.09.40.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 09:40:39 -0800 (PST) From: Stefano Brivio To: Jon Maloy Subject: Re: [PATCH v9] conf: Support CIDR notation for -a/--address option Message-ID: <20260210184035.78a3afa1@elisabeth> In-Reply-To: <707f4e87-3d64-4289-9452-3f8272a3c31d@redhat.com> References: <20260209003727.813006-1-jmaloy@redhat.com> <20260210123638.7e839e2c@elisabeth> <707f4e87-3d64-4289-9452-3f8272a3c31d@redhat.com> Organization: Red Hat X-Mailer: Claws Mail 4.2.0 (GTK 3.24.49; x86_64-pc-linux-gnu) MIME-Version: 1.0 Date: Tue, 10 Feb 2026 18:40:38 +0100 (CET) X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: JGjWCySdWT4tjWK2rlG6m7EiGwv24leHdGNn5ZifgLw_1770745241 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Message-ID-Hash: 5XEJCDPNE2JLUKHCBPFLIHJORER6HRNH X-Message-ID-Hash: 5XEJCDPNE2JLUKHCBPFLIHJORER6HRNH X-MailFrom: sbrivio@redhat.com 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: dgibson@redhat.com, david@gibson.dropbear.id.au, 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: On Tue, 10 Feb 2026 11:39:39 -0500 Jon Maloy wrote: > On 2026-02-10 06:36, Stefano Brivio wrote: > > Note that this has now a trivial conflict with 0c611bcd3120 ("ip: Add > > ipproto_name() function"), you should rebase it. Review below: > > > > On Sun, 8 Feb 2026 19:37:27 -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. > >> > >> We add a new inany_prefix_pton() helper function that: > >> - Parses address strings with a compulsory /prefix_len suffix > >> - Validates prefix length based on address family (0-32 for IPv4, > >> 0-128 for IPv6), including handling of IPv4-to-IPv6 mapping case. > >> > >> 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. > >> > >> Also fix a bug in conf_ip4_prefix() that was incorrectly using the > >> global 'optarg' instead of its 'arg' parameter. > >> > >> Signed-off-by: Jon Maloy > >> > >> --- > >> v3: Fixes after feedback from Laurent, David and Stefano > >> Notably, updated man page for the -a option > >> > >> v4: Fixes based on feedback from David G: > >> - Handling prefix length adjustment when IPv4-to-IPv6 mapping > >> - Removed redundant !IN6_IS_ADDR_V4MAPPED(&addr.a6) test > >> - Simplified tests of acceptable address types > >> - Merged documentation and code commits > >> - Some documentation text clarifications > >> > >> v5: - Moved address/prefix parsing into a refactored > >> inany_prefix_pton() function. > >> - inany_prefix_pton() now only caluclates IPv6 style > >> prefix lengths > >> - Stricter distinction between error causes. > >> - Some refactoring of the 'case a:' branch in conf() > >> - Some small fixes in passt.1 > >> > >> v6: - Refactored inany_prefix_pton() and conf()::'case -a' > >> code after input from David Gibson. > >> > >> v7: - More refactoring after input from David Gibson. > >> - I kept the return values 1 and 0. This is consistent > >> with the return values of inet_pton() and inany_pton(). > >> > >> v8: - Changed condition for updating ipv4 prefix length > >> > >> v9: - Made char *src and char *pstr in inany_prefix_pton() const > >> - Updated logics in conf.c:: case 'a' and case 'n' to > >> be clearer (I think) > >> --- > >> conf.c | 72 +++++++++++++++++++++++++++++++++++++-------------------- > >> inany.c | 51 ++++++++++++++++++++++++++++++++++++++++ > >> inany.h | 15 ++++++++++++ > >> ip.c | 21 +++++++++++++++++ > >> ip.h | 2 ++ > >> passt.1 | 17 ++++++++++---- > >> 6 files changed, 148 insertions(+), 30 deletions(-) > >> > >> diff --git a/conf.c b/conf.c > >> index 2942c8c..46cfb6e 100644 > >> --- a/conf.c > >> +++ b/conf.c > >> @@ -682,7 +682,7 @@ static int conf_ip4_prefix(const char *arg) > >> return -1; > >> } else { > >> errno = 0; > >> - len = strtoul(optarg, NULL, 0); > >> + len = strtoul(arg, NULL, 0); > >> if (len > 32 || errno) > >> return -1; > >> } > >> @@ -896,7 +896,7 @@ static void usage(const char *name, FILE *f, int status) > >> " 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" > >> @@ -1505,6 +1505,8 @@ void conf(struct ctx *c, int argc, char **argv) > >> unsigned long max_mtu = IP_MAX_MTU; > >> struct fqdn *dnss = c->dns_search; > >> unsigned int ifi4 = 0, ifi6 = 0; > >> + bool prefix_from_cidr = false; > >> + uint8_t prefix_from_opt = 0; > > > > Neither of these variables contain a prefix. One might be the length, I > > think, the other one is a boolean. This is a bit confusing. > ok > > > > >> const char *logfile = NULL; > >> size_t logsize = 0; > >> char *runas = NULL; > >> @@ -1808,36 +1810,56 @@ void conf(struct ctx *c, int argc, char **argv) > >> c->mtu = 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)) { > >> - if (c->mode == MODE_PASTA) > >> - c->ip6.no_copy_addrs = true; > >> - break; > >> - } > >> + case 'a': { > >> + union inany_addr addr; > >> + uint8_t prefix_len; > >> + > >> + prefix_from_cidr = > >> + inany_prefix_pton(optarg, &addr, &prefix_len); > > > > I guess this variable represents whether the address specification > > carries the prefix length as well, so perhaps addr_has_prefix or > > addr_has_prefix_len would suit it better. > > > > The current name seems to indicate a variable containing a prefix that > > comes from RFC 1519 somehow, but that's clearly not the case. > > > > By the way, the usual indentation would be: > > > > long_variable_name = inany_prefix_pton(optarg, &addr, > > &prefix_len); > > > >> + > >> + if (prefix_from_cidr && prefix_from_opt) > >> + die("Can't mix CIDR with -n"); > > > > Well, sure, CIDR means "Classless Inter-Domain Routing", so that would > > be like mixing bridges with concrete. What about: > > > > die("Redundant prefix length specification"); > > ? > > ok. > > > > > Sorry for not mentioning it earlier, but only now I realised that the > > current implementation is not really trivial: with commit 65923ba79877 > > ("conf: Accept duplicate and conflicting options, the last one wins"), > > we finally embraced reality over correctness and gave up with all the > > careful "conflict" checking. > > Since we are introducing something new here wa have the freedom to deny > mixing of the options, which I think is what we should do here. Sure, we can, maybe even should, just see one additional remark below. > > Couldn't we just let the last option win? "-n" would override > > everything, while per-address specifiers would override the prefix > > length only for that specific address / IP version. The code would also > > be simpler, I think. > > -n cannot be automatically correlated to any particular address, so the > logical > thing to do would be to overrwite the prefix length in all ipv4 addresses. My suggestion, based on the approach we eventually adopted for other options (not my initial choice either, see that commit I mentioned) would have been that -n overwrites the prefix length of all the *preceding* addresses, so that especially Podman users (and libvirt and rootlesskit users, hopefully, one day) will be able to deterministically override whatever was implicitly configured (by e.g. Podman). That is: > Imagine the surprise of the user when he configures > pasta -a -n prefix_len1 -a /prefix_len2 and finds that > addr2 now is associated with prefix_1. this would be equivalent to -a addr1/prefix_len1 -a addr2/prefix_len2, instead, and: - -a a1/p1 -n p2 -a a3/p3 => -a a1/p2 -a a3/p3 - -a a1/p1 -n p2 -a a3/p3 -n p4 => -a a1/p4 -a a3/p4 and so on. That is, the last -n option wins, everything else remains the same. > I simply think this is a bad idea. Maybe. I'm not insisting, of course, but still I think that, with the interpretation I gave above, it's not *that* surprising and it saves many lines of code (not adding any to the man page, at the same time). > >> + > >> + if (!prefix_from_cidr && !inany_pton(optarg, &addr)) > >> + die("Invalid address: %s", optarg); > >> + > >> + if (prefix_from_opt && inany_v4(&addr)) > >> + prefix_len = prefix_from_opt; > > > > Now I see why prefix_from_opt is a number! If that variable name had > > 'len' in it, it would have been clearer. > > > > I haven't checked how this looks like elsewhere, but what about > > 'prefix_len_from_opt'? > > ok. I'll find a new name. > > > > >> + else if (!prefix_from_cidr) > >> + 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", optarg); > >> > >> - 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)) { > >> + if (inany_v4(&addr)) { > >> + c->ip4.addr = *inany_v4(&addr); > >> + c->ip4.prefix_len = prefix_len - 96; > >> if (c->mode == MODE_PASTA) > >> c->ip4.no_copy_addrs = true; > >> - break; > >> + } else { > >> + c->ip6.addr = addr.a6; > >> + if (c->mode == MODE_PASTA) > >> + c->ip6.no_copy_addrs = true; > >> } > >> - > >> - die("Invalid address: %s", optarg); > >> break; > >> - case 'n': > >> - c->ip4.prefix_len = conf_ip4_prefix(optarg); > >> - if (c->ip4.prefix_len < 0) > >> - die("Invalid netmask: %s", optarg); > >> + } > >> + case 'n': { > >> + int plen; > >> + > >> + if (prefix_from_cidr) > >> + die("Can't use both -n and CIDR prefix length"); > >> > >> + plen = conf_ip4_prefix(optarg); > >> + if (plen < 0) > >> + die("Invalid prefix length: %s", optarg); > >> + > >> + prefix_from_opt = plen + 96; > >> + c->ip4.prefix_len = plen; > >> break; > >> + } > >> case 'M': > >> parse_mac(c->our_tap_mac, optarg); > >> break; > >> diff --git a/inany.c b/inany.c > >> index 7680439..df6a126 100644 > >> --- a/inany.c > >> +++ b/inany.c > >> @@ -11,6 +11,7 @@ > >> #include > >> #include > >> #include > >> +#include > >> > >> #include "util.h" > >> #include "ip.h" > >> @@ -57,3 +58,53 @@ int inany_pton(const char *src, union inany_addr *dst) > >> > >> return 0; > >> } > >> + > >> +/** > >> + * 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: on success, 1, if no parseable address or prefix is found, 0 > > > > I already commented on this. I think it's not clear. See > > https://archives.passt.top/passt-dev/20260121091517.626962f1@elisabeth/. > > Following the link, I see nothing commenting on the return value. What > do you mean? Quoting from that message (the function was already called inany_prefix_pton(), and git helpfully includes enough code context): --- > + * Return: on success, 1, if no parseable address is found, 0 I think it's a bit difficult to read like this, what about "1 on success, 0 if no parsable address is found"? --- -- Stefano