From: Jon Maloy <jmaloy@redhat.com>
To: sbrivio@redhat.com, dgibson@redhat.com,
david@gibson.dropbear.id.au, jmaloy@redhat.com,
passt-dev@passt.top
Subject: [PATCH v5 12/13] ndp: Support advertising multiple prefixes in Router Advertisement
Date: Sun, 22 Feb 2026 12:44:44 -0500 [thread overview]
Message-ID: <20260222174445.743845-13-jmaloy@redhat.com> (raw)
In-Reply-To: <20260222174445.743845-1-jmaloy@redhat.com>
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 <jmaloy@redhat.com>
---
ndp.c | 127 +++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 85 insertions(+), 42 deletions(-)
diff --git a/ndp.c b/ndp.c
index ed8c6ae..cf64f3f 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
@@ -82,7 +84,7 @@ struct ndp_na {
} __attribute__((packed));
/**
- * struct opt_prefix_info - Prefix Information option
+ * struct opt_prefix_info - Prefix Information option header
* @header: Option header
* @prefix_len: The number of leading bits in the Prefix that are valid
* @prefix_flags: Flags associated with the prefix
@@ -99,6 +101,16 @@ struct opt_prefix_info {
uint32_t reserved;
} __attribute__((packed));
+/**
+ * struct ndp_prefix - Complete 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__));
+
/**
* 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: header + prefixes + source_ll + mtu + rdnss + dnssl */
+#define NDP_RA_MAX_SIZE (sizeof(struct ndp_ra_hdr) + \
+ INANY_MAX_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,46 @@ 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,
+ * non-observed addresses with prefix_len == 64 (required for SLAAC).
+ *
+ * Return: number of bytes written
+ */
+static size_t ndp_prefix_fill(const struct ctx *c, unsigned char *buf)
+{
+ int skip = CONF_ADDR_OBSERVED | CONF_ADDR_LINKLOCAL;
+ const struct inany_addr_entry *e;
+ struct ndp_prefix *p;
+ size_t offset = 0;
+
+ for_each_addr(e, c, AF_INET6) {
+ if (e->flags & skip)
+ continue;
+ /* SLAAC requires /64 prefix */
+ if (e->prefix_len != 64)
+ continue;
+
+ p = (struct ndp_prefix *)(buf + offset);
+ p->info.header.type = OPT_PREFIX_INFO;
+ p->info.header.len = 4; /* 4 * 8 = 32 bytes */
+ 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 = e->addr.a6;
+
+ offset += sizeof(struct ndp_prefix);
+ }
+
+ return offset;
+}
+
/**
* ndp_ra() - Send an NDP Router Advertisement (RA) message
* @c: Execution context
@@ -238,7 +286,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,32 +303,22 @@ 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,
- },
- .prefix = IN6ADDR_ANY_INIT,
- .source_ll = {
- .header = {
- .type = OPT_SRC_L2_ADDR,
- .len = 1,
- },
- },
};
- struct inany_addr_entry *e = first_v6(c);
- unsigned char *ptr = NULL;
-
- ASSERT(e);
- ra.prefix = e->addr.a6;
+ /* Fill prefix options */
+ prefix_len = ndp_prefix_fill(c, buf + sizeof(struct ndp_ra_hdr));
+ if (prefix_len == 0) {
+ /* No suitable prefixes to advertise */
+ return;
+ }
- ptr = &ra.var[0];
+ /* Add source link-layer address option */
+ ptr = buf + sizeof(struct ndp_ra_hdr) + prefix_len;
+ source_ll = (struct opt_l2_addr *)ptr;
+ source_ll->header.type = OPT_SRC_L2_ADDR;
+ source_ll->header.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;
@@ -346,10 +392,7 @@ 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);
}
/**
--
2.52.0
next prev parent reply other threads:[~2026-02-22 17:45 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-22 17:44 [PATCH v5 00/13] Introduce multiple addresses and late binding Jon Maloy
2026-02-22 17:44 ` [PATCH v5 01/13] ip: Introduce unified multi-address data structures Jon Maloy
2026-02-22 17:44 ` [PATCH v5 02/13] ip: Introduce for_each_addr() macro for address iteration Jon Maloy
2026-02-22 17:44 ` [PATCH v5 03/13] fwd: Unify guest accessibility checks with unified address array Jon Maloy
2026-02-22 17:44 ` [PATCH v5 04/13] arp: Check all configured addresses in ARP filtering Jon Maloy
2026-02-22 17:44 ` [PATCH v5 05/13] netlink: Return prefix length for IPv6 addresses in nl_addr_get() Jon Maloy
2026-02-22 17:44 ` [PATCH v5 06/13] conf: Allow multiple -a/--address options per address family Jon Maloy
2026-02-22 17:44 ` [PATCH v5 07/13] ip: Track observed guest IPv4 addresses in unified address array Jon Maloy
2026-02-22 17:44 ` [PATCH v5 08/13] ip: Track observed guest IPv6 " Jon Maloy
2026-02-22 17:44 ` [PATCH v5 09/13] migrate: Rename v1 address functions to v2 for clarity Jon Maloy
2026-02-22 17:44 ` [PATCH v5 10/13] migrate: Update protocol to v3 for multi-address support Jon Maloy
2026-02-22 17:44 ` [PATCH v5 11/13] dhcp, dhcpv6: Select addresses for DHCP distribution Jon Maloy
2026-02-22 17:44 ` Jon Maloy [this message]
2026-02-22 17:44 ` [PATCH v5 13/13] netlink: Add host-side monitoring for late template interface binding 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=20260222174445.743845-13-jmaloy@redhat.com \
--to=jmaloy@redhat.com \
--cc=david@gibson.dropbear.id.au \
--cc=dgibson@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).