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=B/INDFjN; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by passt.top (Postfix) with ESMTPS id 0EBEE5A0262 for ; Sat, 20 Jun 2026 00:12:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1781907121; 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=9FYUHQDYIORn7oNxViOtjz9S/GJJZPw/NxwcDm5ssdk=; b=B/INDFjNquouKi2poFZ1VgkWiwB451itAxfZ+AFVmjQAxy+efa03LFMWwJXFIGiZVasjYo +XfvTWvTjIbWr2iKMxOLMdcnhWz/H53YGPtKJutL/l56Zg2L02J7XkKKQzony4HmU7+xru udlPlxLeJrB/2vK8HdP8jo78FbWshSQ= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-650-tNAwh-VTMQ-iphBKl7cQIg-1; Fri, 19 Jun 2026 18:12:00 -0400 X-MC-Unique: tNAwh-VTMQ-iphBKl7cQIg-1 X-Mimecast-MFC-AGG-ID: tNAwh-VTMQ-iphBKl7cQIg_1781907119 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-490a767b782so15993325e9.2 for ; Fri, 19 Jun 2026 15:12:00 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781907119; x=1782511919; 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=9FYUHQDYIORn7oNxViOtjz9S/GJJZPw/NxwcDm5ssdk=; b=I767uyaZskGQlk8wOaythv8EJ/8GTxQpOLUp028e2Rhk7Vd2M7wNMToln31CpHJVjz yv62km+/3fXdcgsFrjwcOz68fz+8xyOtcJg02KQazlR0e7O7F80/RN/A6EsYEPouCRRX bAANwIoWm/t4wyIQa4Bjvl40ax7qgGmIQBOtBS3Wrh1BTIB6zO5FLdh8UvNM9DRFSNAE bSeGd0jaXeAFg5+bt+1Z39E5Yklv9coW7nMO2szrW6va9ylCn7sAF67YN5EJsmiVvLfl 9jzI/+Q2S1coDCYN2WeGznHm7O4eC8fRMZoBCoukFQVeK7uSzE4EeroxqMFYVm31YL3g ztQQ== X-Forwarded-Encrypted: i=1; AFNElJ93++WLeEedv0ezdl9MDOa8FyTNiwzYJ7wBculFDQmSt0ykkTwHqcH2J+qgNfcpY++QnDadDdM2Y0Q=@passt.top X-Gm-Message-State: AOJu0Yx+FrM00jsjYyxnhyBs5hvhdeLMAi2EyG7dvtMHAxXlcdI/2Vvn yX1fiQ0lVRi9Bpcbpxm1yLRVG+PNhjl9xsJ/b/qQEAiFEx6u4P9/9iQrQuXezhmIlJozjl5D4kK +hH4nkP+swpSevM+hkU5Q1xUoQJTyJvoVzGw2czzvnsq3vXhggPJ7mQ== X-Gm-Gg: AfdE7clv6sXTpNZJ9ly0R9VlXuPS9UV5A1h8D08JN8tndRM97aZGW7v+7FaVBgJ5Dzc UY3QteeuRLF9eMNVjLQDYYqRad+NOk7nVQi3QLBeWQrmpktNn51i/fu4i7JNB9OpuWL2Sc5hz3V /JD6ITkxOPfW/FJof+LF2IEtIrq9oVV9T4PSZSFSOfJMKSHgZSrjejYaE+y1/ma6VhzJJgyILOt KF6Np7iGTiCnBEgJFA+fGPQyt62kV6nl/PYvIGLZoONVUp7NIHzfZpdJHB0LJRNMScwF0b/zOP7 V3RGMuYzHUzv35rTpD/AyyjMhfTnzB2v6ziLuOuR28eOdPIj2xJFm/IoMLeokP9zC6gdNJkSJy4 6AHeJ1HSn6rMpyXrxZ9l3ew== X-Received: by 2002:a05:600c:d4:b0:488:ac01:72de with SMTP id 5b1f17b1804b1-49240e20de0mr74443425e9.5.1781907119234; Fri, 19 Jun 2026 15:11:59 -0700 (PDT) X-Received: by 2002:a05:600c:d4:b0:488:ac01:72de with SMTP id 5b1f17b1804b1-49240e20de0mr74443145e9.5.1781907118735; Fri, 19 Jun 2026 15:11:58 -0700 (PDT) Received: from maya.myfinge.rs (ifcgrfdd.trafficplex.cloud. [2a10:fc81:a806:d6a9::1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4923fe7ba08sm98230605e9.11.2026.06.19.15.11.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Jun 2026 15:11:58 -0700 (PDT) From: Stefano Brivio To: Jon Maloy Subject: Re: [PATCH v7 13/13] ndp: Support advertising multiple prefixes in Router Advertisements Message-ID: <20260620001156.0d95987a@elisabeth> In-Reply-To: <20260413005319.3295910-14-jmaloy@redhat.com> References: <20260413005319.3295910-1-jmaloy@redhat.com> <20260413005319.3295910-14-jmaloy@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: Sat, 20 Jun 2026 00:11:57 +0200 (CEST) X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: HxnGnKdBSfkKUvKkZJkYHOfL975oj7oeyRgfxi7Fvkg_1781907119 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Message-ID-Hash: BD55FB2DXAP5G5OQS7GPAL3PRUO4QG62 X-Message-ID-Hash: BD55FB2DXAP5G5OQS7GPAL3PRUO4QG62 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: 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 Sun, 12 Apr 2026 20:53:19 -0400 Jon Maloy wrote: > We extend NDP to advertise all suitable IPv6 prefixes in Router > Advertisements, per RFC 4861. Observed and link-local addresses, > plus addresses with a prefix length != 64, are excluded. > > Signed-off-by: Jon Maloy > > --- > v6: -Adapted to previous changes in series > > v7: -Adapted to previous changes in series > -Use struct initializer for source link-layer address option > -Other minor fixes based on feedback from Stefano > --- > conf.c | 19 ++++++--- > fwd.c | 4 ++ > migrate.c | 5 +++ > ndp.c | 123 +++++++++++++++++++++++++++++++++++++----------------- > passt.h | 3 +- > 5 files changed, 108 insertions(+), 46 deletions(-) > > diff --git a/conf.c b/conf.c > index 7c705de..97c66c9 100644 > --- a/conf.c > +++ b/conf.c > @@ -1216,7 +1216,7 @@ static void conf_print(const struct ctx *c) > } > > if (c->ifi6) { > - bool has_dhcpv6 = false; > + bool has_ndp = false, has_dhcpv6 = false; > const char *head; > > if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.map_host_loopback)) > @@ -1225,18 +1225,25 @@ static void conf_print(const struct ctx *c) > buf, sizeof(buf))); > > for_each_addr(a, c->addrs, c->addr_count, AF_INET6) { > + if (a->flags & CONF_ADDR_SLAAC) > + has_ndp = true; > if (a->flags & CONF_ADDR_DHCPV6) > has_dhcpv6 = true; > } > > - if (c->no_ndp && !has_dhcpv6) > + if (!has_ndp && !has_dhcpv6) > goto dns6; > > - a = fwd_get_addr(c, AF_INET6, 0, CONF_ADDR_LINKLOCAL); > - if (!c->no_ndp && a) { > + if (has_ndp) { > info("NDP:"); > - info(" assign: %s", > - inany_ntop(&a->addr, buf, sizeof(buf))); > + head = "assign: "; > + for_each_addr(a, c->addrs, c->addr_count, AF_INET6) { > + if (!(a->flags & CONF_ADDR_SLAAC)) > + continue; > + inany_ntop(&a->addr, buf, sizeof(buf)); > + info(" %s: %s/%d", head, buf, a->prefix_len); > + head = " "; > + } > } > > if (has_dhcpv6) { > diff --git a/fwd.c b/fwd.c > index 2b444fb..2bc8e33 100644 > --- a/fwd.c > +++ b/fwd.c > @@ -302,6 +302,10 @@ void fwd_set_addr(struct ctx *c, const union inany_addr *addr, > } else if (!(flags & CONF_ADDR_LINKLOCAL)) { > if (!c->no_dhcpv6) > flags |= CONF_ADDR_DHCPV6; > + > + /* NDP/RA only if prefix is /64 */ > + if (!c->no_ndp && prefix_len == 64) > + flags |= CONF_ADDR_SLAAC; > } > > /* Add to head or tail, depending on flag */ > diff --git a/migrate.c b/migrate.c > index adcbc63..f019924 100644 > --- a/migrate.c > +++ b/migrate.c > @@ -54,6 +54,7 @@ struct migrate_seen_addrs_v2 { > #define MIGRATE_ADDR_OBSERVED BIT(3) > #define MIGRATE_ADDR_DHCP BIT(4) > #define MIGRATE_ADDR_DHCPV6 BIT(5) > +#define MIGRATE_ADDR_SLAAC BIT(6) > > /** > * struct migrate_addr_v3 - Migration format for a single address entry > @@ -89,6 +90,8 @@ static uint8_t flags_to_migration(uint8_t flags) > migration |= MIGRATE_ADDR_DHCP; > if (flags & CONF_ADDR_DHCPV6) > migration |= MIGRATE_ADDR_DHCPV6; > + if (flags & CONF_ADDR_SLAAC) > + migration |= MIGRATE_ADDR_SLAAC; > > return migration; > } > @@ -115,6 +118,8 @@ static uint8_t flags_from_migration(uint8_t migration) > flags |= CONF_ADDR_DHCP; > if (migration & MIGRATE_ADDR_DHCPV6) > flags |= CONF_ADDR_DHCPV6; > + if (migration & MIGRATE_ADDR_SLAAC) > + flags |= CONF_ADDR_SLAAC; > > return flags; > } > diff --git a/ndp.c b/ndp.c > index 3750fc5..bb8374a 100644 > --- a/ndp.c > +++ b/ndp.c > @@ -32,6 +32,8 @@ > #include "passt.h" > #include "tap.h" > #include "log.h" > +#include "fwd.h" > +#include "conf.h" > > #define RT_LIFETIME 65535 > > @@ -99,6 +101,16 @@ struct opt_prefix_info { > uint32_t reserved; > } __attribute__((packed)); > > +/** > + * struct ndp_prefix - Prefix Information option with prefix > + * @info: Prefix Information option header > + * @prefix: IPv6 prefix > + */ > +struct ndp_prefix { > + struct opt_prefix_info info; > + struct in6_addr prefix; > +} __attribute__((__packed__)); I think this needs the aligned(__alignof__(struct in6_addr)) attribute, otherwise direct assignments become unsafe, see commit 36c070e6e320 ("ndp: Use struct assignment in preference to memcpy() for IPv6 addresses"). > + > /** > * struct opt_mtu - Maximum transmission unit (MTU) option > * @header: Option header > @@ -140,27 +152,23 @@ struct opt_dnssl { > } __attribute__((packed)); > > /** > - * struct ndp_ra - NDP Router Advertisement (RA) message > + * struct ndp_ra_hdr - NDP Router Advertisement fixed header > * @ih: ICMPv6 header > * @reachable: Reachability time, after confirmation (ms) > * @retrans: Time between retransmitted NS messages (ms) > - * @prefix_info: Prefix Information option > - * @prefix: IPv6 prefix > - * @mtu: MTU option > - * @source_ll: Target link-layer address > - * @var: Variable fields > */ > -struct ndp_ra { > +struct ndp_ra_hdr { > struct icmp6hdr ih; > uint32_t reachable; > uint32_t retrans; > - struct opt_prefix_info prefix_info; > - struct in6_addr prefix; > - struct opt_l2_addr source_ll; > +} __attribute__((__packed__)); > > - unsigned char var[sizeof(struct opt_mtu) + sizeof(struct opt_rdnss) + > - sizeof(struct opt_dnssl)]; > -} __attribute__((packed, aligned(__alignof__(struct in6_addr)))); > +/* Maximum RA message size: hdr + prefixes + source_ll + mtu + rdnss + dnssl */ > +#define NDP_RA_MAX_SIZE (sizeof(struct ndp_ra_hdr) + \ > + MAX_GUEST_ADDRS * sizeof(struct ndp_prefix) + \ > + sizeof(struct opt_l2_addr) + \ > + sizeof(struct opt_mtu) + sizeof(struct opt_rdnss) + \ > + sizeof(struct opt_dnssl)) > > /** > * struct ndp_ns - NDP Neighbor Solicitation (NS) message > @@ -231,6 +239,42 @@ void ndp_unsolicited_na(const struct ctx *c, const struct in6_addr *addr) > ndp_na(c, &in6addr_ll_all_nodes, addr); > } > > +/** > + * ndp_prefix_fill() - Fill prefix options for all suitable addresses > + * @c: Execution context > + * @buf: Buffer to write prefix options into > + * > + * Fills buffer with Prefix Information options for all non-linklocal, Nit: non-link-local. > + * non-observed addresses with prefix_len == 64 Nit: == is C, not English ("64 as prefix_len"). > + * > + * Return: number of bytes written > + */ > +static size_t ndp_prefix_fill(const struct ctx *c, unsigned char *buf) > +{ > + const struct guest_addr *a; > + struct ndp_prefix *p; > + size_t offset = 0; > + > + for_each_addr(a, c->addrs, c->addr_count, AF_INET6) { > + if (!(a->flags & CONF_ADDR_SLAAC)) > + continue; > + > + p = (struct ndp_prefix *)(buf + offset); > + p->info.header.type = OPT_PREFIX_INFO; > + p->info.header.len = 4; /* 4 * 8 = 32 bytes */ It took us quite a bit to finally get a more declarative approach in the assignment of these, see commit c16141eda5e8 ("ndp.c: Turn NDP responder into more declarative implementation"), and this essentially reverts that. Can't we have an initialiser for the fixed parts and use that instead? > + p->info.prefix_len = 64; > + p->info.prefix_flags = 0xc0; /* L, A flags */ > + p->info.valid_lifetime = ~0U; > + p->info.pref_lifetime = ~0U; > + p->info.reserved = 0; > + p->prefix = a->addr.a6; > + > + offset += sizeof(struct ndp_prefix); > + } > + > + return offset; > +} > + > /** > * ndp_ra() - Send an NDP Router Advertisement (RA) message > * @c: Execution context > @@ -238,7 +282,15 @@ void ndp_unsolicited_na(const struct ctx *c, const struct in6_addr *addr) > */ > static void ndp_ra(const struct ctx *c, const struct in6_addr *dst) > { > - struct ndp_ra ra = { > + unsigned char buf[NDP_RA_MAX_SIZE] > + __attribute__((__aligned__(__alignof__(struct in6_addr)))); > + struct ndp_ra_hdr *hdr = (struct ndp_ra_hdr *)buf; > + struct opt_l2_addr *source_ll; > + unsigned char *ptr; > + size_t prefix_len; > + > + /* Build RA header */ > + *hdr = (struct ndp_ra_hdr){ > .ih = { > .icmp6_type = RA, > .icmp6_code = 0, > @@ -247,31 +299,26 @@ static void ndp_ra(const struct ctx *c, const struct in6_addr *dst) > .icmp6_rt_lifetime = htons_constant(RT_LIFETIME), > .icmp6_addrconf_managed = 1, > }, > - .prefix_info = { > - .header = { > - .type = OPT_PREFIX_INFO, > - .len = 4, > - }, > - .prefix_len = 64, > - .prefix_flags = 0xc0, /* prefix flags: L, A */ > - .valid_lifetime = ~0U, > - .pref_lifetime = ~0U, > - }, > - .source_ll = { > - .header = { > - .type = OPT_SRC_L2_ADDR, > - .len = 1, > - }, > - }, > }; > - const struct guest_addr *a = fwd_get_addr(c, AF_INET6, 0, 0); > - unsigned char *ptr = NULL; > > - ASSERT(a); > - > - ra.prefix = a->addr.a6; > + /* Fill prefix options */ > + prefix_len = ndp_prefix_fill(c, (unsigned char *)(hdr + 1)); > + if (prefix_len == 0) { > + /* No suitable prefixes to advertise */ > + return; > + } > > - ptr = &ra.var[0]; > + /* Add source link-layer address option */ > + ptr = (unsigned char *)(hdr + 1) + prefix_len; > + source_ll = (struct opt_l2_addr *)ptr; > + *source_ll = (struct opt_l2_addr) { > + .header = { > + .type = OPT_SRC_L2_ADDR, > + .len = 1, > + }, > + }; > + memcpy(source_ll->mac, c->our_tap_mac, ETH_ALEN); > + ptr += sizeof(struct opt_l2_addr); > > if (c->mtu) { > struct opt_mtu *mtu = (struct opt_mtu *)ptr; > @@ -345,10 +392,8 @@ static void ndp_ra(const struct ctx *c, const struct in6_addr *dst) > } > } > > - memcpy(&ra.source_ll.mac, c->our_tap_mac, ETH_ALEN); > - > /* NOLINTNEXTLINE(clang-analyzer-security.PointerSub) */ > - ndp_send(c, dst, &ra, ptr - (unsigned char *)&ra); > + ndp_send(c, dst, buf, ptr - buf); > } > > /** > diff --git a/passt.h b/passt.h > index 028eb7c..2c633e4 100644 > --- a/passt.h > +++ b/passt.h > @@ -84,7 +84,8 @@ struct guest_addr { > #define CONF_ADDR_LINKLOCAL BIT(3) /* Link-local address */ > #define CONF_ADDR_OBSERVED BIT(4) /* Seen in guest traffic */ > #define CONF_ADDR_DHCP BIT(5) /* Advertise via DHCP (IPv4) */ > -#define CONF_ADDR_DHCPV6 BIT(6) /* Advertise via DHCPv6 (IPv6) */ > +#define CONF_ADDR_DHCPV6 BIT(6) /* Advertise via DHCPv6 */ > +#define CONF_ADDR_SLAAC BIT(7) /* Advertise via NDP/RA (/64) */ > }; > > /** -- Stefano