On Fri, Jan 30, 2026 at 04:44:39PM -0500, Jon Maloy wrote:
As preparation for supporting multiple addresses per interface, we replace the single addr/prefix_len fields with an array. The array consists of a new struct inany_addr_entry containing an address and prefix length, both in inany_addr format.
There are only two functional changes: - The indicated IPv6 prefix length is now properly stored, instead of being ignored and overridden with the hardcoded value 64, as as has been the case until now. - Since even IPv4 addresses now are stored in IPv6 format, we also store the corresponding prefix length in that format, i.e. using the range [96,128] instead of [0,32].
Signed-off-by: Jon Maloy
--- v2: -Using inany_addr instead of protocol specific addresses as entry address field.
v3: -Merging into one array, directly in struct ctx -Changed prefix_len and flags fields in struct inany_addr_entry to uint8_t, since that makes the struct directly migratable --- arp.c | 5 +- conf.c | 145 ++++++++++++++++++++++++++++++++----------------------- conf.h | 6 +++ dhcp.c | 8 +-- dhcpv6.c | 6 +-- fwd.c | 17 ++++--- ip.c | 11 +++-- ip.h | 5 ++ ndp.c | 6 +-- passt.h | 123 +++++++++++++++++++++++++++++++++++++++++++--- pasta.c | 15 ++++-- tap.c | 5 +- 12 files changed, 251 insertions(+), 101 deletions(-)
diff --git a/arp.c b/arp.c index bb042e9..f16beac 100644 --- a/arp.c +++ b/arp.c @@ -54,7 +54,8 @@ static bool ignore_arp(const struct ctx *c, return true;
/* Don't resolve the guest's assigned address, either. */ - if (!memcmp(am->tip, &c->ip4.addr, sizeof(am->tip))) + if (first_v4(c) &&
first_v4() isn't a great name as a global function. But then I guess later patches in the series will remove most of the uses, so it might be ok in the interim.
+ !memcmp(am->tip, inany_v4(&first_v4(c)->addr), sizeof(am->tip))) return true;
return false; @@ -145,7 +146,7 @@ void arp_send_init_req(const struct ctx *c) memcpy(req.am.sha, c->our_tap_mac, sizeof(req.am.sha)); memcpy(req.am.sip, &c->ip4.our_tap_addr, sizeof(req.am.sip)); memcpy(req.am.tha, MAC_BROADCAST, sizeof(req.am.tha)); - memcpy(req.am.tip, &c->ip4.addr, sizeof(req.am.tip)); + memcpy(req.am.tip, inany_v4(&first_v4(c)->addr), sizeof(req.am.tip));
debug("Sending initial ARP request for guest MAC address"); tap_send_single(c, &req, sizeof(req)); diff --git a/conf.c b/conf.c index 5188c02..bb6bcf8 100644 --- a/conf.c +++ b/conf.c @@ -56,7 +56,7 @@ #define IP4_LL_GUEST_GW (struct in_addr){ htonl_constant(0xa9fe0202) } /* 169.254.2.2, libslirp default: 10.0.2.2 */
-#define IP4_LL_PREFIX_LEN 16 +#define IP4_LL_PREFIX_LEN 112 /* 96 + 16, in IPv6 format */
I don't think the encoding should be changed without also changing the #define name.
#define IP6_LL_GUEST_GW (struct in6_addr) \ {{{ 0xfe, 0x80, 0, 0, 0, 0, 0, 0, \ @@ -693,11 +693,11 @@ static int conf_ip4_prefix(const char *arg) /** * conf_ip4() - Verify or detect IPv4 support, get relevant addresses * @ifi: Host interface to attempt (0 to determine one) - * @ip4: IPv4 context (will be written) + * @c: Execution context (will be written)
Putting @c as the first parameter is a pretty strong convention.
* * Return: interface index for IPv4, or 0 on failure. */ -static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4) +static unsigned int conf_ip4(unsigned int ifi, struct ctx *c) { if (!ifi) ifi = nl_get_ext_if(nl_sock, AF_INET); @@ -707,9 +707,9 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4) return 0; }
- if (IN4_IS_ADDR_UNSPECIFIED(&ip4->guest_gw)) { + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.guest_gw)) { int rc = nl_route_get_def(nl_sock, ifi, AF_INET, - &ip4->guest_gw); + &c->ip4.guest_gw); if (rc < 0) { debug("Couldn't discover IPv4 gateway address: %s", strerror_(-rc)); @@ -717,60 +717,57 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4) } }
- if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr)) { + if (!count_v4(c)) {
Do you need count_v4() at this stage of the series? Testing first_v4() would work just as well here, and so far this is the only caller of count_v4().
+ struct in_addr addr = {0,}; + int prefix_len = 0; int rc = nl_addr_get(nl_sock, ifi, AF_INET, - &ip4->addr, &ip4->prefix_len, NULL); + &addr, &prefix_len, NULL); if (rc < 0) { debug("Couldn't discover IPv4 address: %s", strerror_(-rc)); return 0; } - } + if (IN4_IS_ADDR_UNSPECIFIED(&addr)) + return 0;
- if (!ip4->prefix_len) { - in_addr_t addr = ntohl(ip4->addr.s_addr); - if (IN_CLASSA(addr)) - ip4->prefix_len = (32 - IN_CLASSA_NSHIFT); - else if (IN_CLASSB(addr)) - ip4->prefix_len = (32 - IN_CLASSB_NSHIFT); - else if (IN_CLASSC(addr)) - ip4->prefix_len = (32 - IN_CLASSC_NSHIFT); - else - ip4->prefix_len = 32; + c->addrs[c->addr_count].addr = inany_from_v4(addr); + c->addrs[c->addr_count].prefix_len = prefix_len + 96; + c->addrs[c->addr_count].flags = INANY_ADDR_HOST; + c->addr_count++;
I suspect an add_prefix() type function will be worth it, by the time we're done.
+ c->ip4.addr_seen = addr; }
- ip4->addr_seen = ip4->addr; - - ip4->our_tap_addr = ip4->guest_gw; - - if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr)) - return 0; + c->ip4.our_tap_addr = c->ip4.guest_gw;
return ifi; }
/** * conf_ip4_local() - Configure IPv4 addresses and attributes for local mode - * @ip4: IPv4 context (will be written) + * @c: Execution context (will be written) */ -static void conf_ip4_local(struct ip4_ctx *ip4) +static void conf_ip4_local(struct ctx *c) { - ip4->addr_seen = ip4->addr = IP4_LL_GUEST_ADDR; - ip4->our_tap_addr = ip4->guest_gw = IP4_LL_GUEST_GW; - ip4->prefix_len = IP4_LL_PREFIX_LEN; + c->addrs[c->addr_count].addr = inany_from_v4(IP4_LL_GUEST_ADDR); + c->ip4.addr_seen = *inany_v4(&c->addrs[c->addr_count].addr); + c->ip4.our_tap_addr = c->ip4.guest_gw = IP4_LL_GUEST_GW; + c->addrs[c->addr_count].prefix_len = IP4_LL_PREFIX_LEN;
I'd add the 96 here, rather than putting it into the constant.
+ c->addr_count++;
- ip4->no_copy_addrs = ip4->no_copy_routes = true; + c->ip4.no_copy_addrs = c->ip4.no_copy_routes = true; }
/** * conf_ip6() - Verify or detect IPv6 support, get relevant addresses * @ifi: Host interface to attempt (0 to determine one) - * @ip6: IPv6 context (will be written) + * @c: Execution context (will be written) * * Return: interface index for IPv6, or 0 on failure. */ -static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6) +static unsigned int conf_ip6(unsigned int ifi, struct ctx *c) { + struct inany_addr_entry *e; + union inany_addr addr = {0,}; int prefix_len = 0; int rc;
@@ -782,8 +779,8 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6) return 0; }
- if (IN6_IS_ADDR_UNSPECIFIED(&ip6->guest_gw)) { - rc = nl_route_get_def(nl_sock, ifi, AF_INET6, &ip6->guest_gw); + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.guest_gw)) { + rc = nl_route_get_def(nl_sock, ifi, AF_INET6, &c->ip6.guest_gw); if (rc < 0) { debug("Couldn't discover IPv6 gateway address: %s", strerror_(-rc)); @@ -791,21 +788,28 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6) } }
- rc = nl_addr_get(nl_sock, ifi, AF_INET6, - IN6_IS_ADDR_UNSPECIFIED(&ip6->addr) ? &ip6->addr : NULL, - &prefix_len, &ip6->our_tap_ll); + rc = nl_addr_get(nl_sock, ifi, AF_INET6, &addr.a6, + &prefix_len, &c->ip6.our_tap_ll); if (rc < 0) { debug("Couldn't discover IPv6 address: %s", strerror_(-rc)); return 0; }
- ip6->addr_seen = ip6->addr; + if (!count_v6(c)) {
Same comment as for count_v4().
+ c->addrs[c->addr_count].addr = addr; + c->addrs[c->addr_count].prefix_len = prefix_len ? prefix_len : 64; + c->addrs[c->addr_count].flags = INANY_ADDR_HOST; + c->addr_count++; + } + + e = first_v6(c); + c->ip6.addr_seen = e->addr.a6;
- if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw)) - ip6->our_tap_ll = ip6->guest_gw; + if (IN6_IS_ADDR_LINKLOCAL(&c->ip6.guest_gw)) + c->ip6.our_tap_ll = c->ip6.guest_gw;
- if (IN6_IS_ADDR_UNSPECIFIED(&ip6->addr) || - IN6_IS_ADDR_UNSPECIFIED(&ip6->our_tap_ll)) + if (IN6_IS_ADDR_UNSPECIFIED(&e->addr.a6) || + IN6_IS_ADDR_UNSPECIFIED(&c->ip6.our_tap_ll)) return 0;
return ifi; @@ -813,13 +817,13 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6)
/** * conf_ip6_local() - Configure IPv6 addresses and attributes for local mode - * @ip6: IPv6 context (will be written) + * @c: Execution context (will be written) */ -static void conf_ip6_local(struct ip6_ctx *ip6) +static void conf_ip6_local(struct ctx *c) { - ip6->our_tap_ll = ip6->guest_gw = IP6_LL_GUEST_GW; + c->ip6.our_tap_ll = c->ip6.guest_gw = IP6_LL_GUEST_GW;
- ip6->no_copy_addrs = ip6->no_copy_routes = true; + c->ip6.no_copy_addrs = c->ip6.no_copy_routes = true; }
/** @@ -1145,13 +1149,15 @@ static void conf_print(const struct ctx *c) buf4, sizeof(buf4)));
if (!c->no_dhcp) { + struct inany_addr_entry *e = first_v4(c);
You never test for e being NULL in this path. Maybe that's not possible unless c->no_dhcp, but that's pretty non-obvious from here.
uint32_t mask;
- mask = IN4_MASK(c->ip4.prefix_len); + mask = IN4_MASK(e->prefix_len);
info("DHCP:"); info(" assign: %s", - inet_ntop(AF_INET, &c->ip4.addr, buf4, sizeof(buf4))); + inet_ntop(AF_INET, inany_v4(&e->addr), + buf4, sizeof(buf4))); info(" mask: %s", inet_ntop(AF_INET, &mask, buf4, sizeof(buf4))); info(" router: %s", @@ -1189,7 +1195,8 @@ static void conf_print(const struct ctx *c) goto dns6;
info(" assign: %s", - inet_ntop(AF_INET6, &c->ip6.addr, buf6, sizeof(buf6))); + inet_ntop(AF_INET6, &first_v6(c)->addr.a6,
Similarly no test for NULL first_v6() before dereferencing.
+ buf6, sizeof(buf6))); info(" router: %s", inet_ntop(AF_INET6, &c->ip6.guest_gw, buf6, sizeof(buf6))); info(" our link-local: %s", @@ -1811,6 +1818,7 @@ void conf(struct ctx *c, int argc, char **argv) } case 'a': { union inany_addr addr = { 0 }; + struct inany_addr_entry *e; int prefix_len = 0; int af;
@@ -1828,13 +1836,20 @@ void conf(struct ctx *c, int argc, char **argv) die("Can't mix CIDR with -n");
if (af == AF_INET) { - c->ip4.addr = *inany_v4(&addr); - c->ip4.prefix_len = prefix_len ? prefix_len - 96 : - ip4_class_prefix_len(&c->ip4.addr); + e = &c->addrs[c->addr_count]; + e->addr = addr; + e->prefix_len = prefix_len ? prefix_len : + ip4_class_prefix_len(inany_v4(&addr)); + e->flags = INANY_ADDR_CONFIGURED; + c->addr_count++; if (c->mode == MODE_PASTA) c->ip4.no_copy_addrs = true; } else if (af == AF_INET6) { - c->ip6.addr = addr.a6; + e = &c->addrs[c->addr_count]; + e->addr = addr; + e->prefix_len = prefix_len ? prefix_len : 64; + e->flags = INANY_ADDR_CONFIGURED; + c->addr_count++; if (c->mode == MODE_PASTA) c->ip6.no_copy_addrs = true; } else { @@ -1842,14 +1857,21 @@ void conf(struct ctx *c, int argc, char **argv) } break; } - case 'n': + case 'n': { + struct inany_addr_entry *e; + int plen; + if (prefix_from_cidr) die("Can't use both -n and CIDR prefix length"); - c->ip4.prefix_len = conf_ip4_prefix(optarg); - if (c->ip4.prefix_len < 0) + plen = conf_ip4_prefix(optarg); + if (plen < 0) die("Invalid netmask: %s", optarg); + e = first_v4(c); + if (e) + e->prefix_len = plen + 96; prefix_from_opt = true; break; + } case 'M': parse_mac(c->our_tap_mac, optarg); break; @@ -1993,9 +2015,9 @@ void conf(struct ctx *c, int argc, char **argv)
nl_sock_init(c, false); if (!v6_only) - c->ifi4 = conf_ip4(ifi4, &c->ip4); + c->ifi4 = conf_ip4(ifi4, c); if (!v4_only) - c->ifi6 = conf_ip6(ifi6, &c->ip6); + c->ifi6 = conf_ip6(ifi6, c);
if (c->ifi4 && c->mtu < IPV4_MIN_MTU) { warn("MTU %"PRIu16" is too small for IPv4 (minimum %u)", @@ -2018,14 +2040,14 @@ void conf(struct ctx *c, int argc, char **argv) if (!c->ifi4 && !v6_only) { info("IPv4: no external interface as template, use local mode");
- conf_ip4_local(&c->ip4); + conf_ip4_local(c); c->ifi4 = -1; }
if (!c->ifi6 && !v4_only) { info("IPv6: no external interface as template, use local mode");
- conf_ip6_local(&c->ip6); + conf_ip6_local(c); c->ifi6 = -1; }
@@ -2134,7 +2156,8 @@ 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.addr)) { + } else if (!first_v6(c) || + IN6_IS_ADDR_UNSPECIFIED(&first_v6(c)->addr.a6)) { c->no_dhcpv6 = 1; }
diff --git a/conf.h b/conf.h index b45ad74..e86fe96 100644 --- a/conf.h +++ b/conf.h @@ -6,6 +6,12 @@ #ifndef CONF_H #define CONF_H
+/* Flags indicating origin and role of an address + * To be used in struct inany_addr_entry + */ +#define CONF_ADDR_CONFIGURED (1 << 0) /* User set via -a */
As Stefano said, I think "USER" or "CMDLINE" would be preferable to "CONFIGURED".
+#define CONF_ADDR_HOST (1 << 1) /* From host interface */ + enum passt_modes conf_mode(int argc, char *argv[]); void conf(struct ctx *c, int argc, char **argv);
diff --git a/dhcp.c b/dhcp.c index c552f01..933a8cf 100644 --- a/dhcp.c +++ b/dhcp.c @@ -352,7 +352,7 @@ int dhcp(const struct ctx *c, struct iov_tail *data) reply.secs = 0; reply.flags = m->flags; reply.ciaddr = m->ciaddr; - reply.yiaddr = c->ip4.addr; + reply.yiaddr = *inany_v4(&first_v4(c)->addr);
Again, maybe it's impossible for first_v4() to be NULL if !no_dhcp, but that's not obvious from here. If it's your intention to rely on that, an ASSERT would make sense.
reply.siaddr = 0; reply.giaddr = m->giaddr; memcpy(&reply.chaddr, m->chaddr, sizeof(reply.chaddr)); @@ -404,7 +404,7 @@ int dhcp(const struct ctx *c, struct iov_tail *data)
info(" from %s", eth_ntop(m->chaddr, macstr, sizeof(macstr)));
- mask.s_addr = IN4_MASK(c->ip4.prefix_len); + mask.s_addr = IN4_MASK(first_v4(c)->prefix_len);
- 96, since the entry has an IPv6 encoded prefix length. Or maybe it would be nicer to replace first_v4() with something that extracts the first v4 address calling inany_v4() for you, and also extracts the matching prefix, doing the re-encoding to IPv4 format.
memcpy(opts[1].s, &mask, sizeof(mask)); memcpy(opts[3].s, &c->ip4.guest_gw, sizeof(c->ip4.guest_gw)); memcpy(opts[54].s, &c->ip4.our_tap_addr, sizeof(c->ip4.our_tap_addr)); @@ -412,7 +412,7 @@ int dhcp(const struct ctx *c, struct iov_tail *data) /* If the gateway is not on the assigned subnet, send an option 121 * (Classless Static Routing) adding a dummy route to it. */ - if ((c->ip4.addr.s_addr & mask.s_addr) + if ((inany_v4(&first_v4(c)->addr)->s_addr & mask.s_addr) != (c->ip4.guest_gw.s_addr & mask.s_addr)) { /* a.b.c.d/32:0.0.0.0, 0:a.b.c.d */ opts[121].slen = 14; @@ -469,7 +469,7 @@ int dhcp(const struct ctx *c, struct iov_tail *data) if (m->flags & FLAG_BROADCAST) dst = in4addr_broadcast; else - dst = c->ip4.addr; + dst = *inany_v4(&first_v4(c)->addr);
tap_udp4_send(c, c->ip4.our_tap_addr, 67, dst, 68, &reply, dlen);
diff --git a/dhcpv6.c b/dhcpv6.c index e4df0db..a1e5f15 100644 --- a/dhcpv6.c +++ b/dhcpv6.c @@ -625,7 +625,7 @@ int dhcpv6(struct ctx *c, struct iov_tail *data, if (mh->type == TYPE_CONFIRM && server_id) return -1;
- if (dhcpv6_ia_notonlink(data, &c->ip6.addr)) { + if (dhcpv6_ia_notonlink(data, &first_v6(c)->addr.a6)) {
Likewise a helper that returns the first v6 address *as* a struct in6_addr might be neater.
dhcpv6_send_ia_notonlink(c, data, &client_id_base, ntohs(client_id->l), mh->xid); @@ -679,7 +679,7 @@ int dhcpv6(struct ctx *c, struct iov_tail *data,
tap_udp6_send(c, src, 547, tap_ip6_daddr(c, src), 546, mh->xid, &resp, n); - c->ip6.addr_seen = c->ip6.addr; + c->ip6.addr_seen = first_v6(c)->addr.a6;
return 1; } @@ -703,5 +703,5 @@ void dhcpv6_init(const struct ctx *c) memcpy(resp_not_on_link.server_id.duid_lladdr, c->our_tap_mac, sizeof(c->our_tap_mac));
- resp.ia_addr.addr = c->ip6.addr; + resp.ia_addr.addr = first_v6(c)->addr.a6; } diff --git a/fwd.c b/fwd.c index 44a0e10..54248a3 100644 --- a/fwd.c +++ b/fwd.c @@ -516,7 +516,8 @@ static bool fwd_guest_accessible4(const struct ctx *c, /* For IPv4, addr_seen is initialised to addr, so is always a valid * address */ - if (IN4_ARE_ADDR_EQUAL(addr, &c->ip4.addr) || + if ((first_v4(c) && + IN4_ARE_ADDR_EQUAL(addr, inany_v4(&first_v4(c)->addr))) || IN4_ARE_ADDR_EQUAL(addr, &c->ip4.addr_seen)) return false;
@@ -537,7 +538,7 @@ static bool fwd_guest_accessible6(const struct ctx *c, if (IN6_IS_ADDR_LOOPBACK(addr)) return false;
- if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.addr)) + if (first_v6(c) && IN6_ARE_ADDR_EQUAL(addr, &first_v6(c)->addr.a6)) return false;
/* For IPv6, addr_seen starts unspecified, because we don't know what LL @@ -586,10 +587,10 @@ static void nat_outbound(const struct ctx *c, const union inany_addr *addr, *translated = inany_loopback4; else if (inany_equals6(addr, &c->ip6.map_host_loopback)) *translated = inany_loopback6; - else if (inany_equals4(addr, &c->ip4.map_guest_addr)) - *translated = inany_from_v4(c->ip4.addr); - else if (inany_equals6(addr, &c->ip6.map_guest_addr)) - translated->a6 = c->ip6.addr; + else if (first_v4(c) && inany_equals4(addr, &c->ip4.map_guest_addr)) + *translated = first_v4(c)->addr; + else if (first_v6(c) && inany_equals6(addr, &c->ip6.map_guest_addr)) + translated->a6 = first_v6(c)->addr.a6;
Implementing the forward table for the tap pif will make this cleaner, when I get to it.
else *translated = *addr; } @@ -710,10 +711,10 @@ bool nat_inbound(const struct ctx *c, const union inany_addr *addr, inany_equals6(addr, &in6addr_loopback)) { translated->a6 = c->ip6.map_host_loopback; } else if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.map_guest_addr) && - inany_equals4(addr, &c->ip4.addr)) { + first_v4(c) && inany_equals(addr, &first_v4(c)->addr)) { *translated = inany_from_v4(c->ip4.map_guest_addr); } else if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.map_guest_addr) && - inany_equals6(addr, &c->ip6.addr)) { + first_v6(c) && inany_equals6(addr, &first_v6(c)->addr.a6)) { translated->a6 = c->ip6.map_guest_addr; } else if (fwd_guest_accessible(c, addr)) { *translated = *addr; diff --git a/ip.c b/ip.c index 40dc24e..a86753f 100644 --- a/ip.c +++ b/ip.c @@ -74,17 +74,18 @@ found: * ip4_class_prefix_len() - Get class based prefix length for IPv4 address * @addr: IPv4 address * - * Return: prefix length based on address class, or 32 for other + * Return: prefix length in IPv6 format (96 + IPv4 prefix) based on address + * class, or 128 for other
Here again, I don't think you should change the encoding without changing the function name. As I've said, I think we should consistently use IPv6 encoding *when dealing with an inany*. But functions that deal specifically with IPv4 addresses, should use the IPv4 prefix length encoding.
*/ int ip4_class_prefix_len(const struct in_addr *addr) { in_addr_t a = ntohl(addr->s_addr);
if (IN_CLASSA(a)) - return 32 - IN_CLASSA_NSHIFT; + return 96 + 32 - IN_CLASSA_NSHIFT; if (IN_CLASSB(a)) - return 32 - IN_CLASSB_NSHIFT; + return 96 + 32 - IN_CLASSB_NSHIFT; if (IN_CLASSC(a)) - return 32 - IN_CLASSC_NSHIFT; - return 32; + return 96 + 32 - IN_CLASSC_NSHIFT; + return 128; } diff --git a/ip.h b/ip.h index c829d84..91d6d9a 100644 --- a/ip.h +++ b/ip.h @@ -137,6 +137,11 @@ static const struct in_addr in4addr_broadcast = { 0xffffffff }; #define IPV6_MIN_MTU 1280 #endif
+/* Maximum number of addresses per address family */ +#define IP4_MAX_ADDRS 16 +#define IP6_MAX_ADDRS 16
Separate defines here don't really make sense now you have a single address array.
+#define INANY_MAX_ADDRS (IP4_MAX_ADDRS + IP6_MAX_ADDRS) + int ip4_class_prefix_len(const struct in_addr *addr);
#endif /* IP_H */ diff --git a/ndp.c b/ndp.c index eb9e313..f60c298 100644 --- a/ndp.c +++ b/ndp.c @@ -257,7 +257,7 @@ static void ndp_ra(const struct ctx *c, const struct in6_addr *dst) .valid_lifetime = ~0U, .pref_lifetime = ~0U, }, - .prefix = c->ip6.addr, + .prefix = first_v6(c)->addr.a6, .source_ll = { .header = { .type = OPT_SRC_L2_ADDR, @@ -466,8 +466,8 @@ void ndp_send_init_req(const struct ctx *c) .icmp6_solicited = 0, /* Reserved */ .icmp6_override = 0, /* Reserved */ }, - .target_addr = c->ip6.addr + .target_addr = first_v6(c)->addr.a6 }; debug("Sending initial NDP NS request for guest MAC address"); - ndp_send(c, &c->ip6.addr, &ns, sizeof(ns)); + ndp_send(c, &first_v6(c)->addr.a6, &ns, sizeof(ns)); } diff --git a/passt.h b/passt.h index 79d01dd..ec5517d 100644 --- a/passt.h +++ b/passt.h @@ -64,11 +64,42 @@ enum passt_modes { MODE_VU, };
+/* Flags indicating origin and role of an address + * in struct inany_addr_entry + */ +#define INANY_ADDR_CONFIGURED BIT(0) /* User set via -a */ +#define INANY_ADDR_HOST BIT(1) /* From host interface */
These appear to be redundant with with CONF_ADDR_*. These are specific to how we handle configuration, not general properties of a prefix, so I don't think these ones belong.
+ +/** + * for_each_addr() - Iterate over addresses in unified array + * @c: Pointer to struct ctx + * @e: Pointer variable for current entry (struct inany_addr_entry *)
Wll, @c going first is a strongish convention, but the loop variable going first in a for_each_() macro is an even stronger convention.
+ * @af: Address family filter: AF_INET, AF_INET6, or 0 for all
Iterating over just one address type is going to become less and less common as we unify handling in various places. So it might be simpler to just have a single iterator, and explicitly filter the "wrong" type in the places we need to.
+ * + * Note: @_i is the internal loop counter, uses _next_addr_idx() helper + */ +#define for_each_addr(c, e, af) \ + for (int _i = _next_addr_idx((c), 0, (af)); \ + _i < (c)->addr_count && ((e) = &(c)->addrs[_i], true); \ + _i = _next_addr_idx((c), _i + 1, (af))) + +/* first_v4(), first_v6(), count_v4(), count_v6() defined after struct ctx */ + +/** + * struct inany_addr_entry - Unified IPv4/IPv6 address entry + * @addr: IPv4 (as mapped) or IPv6 address + * @prefix_len: Prefix length in IPv6/IPv4-mapped [0,128]/[96,128] format + * @flags: INANY_ADDR_* flags + */ +struct inany_addr_entry { + union inany_addr addr; + uint8_t prefix_len; + uint8_t flags; +}; + /** * struct ip4_ctx - IPv4 execution context - * @addr: IPv4 address assigned to guest * @addr_seen: Latest IPv4 address seen as source from tap - * @prefixlen: IPv4 prefix length (netmask) * @guest_gw: IPv4 gateway as seen by the guest * @map_host_loopback: Outbound connections to this address are NATted to the * host's 127.0.0.1 @@ -84,10 +115,7 @@ enum passt_modes { * @no_copy_addrs: Don't copy all addresses when configuring namespace */ struct ip4_ctx { - /* PIF_TAP addresses */ - struct in_addr addr; struct in_addr addr_seen; - int prefix_len; struct in_addr guest_gw; struct in_addr map_host_loopback; struct in_addr map_guest_addr; @@ -107,7 +135,6 @@ struct ip4_ctx {
/** * struct ip6_ctx - IPv6 execution context - * @addr: IPv6 address assigned to guest * @addr_seen: Latest IPv6 global/site address seen as source from tap * @addr_ll_seen: Latest IPv6 link-local address seen as source from tap * @guest_gw: IPv6 gateway as seen by the guest @@ -125,8 +152,6 @@ struct ip4_ctx { * @no_copy_addrs: Don't copy all addresses when configuring namespace */ struct ip6_ctx { - /* PIF_TAP addresses */ - struct in6_addr addr; struct in6_addr addr_seen; struct in6_addr addr_ll_seen; struct in6_addr guest_gw; @@ -257,6 +282,10 @@ struct ctx { int ifi6; struct ip6_ctx ip6;
+ /* Unified address array for both IPv4 (mapped) and IPv6 */ + struct inany_addr_entry addrs[INANY_MAX_ADDRS]; + int addr_count; +
THe new fields need to be added to the structure comment
char pasta_ifn[IF_NAMESIZE]; unsigned int pasta_ifi; int pasta_conf_ns; @@ -294,6 +323,84 @@ struct ctx { bool migrate_exit; };
+/** + * first_v4() - Get first IPv4 address entry + * @c: Pointer to struct ctx + * + * Return: pointer to first IPv4 entry, or NULL if none + */ +static inline struct inany_addr_entry *first_v4(const struct ctx *c) +{ + for (int i = 0; i < c->addr_count; i++) + if (inany_v4(&c->addrs[i].addr)) + return (struct inany_addr_entry *)&c->addrs[i]; + return NULL; +} + +/** + * first_v6() - Get first IPv6 address entry + * @c: Pointer to struct ctx + * + * Return: pointer to first IPv6 entry, or NULL if none + */ +static inline struct inany_addr_entry *first_v6(const struct ctx *c) +{ + for (int i = 0; i < c->addr_count; i++) + if (!inany_v4(&c->addrs[i].addr)) + return (struct inany_addr_entry *)&c->addrs[i]; + return NULL; +} + +/** + * count_v4() - Count IPv4 addresses + * @c: Pointer to struct ctx + * + * Return: number of IPv4 addresses + */ +static inline int count_v4(const struct ctx *c) +{ + int count = 0; + + for (int i = 0; i < c->addr_count; i++) + if (inany_v4(&c->addrs[i].addr)) + count++; + return count; +} + +/** + * count_v6() - Count IPv6 addresses + * @c: Pointer to struct ctx + * + * Return: number of IPv6 addresses + */ +static inline int count_v6(const struct ctx *c) +{ + int count = 0; + + for (int i = 0; i < c->addr_count; i++) + if (!inany_v4(&c->addrs[i].addr)) + count++; + return count; +} + +/** + * _next_addr_idx() - Find next address index matching family filter + * @c: Pointer to struct ctx + * @i: Starting index + * @af: Address family filter: AF_INET, AF_INET6, or 0 for all + * + * Return: next matching index, or addr_count if none found + */ +static inline int _next_addr_idx(const struct ctx *c, int i, int af) +{ + while (i < c->addr_count) { + if (!af || ((af == AF_INET) == !!inany_v4(&c->addrs[i].addr))) + return i; + i++; + } + return i; +}
This is a pretty fiddly way of doing the iteration. Simpler to have a plain loop through the array, and skip the entries you don't want.
+ void proto_update_l2_buf(const unsigned char *eth_d);
#endif /* PASST_H */ diff --git a/pasta.c b/pasta.c index c307b8a..08f35f4 100644 --- a/pasta.c +++ b/pasta.c @@ -338,10 +338,12 @@ void pasta_ns_conf(struct ctx *c)
if (c->ifi4) { if (c->ip4.no_copy_addrs) { + struct inany_addr_entry *e = first_v4(c);
NULL check?
+ rc = nl_addr_set(nl_sock_ns, c->pasta_ifi, AF_INET, - &c->ip4.addr, - c->ip4.prefix_len); + inany_v4(&e->addr), + e->prefix_len); } else { rc = nl_addr_dup(nl_sock, c->ifi4, nl_sock_ns, c->pasta_ifi, @@ -387,10 +389,13 @@ void pasta_ns_conf(struct ctx *c) 0, IFF_NOARP);
if (c->ip6.no_copy_addrs) { - if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr)) { + struct inany_addr_entry *e = first_v6(c); + + if (e && !IN6_IS_ADDR_UNSPECIFIED(&e->addr.a6)) {
If it was UNSPECIFIED, then first_v6() would return NULL, no?
rc = nl_addr_set(nl_sock_ns, - c->pasta_ifi, AF_INET6, - &c->ip6.addr, 64); + c->pasta_ifi, + AF_INET6, + &e->addr.a6, 64); } } else { rc = nl_addr_dup(nl_sock, c->ifi6, diff --git a/tap.c b/tap.c index 9d1344b..f3073c6 100644 --- a/tap.c +++ b/tap.c @@ -951,8 +951,9 @@ resume: c->ip6.addr_seen = *saddr; }
- if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr)) - c->ip6.addr = *saddr; + if (first_v6(c) && + IN6_IS_ADDR_UNSPECIFIED(&first_v6(c)->addr.a6))
Again, if the address were unspecified, first_v6() would return NULL, wouldn't it?
+ first_v6(c)->addr.a6 = *saddr; } else if (!IN6_IS_ADDR_UNSPECIFIED(saddr)){ c->ip6.addr_seen = *saddr; } -- 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