public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
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: [PATCH v5 12/13] ndp: Support advertising multiple prefixes in Router Advertisement
Date: Tue, 10 Mar 2026 15:03:20 +1100	[thread overview]
Message-ID: <aa-YCNj42J0Dwd0K@zatzit> (raw)
In-Reply-To: <20260222174445.743845-13-jmaloy@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 7151 bytes --]

On Sun, Feb 22, 2026 at 12:44:44PM -0500, 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 <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)

Again, this will skip OBSERVED addresses that are also USER|HOST.

> +			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;

Somewhat idiomatic trick you can use here is
	ptr = (char *)(hdr + 1) + 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
> 

-- 
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 --]

  reply	other threads:[~2026-03-10  4:03 UTC|newest]

Thread overview: 30+ 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-03-02 10:22   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 02/13] ip: Introduce for_each_addr() macro for address iteration Jon Maloy
2026-03-02 10:29   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 03/13] fwd: Unify guest accessibility checks with unified address array Jon Maloy
2026-03-02 10:33   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 04/13] arp: Check all configured addresses in ARP filtering Jon Maloy
2026-03-02 10:41   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 05/13] netlink: Return prefix length for IPv6 addresses in nl_addr_get() Jon Maloy
2026-03-02 10:43   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 06/13] conf: Allow multiple -a/--address options per address family Jon Maloy
2026-03-02 10:51   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 07/13] ip: Track observed guest IPv4 addresses in unified address array Jon Maloy
2026-03-03  1:40   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 08/13] ip: Track observed guest IPv6 " Jon Maloy
2026-03-03  1:52   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 09/13] migrate: Rename v1 address functions to v2 for clarity Jon Maloy
2026-03-03  1:53   ` David Gibson
2026-03-03 19:11     ` Stefano Brivio
2026-03-03 22:17       ` David Gibson
2026-03-03 22:56         ` David Gibson
2026-02-22 17:44 ` [PATCH v5 10/13] migrate: Update protocol to v3 for multi-address support Jon Maloy
2026-03-03  4:53   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 11/13] dhcp, dhcpv6: Select addresses for DHCP distribution Jon Maloy
2026-03-03  5:26   ` David Gibson
2026-02-22 17:44 ` [PATCH v5 12/13] ndp: Support advertising multiple prefixes in Router Advertisement Jon Maloy
2026-03-10  4:03   ` David Gibson [this message]
2026-02-22 17:44 ` [PATCH v5 13/13] netlink: Add host-side monitoring for late template interface binding Jon Maloy
2026-03-10  4:04   ` David Gibson

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=aa-YCNj42J0Dwd0K@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).