On Sat, Mar 21, 2026 at 08:43:26PM -0400, Jon Maloy wrote: > Add nl_addr_get_all() to read all addresses from the template interface > into c->addrs[] array, rather than just selecting the "best" one. > > This allows multi-address configurations where the template interface > has multiple IPv4 or IPv6 addresses assigned to it, all of which > will now be copied to the guest namespace when using --config-net. > > For IPv6, the function also captures the link-local address into > c->ip6.our_tap_ll as a side effect. > > Update conf_ip4() and conf_ip6() to use nl_addr_get_all() when no > user-specified addresses are present. > > Signed-off-by: Jon Maloy Reviewed-by: David Gibson > --- > conf.c | 51 ++++++++++++++++++++++++++++----------------------- > netlink.c | 53 +++++++++++++++++++++++++++++------------------------ > netlink.h | 3 +-- > 3 files changed, 58 insertions(+), 49 deletions(-) > > diff --git a/conf.c b/conf.c > index 95f09cb..70435d3 100644 > --- a/conf.c > +++ b/conf.c > @@ -736,6 +736,7 @@ static int conf_ip4_prefix(const char *arg) > static unsigned int conf_ip4(struct ctx *c, unsigned int ifi) > { > struct ip4_ctx *ip4 = &c->ip4; > + const struct guest_addr *a; > > if (!ifi) > ifi = nl_get_ext_if(nl_sock, AF_INET); > @@ -755,24 +756,24 @@ static unsigned int conf_ip4(struct ctx *c, unsigned int ifi) > } > } > > - if (!fwd_get_addr(c, AF_INET, 0, 0)) { > - struct in_addr addr; > - int prefix_len; > - int rc = nl_addr_get(nl_sock, ifi, AF_INET, > - &addr, &prefix_len, NULL); > + a = fwd_get_addr(c, AF_INET, CONF_ADDR_USER, 0); > + if (!a) { > + int rc = nl_addr_get_all(c, nl_sock, ifi, AF_INET); > + > if (rc < 0) { > - debug("Couldn't discover IPv4 address: %s", > + debug("Couldn't discover IPv4 addresses: %s", > strerror_(-rc)); > return 0; > } > - if (IN4_IS_ADDR_UNSPECIFIED(&addr)) > + if (!rc || !fwd_get_addr(c, AF_INET, 0, 0)) > return 0; > > - fwd_set_addr(c, &inany_from_v4(addr), CONF_ADDR_HOST, > - prefix_len + 96); > - ip4->addr_seen = addr; > + a = fwd_get_addr(c, AF_INET, CONF_ADDR_HOST, 0); > } > > + if (a) > + ip4->addr_seen = *inany_v4(&a->addr); > + > ip4->our_tap_addr = ip4->guest_gw; > > return ifi; > @@ -803,10 +804,8 @@ static void conf_ip4_local(struct ctx *c) > */ > static unsigned int conf_ip6(struct ctx *c, unsigned int ifi) > { > - const struct guest_addr *a = fwd_get_addr(c, AF_INET6, 0, 0); > struct ip6_ctx *ip6 = &c->ip6; > - union inany_addr addr; > - int prefix_len = 0; > + const struct guest_addr *a; > int rc; > > if (!ifi) > @@ -826,20 +825,26 @@ static unsigned int conf_ip6(struct ctx *c, unsigned int ifi) > } > } > > - rc = nl_addr_get(nl_sock, ifi, AF_INET6, &addr.a6, > - &prefix_len, &ip6->our_tap_ll); > - if (rc < 0) { > - debug("Couldn't discover IPv6 address: %s", strerror_(-rc)); > - return 0; > - } > - > + a = fwd_get_addr(c, AF_INET6, CONF_ADDR_USER, 0); > if (!a) { > - fwd_set_addr(c, &addr, CONF_ADDR_HOST, prefix_len); > - ip6->addr_seen = addr.a6; > + rc = nl_addr_get_all(c, nl_sock, ifi, AF_INET6); > + if (rc < 0) { > + debug("Couldn't discover IPv6 addresses: %s", > + strerror_(-rc)); > + return 0; > + } > + a = fwd_get_addr(c, AF_INET6, CONF_ADDR_HOST, 0); > } else { > - ip6->addr_seen = a->addr.a6; > + rc = nl_addr_get_ll(nl_sock, ifi, &ip6->our_tap_ll); > + if (rc < 0) { > + debug("Couldn't get link-local address: %s", > + strerror_(-rc)); > + } > } > > + if (a) > + ip6->addr_seen = a->addr.a6; > + > if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw)) > ip6->our_tap_ll = ip6->guest_gw; > > diff --git a/netlink.c b/netlink.c > index e07b47f..2727eec 100644 > --- a/netlink.c > +++ b/netlink.c > @@ -747,20 +747,20 @@ int nl_addr_set_ll_nodad(int s, unsigned int ifi) > } > > /** > - * nl_addr_get() - Get most specific global address, given interface and family > + * nl_addr_get_all() - Get all addresses for a given interface into ctx > + * @c: Execution context > * @s: Netlink socket > - * @ifi: Interface index in outer network namespace > - * @af: Address family > - * @addr: Global address to fill > - * @prefix_len: Mask or prefix length, to fill > - * @addr_l: Link-scoped address to fill (for IPv6) > + * @ifi: Interface index > + * @af: Address family (AF_INET or AF_INET6) > * > - * Return: 0 on success, negative error code on failure > + * Populates c->addrs[] with all non-deprecated addresses from the interface. > + * For IPv6, also captures link-local address in c->ip6.our_tap_ll. > + * Skips link-local addresses for the main array (kernel auto-configures them). > + * > + * Return: number of addresses added, or negative error code on failure > */ > -int nl_addr_get(int s, unsigned int ifi, sa_family_t af, > - void *addr, int *prefix_len, void *addr_l) > +int nl_addr_get_all(struct ctx *c, int s, unsigned int ifi, sa_family_t af) > { > - uint8_t prefix_max = 0, prefix_max_ll = 0; > struct req_t { > struct nlmsghdr nlh; > struct ifaddrmsg ifa; > @@ -771,6 +771,7 @@ int nl_addr_get(int s, unsigned int ifi, sa_family_t af, > struct nlmsghdr *nh; > char buf[NLBUFSIZ]; > ssize_t status; > + int count = 0; > uint32_t seq; > > seq = nl_send(s, &req, RTM_GETADDR, NLM_F_DUMP, sizeof(req)); > @@ -784,27 +785,31 @@ int nl_addr_get(int s, unsigned int ifi, sa_family_t af, > > for (rta = IFA_RTA(ifa), na = IFA_PAYLOAD(nh); RTA_OK(rta, na); > rta = RTA_NEXT(rta, na)) { > - if ((af == AF_INET && rta->rta_type != IFA_LOCAL) || > - (af == AF_INET6 && rta->rta_type != IFA_ADDRESS)) > - continue; > + union inany_addr addr; > + int prefix_len; > > - if (ifa->ifa_prefixlen > prefix_max && addr && > - (af == AF_INET || ifa->ifa_scope < RT_SCOPE_LINK)) { > - memcpy(addr, RTA_DATA(rta), RTA_PAYLOAD(rta)); > + if (af == AF_INET && rta->rta_type != IFA_LOCAL) > + continue; > + if (af == AF_INET6 && rta->rta_type != IFA_ADDRESS) > + continue; > > - prefix_max = *prefix_len = ifa->ifa_prefixlen; > + if (af == AF_INET6 && > + ifa->ifa_scope == RT_SCOPE_LINK) { > + memcpy(&c->ip6.our_tap_ll, RTA_DATA(rta), > + sizeof(c->ip6.our_tap_ll)); > + continue; > } > > - if (addr_l && > - af == AF_INET6 && ifa->ifa_scope == RT_SCOPE_LINK && > - ifa->ifa_prefixlen > prefix_max_ll) { > - memcpy(addr_l, RTA_DATA(rta), RTA_PAYLOAD(rta)); > + inany_from_af(&addr, af, RTA_DATA(rta)); > + prefix_len = ifa->ifa_prefixlen + > + (af == AF_INET ? 96 : 0); > > - prefix_max_ll = ifa->ifa_prefixlen; > - } > + fwd_set_addr(c, &addr, CONF_ADDR_HOST, prefix_len); > + count++; > } > } > - return status; > + > + return status < 0 ? status : count; > } > > /** > diff --git a/netlink.h b/netlink.h > index b22f485..3af6d58 100644 > --- a/netlink.h > +++ b/netlink.h > @@ -19,8 +19,7 @@ int nl_route_get_def(int s, unsigned int ifi, sa_family_t af, void *gw); > int nl_route_set_def(int s, unsigned int ifi, sa_family_t af, const void *gw); > int nl_route_dup(int s_src, unsigned int ifi_src, > int s_dst, unsigned int ifi_dst, sa_family_t af); > -int nl_addr_get(int s, unsigned int ifi, sa_family_t af, > - void *addr, int *prefix_len, void *addr_l); > +int nl_addr_get_all(struct ctx *c, int s, unsigned int ifi, sa_family_t af); > bool nl_neigh_mac_get(int s, const union inany_addr *addr, int ifi, > unsigned char *mac); > int nl_addr_set(int s, unsigned int ifi, sa_family_t af, > -- > 2.52.0 > -- 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