Index | Thread | Search

From:
Ryan Vogt <rvogt.ca@gmail.com>
Subject:
Allow rad(8) to advertise shorter lifetimes
To:
tech@openbsd.org
Date:
Tue, 1 Jul 2025 16:11:48 -0700

Download raw body.

Thread
Hi tech@,

I've attached a proposed patch for rad(8). It allows rad to send
advertisements with shorter valid and preferred lifetimes than the
lifetimes otherwise already configured for a prefix discovered on an
interface.

The motivation for the patch is for debugging network setups.
Currently, let's say you have dhcp6leased(8) getting an upstream
prefix-delegation lease, good for 7 days. When rad starts advertising
the prefix, it'll advertise it as having a lifetime of 7 days. In
general, this is good behaviour. But, for debugging, I'd like to be
able to tell rad to advertise a lifetime of *no longer* than
10 minutes, even if the upstream lease is good for more than 10 min.

There was a related discussion [1] back in May 2024 on tech@, in
which florian@ and I had talked about making rad announce lifetimes
bounded by both their static rad.conf(5) setting and any discovered
configured lifetimes.
[1] https://marc.info/?l=openbsd-tech&m=171717272517654

This patch isn't exactly what was discussed back then, but I think
it accomplishes the goal. It introduces a new "configured lifetimes"
option for a prefix in rad.conf, and it defaults to "yes". That
option, when set to "yes", instructs rad: please use any configured
lifetimes (valid/preferred) you find for this prefix instead of the
statically declared lifetimes in rad.conf. If it's set to "no", the
statically declared lifetimes will be used instead, *unless* they're
longer than the configured lifetimes for the prefix (in which case the
shorter configured lifetimes are still used).

Phrased slightly differently: "configured lifetimes yes" instructs
rad to use longer configured lifetimes than the statically declared
lifetimes, if longer ones are there.

After the patch is applied, rad.conf can look like this:

interface em1
{
	auto prefix {
		preferred lifetime 300
		valid lifetime 600
		configured lifetimes no
	}
}

In this case, rad will send advertisements with a preferred lifetime
of 5 minutes and a valid lifetime of 10 minutes, unless there are
shorter lifetimes configured for the prefix (then, rad will use a
shorter preferred lifetime and/or a shorter valid lifetime). Longer
lifetimes configured on em1 will discarded.

As a final note for clarity: existing rad.conf configuration files
will be unaffected -- the default setting of "yes" for
"configured lifetimes" matches the current behaviour of rad.

Thank you very much for taking the time to consider this patch, and
for any feedback provided.

Cheers,
Ryan

diff --git usr.sbin/rad/frontend.c usr.sbin/rad/frontend.c
index 2a519dac0b1..cb955043576 100644
--- usr.sbin/rad/frontend.c
+++ usr.sbin/rad/frontend.c
@@ -89,6 +89,9 @@
 #define	RA_MAX_SIZE		1500
 #define	ROUTE_SOCKET_BUF_SIZE	16384
 
+#define LT_IS_DECAYING(vltime, pltime) \
+    ((vltime) != ND6_INFINITE_LIFETIME || (pltime) != ND6_INFINITE_LIFETIME)
+
 struct icmp6_ev {
 	struct event		 ev;
 	uint8_t			 answer[1500];
@@ -1122,37 +1125,35 @@ add_new_prefix_to_ra_iface(struct ra_iface *ra_iface, struct in6_addr *addr,
     uint32_t decaying_vltime, uint32_t decaying_pltime)
 {
 	struct ra_prefix_conf	*new_ra_prefix_conf;
+	struct ra_prefix_conf	*pc;
+	int			 was_decaying;
 
 	if ((new_ra_prefix_conf = find_ra_prefix_conf(&ra_iface->prefixes, addr,
 	    prefixlen)) != NULL) {
-		if (decaying_vltime != ND6_INFINITE_LIFETIME ||
-		    decaying_pltime != ND6_INFINITE_LIFETIME) {
+		if (new_ra_prefix_conf->decaying_vltime == decaying_vltime &&
+		    new_ra_prefix_conf->decaying_pltime == decaying_pltime) {
+			log_debug("ignoring duplicate %s/%d prefix",
+			    in6_to_str(addr), prefixlen);
+			return;
+		}
+		was_decaying = LT_IS_DECAYING(
+		    new_ra_prefix_conf->decaying_vltime,
+		    new_ra_prefix_conf->decaying_pltime);
+		new_ra_prefix_conf->decaying_vltime = decaying_vltime;
+		new_ra_prefix_conf->decaying_pltime = decaying_pltime;
+		if (LT_IS_DECAYING(decaying_vltime, decaying_pltime))
 			ra_iface->ltime_decaying = 1;
-			new_ra_prefix_conf->ltime_decaying = 0;
-			if (decaying_vltime != ND6_INFINITE_LIFETIME) {
-				new_ra_prefix_conf->vltime = decaying_vltime;
-				new_ra_prefix_conf->ltime_decaying |=
-				    VLTIME_DECAYING;
-			}
-			if (decaying_pltime != ND6_INFINITE_LIFETIME) {
-				new_ra_prefix_conf->pltime = decaying_pltime;
-				new_ra_prefix_conf->ltime_decaying |=
-				    PLTIME_DECAYING;
-			}
-		} else if (new_ra_prefix_conf->ltime_decaying) {
-			struct ra_prefix_conf *pc;
-
-			new_ra_prefix_conf->ltime_decaying = 0;
+		else if (was_decaying) {
 			ra_iface->ltime_decaying = 0;
 			SIMPLEQ_FOREACH(pc, &ra_iface->prefixes, entry) {
-				if (pc->ltime_decaying) {
+				if (LT_IS_DECAYING(pc->vltime, pc->pltime)) {
 					ra_iface->ltime_decaying = 1;
 					break;
 				}
 			}
-		} else
-			log_debug("ignoring duplicate %s/%d prefix",
-			    in6_to_str(addr), prefixlen);
+		}
+		log_debug("updating %s/%d prefix lifetimes", in6_to_str(addr),
+		    prefixlen);
 		return;
 	}
 
@@ -1164,18 +1165,11 @@ add_new_prefix_to_ra_iface(struct ra_iface *ra_iface, struct in6_addr *addr,
 	new_ra_prefix_conf->prefixlen = prefixlen;
 	new_ra_prefix_conf->vltime = ra_prefix_conf->vltime;
 	new_ra_prefix_conf->pltime = ra_prefix_conf->pltime;
-	if (decaying_vltime != ND6_INFINITE_LIFETIME ||
-	    decaying_pltime != ND6_INFINITE_LIFETIME) {
+	new_ra_prefix_conf->decaying_vltime = decaying_vltime;
+	new_ra_prefix_conf->decaying_pltime = decaying_pltime;
+	if (LT_IS_DECAYING(decaying_vltime, decaying_pltime))
 		ra_iface->ltime_decaying = 1;
-		if (decaying_vltime != ND6_INFINITE_LIFETIME) {
-			new_ra_prefix_conf->vltime = decaying_vltime;
-			new_ra_prefix_conf->ltime_decaying |= VLTIME_DECAYING;
-		}
-		if (decaying_pltime != ND6_INFINITE_LIFETIME) {
-			new_ra_prefix_conf->pltime = decaying_pltime;
-			new_ra_prefix_conf->ltime_decaying |= PLTIME_DECAYING;
-		}
-	}
+	new_ra_prefix_conf->cfgltimes = ra_prefix_conf->cfgltimes;
 	new_ra_prefix_conf->aflag = ra_prefix_conf->aflag;
 	new_ra_prefix_conf->lflag = ra_prefix_conf->lflag;
 	SIMPLEQ_INSERT_TAIL(&ra_iface->prefixes, new_ra_prefix_conf, entry);
@@ -1294,14 +1288,22 @@ build_packet(struct ra_iface *ra_iface)
 			ndopt_pi->nd_opt_pi_flags_reserved |=
 			    ND_OPT_PI_FLAG_AUTO;
 
-		if (ra_prefix_conf->ltime_decaying & VLTIME_DECAYING)
-			vltime = ra_prefix_conf->vltime < t ? 0 :
-			    ra_prefix_conf->vltime - t;
+		if (ra_prefix_conf->decaying_vltime != ND6_INFINITE_LIFETIME) {
+			vltime = ra_prefix_conf->decaying_vltime < t ? 0 :
+			    ra_prefix_conf->decaying_vltime - t;
+			if (vltime > ra_prefix_conf->vltime &&
+			    !ra_prefix_conf->cfgltimes)
+			    vltime = ra_prefix_conf->vltime;
+		}
 		else
 			vltime = ra_prefix_conf->vltime;
-		if (ra_prefix_conf->ltime_decaying & PLTIME_DECAYING)
-			pltime = ra_prefix_conf->pltime < t ? 0 :
-			    ra_prefix_conf->pltime - t;
+		if (ra_prefix_conf->decaying_pltime != ND6_INFINITE_LIFETIME) {
+			pltime = ra_prefix_conf->decaying_pltime < t ? 0 :
+			    ra_prefix_conf->decaying_pltime - t;
+			if (pltime > ra_prefix_conf->pltime &&
+			    !ra_prefix_conf->cfgltimes)
+			    pltime = ra_prefix_conf->pltime;
+		}
 		else
 			pltime = ra_prefix_conf->pltime;
 
diff --git usr.sbin/rad/parse.y usr.sbin/rad/parse.y
index bf6b6e6b86d..d4d058491dd 100644
--- usr.sbin/rad/parse.y
+++ usr.sbin/rad/parse.y
@@ -122,7 +122,7 @@ typedef struct {
 %token	CONFIGURATION OTHER LIFETIME REACHABLE TIME RETRANS TIMER
 %token	AUTO PREFIX VALID PREFERENCE PREFERRED LIFETIME ONLINK AUTONOMOUS
 %token	ADDRESS_CONFIGURATION DNS NAMESERVER SEARCH MTU NAT64 HIGH MEDIUM LOW
-%token	SOURCE LINK_LAYER
+%token	SOURCE LINK_LAYER CONFIGURED LIFETIMES
 
 %token	<v.string>	STRING
 %token	<v.number>	NUMBER
@@ -368,6 +368,9 @@ ra_prefixoptsl	: VALID LIFETIME NUMBER {
 		| PREFERRED LIFETIME NUMBER {
 			ra_prefix_conf->pltime = $3;
 		}
+		| CONFIGURED LIFETIMES yesno {
+			ra_prefix_conf->cfgltimes = $3;
+		}
 		| ONLINK yesno {
 			ra_prefix_conf->lflag = $2;
 		}
@@ -519,6 +522,7 @@ lookup(char *s)
 		{"auto",		AUTO},
 		{"autonomous",		AUTONOMOUS},
 		{"configuration",	CONFIGURATION},
+		{"configured",		CONFIGURED},
 		{"default",		DEFAULT},
 		{"dns",			DNS},
 		{"high",		HIGH},
@@ -526,6 +530,7 @@ lookup(char *s)
 		{"include",		INCLUDE},
 		{"interface",		RA_IFACE},
 		{"lifetime",		LIFETIME},
+		{"lifetimes",		LIFETIMES},
 		{"limit",		LIMIT},
 		{"link-layer",		LINK_LAYER},
 		{"low",			LOW},
@@ -1087,6 +1092,7 @@ conf_get_ra_prefix(struct in6_addr *addr, int prefixlen)
 	prefix->prefixlen = prefixlen;
 	prefix->vltime = ADV_VALID_LIFETIME;
 	prefix->pltime = ADV_PREFERRED_LIFETIME;
+	prefix->cfgltimes = 1;
 	prefix->lflag = 1;
 	prefix->aflag = 1;
 
diff --git usr.sbin/rad/rad.conf.5 usr.sbin/rad/rad.conf.5
index bf684759e11..292070c7c60 100644
--- usr.sbin/rad/rad.conf.5
+++ usr.sbin/rad/rad.conf.5
@@ -168,6 +168,20 @@ options are as follows:
 This prefix can be used to generate IPv6 addresses.
 The default is
 .Cm yes .
+.It Ic configured lifetimes Pq Cm yes Ns | Ns Cm no
+If the prefix is discovered from a network interface and it has configured
+valid or preferred lifetimes, use the configured lifetimes instead of those
+specified in the static
+.Ic preferred lifetime
+and
+.Ic valid lifetime
+options.
+Even if set to
+.Cm no ,
+static lifetimes will never be used when they are longer than the
+prefix's configured lifetimes.
+The default is
+.Cm yes .
 .It Ic on-link Pq Cm yes Ns | Ns Cm no
 This prefix is considered on-link.
 The default is
@@ -176,14 +190,10 @@ The default is
 The preferred lifetime (pltime) in seconds for addresses generated from this
 prefix.
 The default is 2700.
-This option is ignored if the prefix is discovered from a network interface
-and it has a preferred lifetime configured.
 .It Ic valid lifetime Ar seconds
 The valid lifetime (vltime) in seconds for addresses generated from this
 prefix.
 The default is 5400.
-This option is ignored if the prefix is discovered from a network interface
-and it has a valid lifetime configured.
 .El
 .El
 .El
diff --git usr.sbin/rad/rad.h usr.sbin/rad/rad.h
index 7775ac4a0aa..5f7b79fb38b 100644
--- usr.sbin/rad/rad.h
+++ usr.sbin/rad/rad.h
@@ -35,8 +35,6 @@
 #define	MIN_DELAY_BETWEEN_RAS	3	/* 3 seconds */
 #define	MAX_SEARCH		1025	/* MAXDNAME in arpa/nameser.h */
 #define	DEFAULT_RDNS_LIFETIME	3 * MAX_RTR_ADV_INTERVAL
-#define	PLTIME_DECAYING		1
-#define	VLTIME_DECAYING		2
 
 #define	IMSG_DATA_SIZE(imsg)	((imsg).hdr.len - IMSG_HEADER_SIZE)
 
@@ -116,7 +114,9 @@ struct ra_prefix_conf {
 	int				 prefixlen;	/* prefix length */
 	uint32_t			 vltime;	/* valid lifetime */
 	uint32_t			 pltime;	/* preferred lifetime */
-	int				 ltime_decaying;
+	uint32_t			 decaying_vltime;
+	uint32_t			 decaying_pltime;
+	int				 cfgltimes;	/* config'd lifetimes */
 	int				 lflag;		/* on-link flag*/
 	int				 aflag;		/* autonom. addr flag */
 };