From: David Gibson <david@gibson.dropbear.id.au>
To: Jon Maloy <jmaloy@redhat.com>
Cc: sbrivio@redhat.com, dgibson@redhat.com, passt-dev@passt.top
Subject: Re: [RFC 03/12] conf: Allow multiple -a/--address options per address family
Date: Mon, 15 Dec 2025 20:53:02 +1100 [thread overview]
Message-ID: <aT_afgmf-Jf-FRUS@zatzit> (raw)
In-Reply-To: <20251215015441.887736-4-jmaloy@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 9324 bytes --]
On Sun, Dec 14, 2025 at 08:54:32PM -0500, Jon Maloy wrote:
> We enable configuration of multiple IPv4 and IPv6 addresses by allowing
> repeated use of the -a/--address option.
>
> - We update option parsing to append addresses to the addrs[] array.
>
> - Each address specified via -a does initially get a class-based default
> prefix.
>
> - If no -a option is given, address and prefix are inherited from
> the template interface, just like now.
>
> - The -n/--netmask option applies only to the first address, in addrs[0].
I know this is temporary, but this seems a really bad semantic.
Might be worth merging this patch with the -n patch.
> - We configure all indicated addresses in the namespace interface.
>
> Signed-off-by: Jon Maloy <jmaloy@redhat.com>
> ---
> conf.c | 74 ++++++++++++++++++++++++++++++++++++++-------------------
> pasta.c | 24 ++++++++++++++-----
> 2 files changed, 68 insertions(+), 30 deletions(-)
>
> diff --git a/conf.c b/conf.c
> index 31acc20..e9f217b 100644
> --- a/conf.c
> +++ b/conf.c
> @@ -741,7 +741,7 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4,
>
> ip4->our_tap_addr = ip4->guest_gw;
>
> - if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addrs[0].addr))
> + if (!ip4->addr_count)
> return 0;
>
> return ifi;
> @@ -756,6 +756,7 @@ static void conf_ip4_local(struct ip4_ctx *ip4)
> ip4->addr_seen = ip4->addrs[0].addr = IP4_LL_GUEST_ADDR;
> ip4->our_tap_addr = ip4->guest_gw = IP4_LL_GUEST_GW;
> ip4->addrs[0].prefix_len = IP4_LL_PREFIX_LEN;
> + ip4->addr_count = 1;
Doesn't this belong in an earlier patch?
Also, kind of pre-existing, but I don't think conf_ip4_local() should
overwrite an address specified with -a. I think we want to change
that to either be skipped if -a is used, or to add the link local
address to the ones given explicitly.
>
> ip4->no_copy_addrs = ip4->no_copy_routes = true;
> }
> @@ -810,8 +811,7 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6,
> if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw))
> ip6->our_tap_ll = ip6->guest_gw;
>
> - if (IN6_IS_ADDR_UNSPECIFIED(&ip6->addrs[0].addr) ||
> - IN6_IS_ADDR_UNSPECIFIED(&ip6->our_tap_ll))
> + if (!ip6->addr_count || IN6_IS_ADDR_UNSPECIFIED(&ip6->our_tap_ll))
> return 0;
>
> return ifi;
> @@ -903,9 +903,11 @@ static void usage(const char *name, FILE *f, int status)
> " 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"
> - " can be specified zero to two times (for IPv4 and IPv6)\n"
> + " can be specified multiple times (limit: %d IPv4, %d IPv6)\n"
man page might need an update too.
> " default: use addresses from interface with default route\n"
> - " -n, --netmask MASK Assign IPv4 MASK, dot-decimal or bits\n"
> + " -n, --netmask MASK Assign IPv4 MASK, dot-decimal or bits\n",
> + IP4_MAX_ADDRS, IP6_MAX_ADDRS);
> + FPRINTF(f,
> " default: netmask from matching address on the host\n"
> " -M, --mac-addr ADDR Use source MAC address ADDR\n"
> " default: 9a:55:9a:55:9a:55 (locally administered)\n"
> @@ -1159,9 +1161,11 @@ static void conf_print(const struct ctx *c)
> (32 - c->ip4.addrs[0].prefix_len));
>
> info("DHCP:");
> - info(" assign: %s",
> - inet_ntop(AF_INET, &c->ip4.addrs[0].addr,
> - buf4, sizeof(buf4)));
> + for (i = 0; i < (int)c->ip4.addr_count; i++) {
> + info(" assign: %s",
> + inet_ntop(AF_INET, &c->ip4.addrs[i].addr,
> + buf4, sizeof(buf4)));
> + }
This is misleading. We allow multiple addresses, but (at least as of
this patch) DHCP will only assign one of them. In fact I'm not sure
we can assign multiple addresses with DHCP, short of rarely-supported
extensions.
> info(" mask: %s",
> inet_ntop(AF_INET, &mask, buf4, sizeof(buf4)));
> info(" router: %s",
> @@ -1198,9 +1202,11 @@ static void conf_print(const struct ctx *c)
> else
> goto dns6;
>
> - info(" assign: %s",
> - inet_ntop(AF_INET6, &c->ip6.addrs[0].addr,
> - buf6, sizeof(buf6)));
> + for (i = 0; i < (int)c->ip6.addr_count; i++) {
> + info(" assign: %s",
> + inet_ntop(AF_INET6, &c->ip6.addrs[i].addr,
> + buf6, sizeof(buf6)));
> + }
Similar comments, though I think DHCPv6 can assign multiple addresses
(it's still not implemented as of this patch, though).
> info(" router: %s",
> inet_ntop(AF_INET6, &c->ip6.guest_gw, buf6, sizeof(buf6)));
> info(" our link-local: %s",
> @@ -1517,6 +1523,8 @@ void conf(struct ctx *c, int argc, char **argv)
> struct fqdn *dnss = c->dns_search;
> unsigned int ifi4 = 0, ifi6 = 0;
> const char *logfile = NULL;
> + struct in6_addr addr6;
> + struct in_addr addr4;
> size_t logsize = 0;
> char *runas = NULL;
> long fd_tap_opt;
> @@ -1821,23 +1829,41 @@ void conf(struct ctx *c, int argc, char **argv)
> break;
> }
> case 'a':
> - if (inet_pton(AF_INET6, optarg,
> - &c->ip6.addrs[0].addr) &&
> - !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addrs[0].addr) &&
> - !IN6_IS_ADDR_LOOPBACK(&c->ip6.addrs[0].addr) &&
> - !IN6_IS_ADDR_V4MAPPED(&c->ip6.addrs[0].addr) &&
> - !IN6_IS_ADDR_V4COMPAT(&c->ip6.addrs[0].addr) &&
> - !IN6_IS_ADDR_MULTICAST(&c->ip6.addrs[0].addr)) {
> + if (inet_pton(AF_INET6, optarg, &addr6) &&
> + !IN6_IS_ADDR_UNSPECIFIED(&addr6) &&
> + !IN6_IS_ADDR_LOOPBACK(&addr6) &&
> + !IN6_IS_ADDR_V4MAPPED(&addr6) &&
> + !IN6_IS_ADDR_V4COMPAT(&addr6) &&
> + !IN6_IS_ADDR_MULTICAST(&addr6)) {
> + unsigned int i = c->ip6.addr_count;
> +
> + if (i >= IP6_MAX_ADDRS)
> + die("Too many IPv6 addresses");
> +
> + c->ip6.addrs[i].addr = addr6;
> + c->ip6.addrs[i].prefix_len = 64;
> + c->ip6.addrs[i].permanent = true;
> + c->ip6.addr_count++;
> if (c->mode == MODE_PASTA)
> c->ip6.no_copy_addrs = true;
> break;
> }
>
> - if (inet_pton(AF_INET, optarg, &c->ip4.addrs[0].addr) &&
> - !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addrs[0].addr) &&
> - !IN4_IS_ADDR_BROADCAST(&c->ip4.addrs[0].addr) &&
> - !IN4_IS_ADDR_LOOPBACK(&c->ip4.addrs[0].addr) &&
> - !IN4_IS_ADDR_MULTICAST(&c->ip4.addrs[0].addr)) {
> + if (inet_pton(AF_INET, optarg, &addr4) &&
> + !IN4_IS_ADDR_UNSPECIFIED(&addr4) &&
> + !IN4_IS_ADDR_BROADCAST(&addr4) &&
> + !IN4_IS_ADDR_LOOPBACK(&addr4) &&
> + !IN4_IS_ADDR_MULTICAST(&addr4)) {
> + unsigned int i = c->ip4.addr_count;
> +
> + if (i >= IP4_MAX_ADDRS)
> + die("Too many IPv4 addresses");
> +
> + c->ip4.addrs[i].addr = addr4;
> + c->ip4.addrs[i].prefix_len =
> + ip4_default_prefix_len(&addr4);
> + c->ip4.addrs[i].permanent = true;
> + c->ip4.addr_count++;
> if (c->mode == MODE_PASTA)
> c->ip4.no_copy_addrs = true;
> break;
> @@ -2135,7 +2161,7 @@ void conf(struct ctx *c, int argc, char **argv)
> if (!c->ifi6) {
> c->no_ndp = 1;
> c->no_dhcpv6 = 1;
> - } else if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addrs[0].addr)) {
> + } else if (!c->ip6.addr_count) {
> c->no_dhcpv6 = 1;
> }
>
> diff --git a/pasta.c b/pasta.c
> index 49b393c..fe2908f 100644
> --- a/pasta.c
> +++ b/pasta.c
> @@ -329,10 +329,16 @@ void pasta_ns_conf(struct ctx *c)
>
> if (c->ifi4) {
> if (c->ip4.no_copy_addrs) {
> - rc = nl_addr_set(nl_sock_ns, c->pasta_ifi,
> - AF_INET,
> - &c->ip4.addrs[0].addr,
> - c->ip4.addrs[0].prefix_len);
> + int i;
> +
> + for (i = 0; i < c->ip4.addr_count; i++) {
> + rc = nl_addr_set(nl_sock_ns,
> + c->pasta_ifi, AF_INET,
> + &c->ip4.addrs[i].addr,
> + c->ip4.addrs[i].prefix_len);
> + if (rc < 0)
> + break;
> + }
Maybe it belongs in a different patch, but multiple address support
might less us partially unify the "copy addrs" and "no_copy_addrs"
paths. Rather than copying addresses direct from the host into the
guest, we optionally add host addresses into the address list, then
unconditionally set everything in the list in the guest.
> } else {
> rc = nl_addr_dup(nl_sock, c->ifi4,
> nl_sock_ns, c->pasta_ifi,
> @@ -378,12 +384,18 @@ void pasta_ns_conf(struct ctx *c)
> 0, IFF_NOARP);
>
> if (c->ip6.no_copy_addrs) {
> - struct in6_addr *a = &c->ip6.addrs[0].addr;
> + struct in6_addr *a;
> + int i;
>
> - if (!IN6_IS_ADDR_UNSPECIFIED(a)) {
> + for (i = 0; i < c->ip6.addr_count; i++) {
> + a = &c->ip6.addrs[i].addr;
> + if (IN6_IS_ADDR_UNSPECIFIED(a))
> + continue;
> rc = nl_addr_set(nl_sock_ns,
> c->pasta_ifi,
> AF_INET6, a, 64);
> + if (rc < 0)
> + break;
> }
> } else {
> rc = nl_addr_dup(nl_sock, c->ifi6,
> --
> 2.51.1
>
--
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
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
next prev parent reply other threads:[~2025-12-15 10:08 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-15 1:54 [RFC 00/12] Support for multiple address and late binding Jon Maloy
2025-12-15 1:54 ` [RFC 01/12] ip: Introduce multi-address data structures for IPv4 and IPv6 Jon Maloy
2025-12-15 9:40 ` David Gibson
2025-12-15 22:05 ` Jon Maloy
2025-12-16 1:58 ` Jon Maloy
2025-12-16 3:14 ` David Gibson
2025-12-15 9:46 ` David Gibson
2025-12-15 1:54 ` [RFC 02/12] ip: Add ip4_default_prefix_len() helper function for class-based prefix Jon Maloy
2025-12-15 9:41 ` David Gibson
2025-12-15 1:54 ` [RFC 03/12] conf: Allow multiple -a/--address options per address family Jon Maloy
2025-12-15 9:53 ` David Gibson [this message]
2025-12-15 1:54 ` [RFC 04/12] conf: Apply -n/--netmask to most recently added address Jon Maloy
2025-12-15 9:54 ` David Gibson
2025-12-15 22:43 ` Jon Maloy
2025-12-15 1:54 ` [RFC 05/12] fwd: Check all configured addresses in guest accessibility functions Jon Maloy
2025-12-15 10:06 ` David Gibson
2025-12-15 1:54 ` [RFC 06/12] arp: Check all configured addresses in ARP filtering Jon Maloy
2025-12-15 10:07 ` David Gibson
2025-12-15 1:54 ` [RFC 07/12] netlink: Subscribe to link/address changes in namespace Jon Maloy
2025-12-15 10:32 ` David Gibson
2025-12-15 23:25 ` Jon Maloy
2025-12-16 3:21 ` David Gibson
2025-12-15 1:54 ` [RFC 08/12] netlink: Subscribe to route " Jon Maloy
2025-12-15 10:38 ` David Gibson
2025-12-15 1:54 ` [RFC 09/12] netlink: Add host-side monitoring for late template interface binding Jon Maloy
2025-12-15 1:54 ` [RFC 10/12] netlink: Add host-side route monitoring and propagation Jon Maloy
2025-12-15 1:54 ` [RFC 11/12] netlink: Prevent host route events from overwriting guest-configured gateway Jon Maloy
2025-12-15 1:54 ` [RFC 12/12] netlink: Rename tap interface when late binding discovers template name Jon Maloy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aT_afgmf-Jf-FRUS@zatzit \
--to=david@gibson.dropbear.id.au \
--cc=dgibson@redhat.com \
--cc=jmaloy@redhat.com \
--cc=passt-dev@passt.top \
--cc=sbrivio@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://passt.top/passt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).