[Dnsmasq-discuss] [PATCH] radv: explicit lease time as a floor for constructed-prefix RA lifetimes
Simon Kelley
simon at thekelleys.org.uk
Sat Aug 15 11:34:26 UTC 2026
Patch applied.
Cheers,
Simon.
On 09.08.2026 22:56, Giuseppe Piscitelli wrote:
> Simon,
>
> one correction to my v2: the code was unchanged and fine, but the
> man-page hunk had stale/incorrect context and would not apply cleanly
> to current master.
>
> Attached is v3, regenerated and checked against master
> 8d8ad76d896f8eff3bd59dcdb207062c7466a49f. The radv.c change is still
> identical to v1/v2; only the man-page hunk context is corrected.
>
> Verified here with:
> - git diff --check
> - git apply --check
> - make -j2
> - dnsmasq --test
>
> All pass.
>
> Patch inline below as well.
>
> ooonea
>
> diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
> index c557553..2ec9b66 100644
> --- a/man/dnsmasq.8
> +++ b/man/dnsmasq.8
> @@ -1047,7 +1047,10 @@ options. If the lease time is given, then leases
> will be given for that length of time. The lease time is in seconds,
> or minutes (eg 45m) or hours (eg 1h) or days (2d) or weeks (1w) or
> "infinite". If not given,
> the default lease time is one hour for IPv4 and one day for IPv6. The
> -minimum lease time is two minutes. For IPv6 ranges, the lease time
> +minimum lease time is two minutes. An explicitly configured lease time also
> +affects IPv6 router advertisements: it acts as a ceiling on the advertised
> +prefix lifetimes. For constructed ranges it also acts as a floor, unless the
> +prefix is being deprecated. For IPv6 ranges, the lease time
> maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
> lease or router advertisement to zero, which causes clients to use
> other addresses, if available, for new connections as a prelude to renumbering.
> diff --git a/src/radv.c b/src/radv.c
> index 00c2aa7..e31bc25 100644
> --- a/src/radv.c
> +++ b/src/radv.c
> @@ -608,6 +608,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
> int do_slaac = 0;
> int deprecate = 0;
> int constructed = 0;
> + int have_lease = 0;
> int adv_router = 0;
> int off_link = 0;
> unsigned int time = 0xffffffff;
> @@ -660,6 +661,8 @@ static int add_prefixes(struct in6_addr *local, int prefix,
> if (time < ((unsigned int)(3 * param->adv_interval)))
> time = 3 * param->adv_interval;
> }
> + if (context->flags & CONTEXT_SETLEASE)
> + have_lease = 1;
>
> if (context->flags & CONTEXT_DEPRECATE)
> deprecate = 1;
> @@ -697,7 +700,19 @@ static int add_prefixes(struct in6_addr *local,
> int prefix,
> /* configured time is ceiling */
> if (!constructed || valid > time)
> valid = time;
> -
> +
> + /* For CONSTRUCTED prefixes an explicitly configured lease time is
> + also a floor: an upstream delegating with very short lifetimes
> + (mobile links) would otherwise churn the addresses of any client
> + that sleeps through a few RAs. Deprecation still wins below. */
> + if (constructed && have_lease && !deprecate && !(flags & IFACE_DEPRECATED))
> + {
> + if (valid < time)
> + valid = time;
> + if (preferred < time)
> + preferred = time;
> + }
> +
> if (flags & IFACE_DEPRECATED)
> preferred = 0;
>
>
> On Sun, 9 Aug 2026 14:44:32 -0700, Giuseppe Piscitelli <ooonea at gmail.com> wrote:
>> Thanks Simon.
>>
>> Sure — attached is v2 with the man-page update. It documents both the
>> existing ceiling behaviour for explicitly configured lease times in
>> router advertisements and the new floor behaviour for constructed
>> ranges. No code changes from v1.
>>
>> Patch inline below as well.
>>
>> ooonea
>>
>> radv: let an explicit lease time floor constructed-prefix RA lifetimes
>>
>> For constructor: ranges dnsmasq advertises the residual kernel lifetimes
>> of the interface address, and an explicitly configured lease time only
>> caps them. Behind an upstream that delegates with very short lifetimes
>> (Starlink PD: ~300s valid / 150s preferred) every client inherits the
>> churn verbatim, and any device that sleeps through a few RAs (every
>> phone) loses its GUA mid-flow, killing established connections.
>>
>> With this patch an explicitly configured lease time on a constructed
>> range acts as a floor as well. Prefix deprecation is untouched: a
>> deprecated context or kernel-deprecated address still advertises
>> preferred=0, and a vanished prefix still goes through the CONTEXT_OLD
>> path, so renumbering behaves exactly as stock.
>>
>> Document both the existing RA lifetime ceiling and the new floor
>> behaviour for constructed ranges in dnsmasq.8.
>>
>> diff --git a/src/radv.c b/src/radv.c
>> --- a/src/radv.c
>> +++ b/src/radv.c
>> @@ -608,6 +608,7 @@
>> int do_slaac = 0;
>> int deprecate = 0;
>> int constructed = 0;
>> + int have_lease = 0;
>> int adv_router = 0;
>> int off_link = 0;
>> unsigned int time = 0xffffffff;
>> @@ -660,6 +661,8 @@
>> if (time < ((unsigned int)(3 * param->adv_interval)))
>> time = 3 * param->adv_interval;
>> }
>> + if (context->flags & CONTEXT_SETLEASE)
>> + have_lease = 1;
>>
>> if (context->flags & CONTEXT_DEPRECATE)
>> deprecate = 1;
>> @@ -697,7 +700,19 @@
>> /* configured time is ceiling */
>> if (!constructed || valid > time)
>> valid = time;
>> -
>> +
>> + /* For CONSTRUCTED prefixes an explicitly configured lease time is
>> + also a floor: an upstream delegating with very short lifetimes
>> + (mobile links) would otherwise churn the addresses of any client
>> + that sleeps through a few RAs. Deprecation still wins below. */
>> + if (constructed && have_lease && !deprecate && !(flags & IFACE_DEPRECATED))
>> + {
>> + if (valid < time)
>> + valid = time;
>> + if (preferred < time)
>> + preferred = time;
>> + }
>> +
>> if (flags & IFACE_DEPRECATED)
>> preferred = 0;
>>
>> diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
>> --- a/man/dnsmasq.8
>> +++ b/man/dnsmasq.8
>> @@ -1061,1 +1061,5 @@
>> The minimum lease time is two minutes.
>> +An explicitly configured lease time also affects IPv6 router advertisements:
>> +it acts as a ceiling on the advertised prefix lifetimes. For constructed
>> +ranges it also acts as a floor, unless the prefix is being deprecated.
>> +
>>
>> On Sun, 9 Aug 2026 21:47:07 +0100, Simon Kelley <simon at thekelleys.org.uk> wrote:
>>> This looks sensible to me.
>>>
>>> Can I ask for one more contribution? It would really save me time if the
>>> patch included an update to the man page, describing an explicitly set
>>> lease time affects RA, both the existing ceiling behaviour, which is
>>> inexplicably missing :) and the new floor behaviour for constructed ranges.
>>>
>>> This is posted to the list partly because it's generally applicable.
>>> Your patch is more likely to get accepted if you don't make me write the
>>> documentation, people.
>>>
>>> Cheers,
>>>
>>> Simon.
>>>
>>> On 07.08.2026 01:16, Giuseppe Piscitelli wrote:
>>>> Hi,
>>>>
>>>> on constructed prefixes (--dhcp-range=::,constructor:eth0,...) the RA
>>>> carries the residual lifetimes of the interface address, and an
>>>> explicitly configured lease time only caps them ("configured time is
>>>> ceiling" in radv.c).
>>>>
>>>> Behind an upstream that delegates with very short lifetimes this
>>>> propagates the churn to every client. Starlink delegates a /56 with
>>>> about 300s valid / 150s preferred: the router refreshes forever, but a
>>>> client that sleeps through a few RAs -- every phone -- loses its
>>>> global address, and its established connections die with it. Seen live
>>>> on my LAN: phones answering on v4 and link-local while their GUA sat
>>>> FAILED in the router's neighbour cache, apps stalling mid-stream on
>>>> WiFi only.
>>>>
>>>> With this patch an explicitly configured lease time on a constructed
>>>> range acts as a floor as well. Ranges without an explicit lease time
>>>> are unchanged. Deprecation still wins: a kernel-deprecated address or
>>>> a deprecated context still advertises preferred=0, and a vanished
>>>> prefix still goes through the old-prefix path, so renumbering behaves
>>>> as before.
>>>>
>>>> Running in production here on 2.93 with a 12h lease on the constructed
>>>> range: clients hold 12h instead of the 300s residue and the
>>>> sleep-induced address loss is gone. The patch applies cleanly to
>>>> current master.
>>>>
>>>> If changing the meaning of the existing lease-time field on
>>>> constructed ranges is not wanted, I can rework this behind a dedicated
>>>> keyword instead.
>>>>
>>>> Patch inline below and attached too, in case my mailer mangles the
>>>> tabs.
>>>>
>>>> ooonea
>>>>
>>>> --- a/src/radv.c
>>>> +++ b/src/radv.c
>>>> @@ -608,6 +608,7 @@
>>>> int do_slaac = 0;
>>>> int deprecate = 0;
>>>> int constructed = 0;
>>>> + int have_lease = 0;
>>>> int adv_router = 0;
>>>> int off_link = 0;
>>>> unsigned int time = 0xffffffff;
>>>> @@ -660,6 +661,8 @@
>>>> if (time < ((unsigned int)(3 * param->adv_interval)))
>>>> time = 3 * param->adv_interval;
>>>> }
>>>> + if (context->flags & CONTEXT_SETLEASE)
>>>> + have_lease = 1;
>>>>
>>>> if (context->flags & CONTEXT_DEPRECATE)
>>>> deprecate = 1;
>>>> @@ -697,7 +700,19 @@
>>>> /* configured time is ceiling */
>>>> if (!constructed || valid > time)
>>>> valid = time;
>>>> -
>>>> +
>>>> + /* For CONSTRUCTED prefixes an explicitly configured lease time is
>>>> + also a floor: an upstream delegating with very short lifetimes
>>>> + (mobile links) would otherwise churn the addresses of any client
>>>> + that sleeps through a few RAs. Deprecation still wins below. */
>>>> + if (constructed && have_lease && !deprecate && !(flags & IFACE_DEPRECATED))
>>>> + {
>>>> + if (valid < time)
>>>> + valid = time;
>>>> + if (preferred < time)
>>>> + preferred = time;
>>>> + }
>>>> +
>>>> if (flags & IFACE_DEPRECATED)
>>>> preferred = 0;
>>>>
>>>>
>>>> _______________________________________________
>>>> Dnsmasq-discuss mailing list
>>>> Dnsmasq-discuss at lists.thekelleys.org.uk
>>>> https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss
More information about the Dnsmasq-discuss
mailing list