Static IPv6 addresses inside a DHCPv6-PD prefix become permanently deprecated ~8h after creation — lease lifetimes stamped once, never refreshed on renew (7.23.3)

RB5009UPr+S+, RouterOS 7.23.3 (stable). WAN is PPPoE to a UK ISP (IDNet) with DHCPv6-PD delegating a static /48. The ISP lease has valid = 24h, preferred = 8h. The DHCPv6 client renews reliably.

Three manually configured addresses (from-pool="") inside the delegated /48 each become flagged deprecated (d) roughly 8 hours after being created — i.e. after exactly the lease's preferred lifetime — and never recover, even though renewals keep succeeding. This looks related to the 7.21 change where advertised lifetimes started coming from the pool/lease, but it goes further than the existing threads describe.

The smoking gun — addresses deprecated while the pool (refreshed by a successful renew ~2.5h earlier) still has hours of preferred lifetime left:

/ipv6/address/print detail where global
0   G d address=2a02:xxx:xxxx:71::1/64 from-pool="" interface=bridge1
         advertise=no ...
1   G d address=2a02:xxx:xxxx:222::1/64 from-pool="" interface=wireguard1
         advertise=no ...
2   G d address=2a02:xxx:xxxx:233::1/64 from-pool="" interface=wireguard2
         advertise=no ...

/ipv6/pool/print detail
0 D name="pool-pd1" prefix=2a02:xxx:xxxx::/48 prefix-length=64
     actual-prefix=2a02:xxx:xxxx::/48 valid-lifetime=21h35m20s
     preferred-lifetime=5h35m20s

/ipv6/dhcp-client/print detail
0    interface=pppoe-out1 status=bound duid="0x00030001xxxxxxxxxxxx"
      dhcp-server-v6=fe80::xxxx:xxff:fexx:xxx request=address,prefix
      accept-prefix-without-address=yes add-default-route=no
      use-peer-dns=no validate-server-duid=yes
      pool-name="pool-pd1" pool-prefix-length=64 prefix-hint=::/48
      prefix=2a02:xxx:xxxx::/48, 21h35m20s

Current config (after reverting test changes — the prints above were taken mid-testing while all three addresses were temporarily advertise=no, see finding 2):

/ipv6 address
add address=2a02:xxx:xxxx:71::1 interface=bridge1
add address=2a02:xxx:xxxx:222::1 advertise=no interface=wireguard1
add address=2a02:xxx:xxxx:233::1 advertise=no interface=wireguard2

/ipv6 nd
set [ find default=yes ] advertise-dns=yes dns=2a02:xxx:xxxx:71::1 hop-limit=64 interface=bridge1

/ipv6 dhcp-client
add interface=pppoe-out1 pool-name=pool-pd1 pool-prefix-length=64 prefix-hint=::/48 request=address,prefix use-peer-dns=no

/system scheduler
add name=ipv6-restamp interval=1h start-time=startup on-event=":foreach a in=[/ipv6/address/find where !dynamic] do={/ipv6/address/set \$a advertise=[/ipv6/address/get \$a advertise]}"
  1. The deprecated flag doesn't extend to the RAs. tcpdump on a LAN host while the bridge1 address was flagged deprecated showed the PIO still carrying constant full lifetimes (valid 86400s / pref 28800s — the lease's original values, not the decaying remainder). SLAAC clients stay healthy. The flag is router-internal.
  2. advertise=no clears the flag instantly but does NOT opt the address out of whatever this deprecation mechanism is. All three addresses were set advertise=no; the d flag vanished immediately on each — and ~8h later all three were deprecated again (that is the state shown in the prints above). So any modification of the address entry merely re-stamps the lease lifetime snapshot for another cycle. The stamping applies to every manual address inside the PD prefix, advertised or not. The same is true of disable/enable, and even of a no-op set writing an identical value: each results in one more preferred-lifetime interval.
  3. Static ND prefix entries DO override the lease lifetimes for clients. After removing the dynamic entry (via advertise=no) and adding a static entry with valid=30d preferred=7d, tcpdump shows the PIO advertising valid 2592000s / pref 604800s, and client kernels hold those lifetimes. So the LAN side can be decoupled; the router's own addresses cannot. (Since reverted — not needed once the scheduler below was in place.)
  4. dhcp6-pd-preferred (new in 7.22) is unrelated — it only sets the RFC 9762 P flag in the PIO.

Impact: all of the router's global addresses spend most of their life flagged deprecated, which demotes them in RFC 6724 source-address selection for router-originated traffic, and the state is simply wrong for a manually configured address. There appears to be no configuration that prevents it. Current workaround is the ipv6-restamp scheduler shown in the config above: hourly no-op sets on every static address re-stamp the snapshot before the ~8h can elapse.

Expected behaviour: either renews refresh the lifetimes of manual addresses inside the delegated prefix (as they refresh the pool), or manual addresses keep infinite lifetimes as they did before 7.21.

Supout captured while the fault was live has been submitted to support as SUP-221645.

If you look at the way you created the /ipv6 address entries, there is nothing that connects them to the pool. The fact that the pool refreshes / renews is irrelevant to them. You should create the entries like this if you want them to be tied together:

/ipv6 address
add address=0:0:0:71::1 from-pool=pool-pd1 interface=bridge1
add address=0:0:0:222::1 from-pool=pool-pd1 advertise=no interface=wireguard1
add address=0:0:0:233::1 from-pool=pool-pd1 advertise=no interface=wireguard2

(if you use Subnet-Id 0, instead of 0x71, 0x222, 0x233, then you should include from-pool-policy=strict too)

But please note that if you use /ipv6 address print or WinBox to view the IPv6 -> Addresses table, then the Valid and Preferred columns only show correct values with 7.24+.

With 7.24 you'll see that the static entries you created (with from-pool="") will have the lifetime forever. The one created from the pool will take the lifetime from the pool. But when advertised in RA, the lifetime values will be clamped down by the values under /ipv6 nd prefix default.

You don't need to upgrade to 7.24rc if you want a working behaviour, just set from-pool correctly like I wrote above.

But they don't have lifetime=forever, that's the entire point! I want them to have forever, thus I consciously didn't link them to the pool. This is what I don't understand: not linking them to the pool should give lifetime=forever, and instead it links it to the prefix dynamically created by the DHCP6 client. My old ISP used to give 30d on the prefix, this new ISP gives 24h, and this has somehow triggered a bug in RouterOS.

Linking IPs to the pool means that any hiccough on the WAN renewal side kills IPv6 in my entire network, something I very much do not want. This seems like such a bad design choice, I'm not sure is it a MikroTik thing or a general IPv6 thing...?

Also, don't forget that a no-op "change" to each IP (in this case setting its advertise to yes or no depending on what it is set to) instantly refreshes the IP so it doesn't deprecate -- this in my opinion very strongly implies there's a mechanism in RouterOS that isn't triggering unless you touch the IP config. Hence my hourly script. If I create a static prefix for the e.g. ::71::/64 with 30d/7d all the advertisements on the network become 30/7, but the actual IP on the router stays invisibly tied to the 24h/8h of the DHCP6 prefix, and deprecates within 8h, even though it lands under the more specific static prefix with the much longer lifetime.

As you can see, they already behave like "forever*

Even in 7.24 when they are shown as "forever" the value in the actual RA messages will be clamped down, but because the RA lifetime and RA interval are way shorter, the net result for the clients is that the prefix is "forever".

You will not see the lifetime set to "forever" in the RA messages, and you also don't need that. It's a PITA if RouterOS advertises prefixes with "forever" lifetime, because the prefixes are not remembered across router reboots, and the router has no ability to deprecate prefixes from previous reboot. That was a reason why the advertised lifetimes in RA can even be reduced further from 7.24+

Except they deprecate, and the router stops using them.

I just checked a device running 7.23.3, and I don't see this behavior (interface has static ULA prefix without pool) and the device has an uptime of over 11 days. The address is not marked as deprecated. My other devices are all on 7.24rc4 already, but before that, I didn't have the issue you mentioned, even though the devices have ULA and GUA static address entries without pool.

Try changing the /ipv6 nd prefix default setting and set Valid and Preferred to the same value. All my devices have:

/ipv6 nd prefix default
set preferred-lifetime=10m valid-lifetime=10m

The entries without from-pool take the lifetime from there. If you don't want the deprecated state, then setting preferred == valid will probably solve your issue.

How does one engage with MikroTik support? A previous ticket I logged with them has been untouched for weeks, so I don’t hold much hope for this bug getting fixed any time soon. I genuinely see this as a bug that must be affecting lots of users who may just not be aware that their IPv6 stack is partially broken (because IPv4 still works).

The MikroTik default is 30d/7d and my “not from-pool” IP gets deprecated after 8h, which is the setting incorrectly inherited from my DHCP6 prefix in the presence of a smaller static prefix. On top of that the mechanism that refreshes the lifetime per the incorrect 8h is also not working.

Why are you setting yours to 10 minutes? That means your entire stack breaks any time there’s a brief interruption.

Like I wrote, try 7.24rc4 first, because at least that version fixes the issue with the displayed / printed values of Valid and Preferred.

But I've been using these settings for several years on all my devices:

/ipv6 nd prefix default
set preferred-lifetime=10m valid-lifetime=10m

And have never had the "deprecated" issue you encountered. As long as the router is running, all the statically configured ULA and GUA prefix (that I "borrow" from Hurricane Electric to use internally because they don't suffer from the priority issue of ULA) never become "deprecated" on the devices in the VLANs.

And I need to keep the two default values short because my ISP gives out new GUA prefixes at each redial, which includes each router reboot, and RouterOS doesn't remember the previous prefixes obtained from the ISP across reboot. Keeping long lifetime means my devices retain bogus GUA prefixes every time the router has to reboot (like after an upgrade) and have broken IPv6 connectivity. With the short lifetime, the broken period only lasts at most 10 minutes.

It only breaks if the router is offline for more than 5 minutes. Which never happens for me, except for power outages, but then all other devices are off too.

Again, that short lifetime has never given me IPv6 problems. The only problem I had was when MikroTik decided to do this change in 7.21:

Which unfortunately caused the advertised lifetimes in the RA to be as long as what the DHCPv6 server from the ISP set (which is 5h in my cases). I've complained about this:

V7.21rc [testing] is released! - Announcements - MikroTik community forum

And proposed a compromise:

Which MikroTik has implemented in 7.24:

(in the meantime, I had to use a script to make RouterOS "remember" the prefixes from before a reboot: Ipv6 address deprecation - #20 by CGGXANNX)

Not everyone has the luxury of owning a static GUA prefix.

Anyway, try to set VALID = PREFERRED in the setting and I think your static prefixes will no longer be deprecated.

I don’t understand why — Is this just a workaround you found to yet another bug? Setting them the same means it just fails catastrophically when it fails, rather than giving the deprecation first.

Yeah, I’ve logged the ticket, I just don’t seem to get a response. :sweat_smile:

Deprecation (what Preferred < Valid provides) is only useful when the prefix changes and is still properly routed to you. It's useless if you have a static prefix that never changes, and it really breaks stuffs when like me, you receive the prefix from the ISP and when they give you a new prefix, the previous prefix no longer belongs to you and is not routed to you anymore. In such case I rather all old connections with the old prefix be dropped immediately than trying to cling to an address that no longer has the ability to receive anything.

You only need deprecation when you are planing a slow migration where you still own both the old and new prefixes during the transition. Which never applies to my networks.

Your 10m/10m default is a big part of why this thread is stuck. Deprecated is exactly the window between preferred expiring and valid expiring, so with preferred == valid there is no window to observe, whatever the entry inherits. That setup cannot show the symptom even if it is there.

So I set it up so it could show. RB5009, PPPoE with DHCPv6-PD, first on 7.23.2 and then again after upgrading to 7.24.1. Test addresses on a throwaway bridge with no ports.

Short default set before creating anything:

/ipv6 nd prefix default set preferred-lifetime=2m valid-lifetime=10m

Then three addresses inside the delegated prefix, on the test bridge:

A  from-pool=""            advertise=no
B  from-pool=""            advertise=yes
C  from-pool=<the pd pool> advertise=no

Positive control first, because "nothing happened" is worth nothing if the setting was not live. B generated a dynamic ND prefix that picked up the short values exactly:

1 ID prefix=2a0c:xxxx:xxxx:d4fb::/64 6to4-interface=none interface=v6test
      on-link=yes autonomous=yes dhcp6-pd-preferred=no valid-lifetime=10m
      preferred-lifetime=2m

So the short default was in effect and being consumed. Result after 13 minutes, well past both 2m and 10m, identical on both versions:

Flags: G - GLOBAL
3 G ;;; A
    address=2a0c:xxxx:xxxx:d4fa::1/64 from-pool="" interface=v6test advertise=no
4 G ;;; B
    address=2a0c:xxxx:xxxx:d4fb::1/64 from-pool="" interface=v6test advertise=yes
5 G ;;; C
    address=2a0c:xxxx:xxxx:d401::1/64 from-pool=pool6 interface=v6test advertise=no

Nothing deprecated, nothing removed.

7.23.2 7.24.1
from-pool="" + advertise=no not deprecated not deprecated
from-pool="" + advertise=yes not deprecated not deprecated
from-pool=<pool> + advertise=no not deprecated not deprecated
dynamic ND prefix takes the short default yes yes

/ipv6 nd prefix default feeds the advertised ND prefix and nothing else. It does not set the lifetime of /ipv6 address entries, with or without from-pool, advertised or not. So that part does not hold, and @rotor getting exactly his lease preferred value stamped on his addresses is still the thing that needs explaining.

Now the other half of the advice, upgrade to see the real Valid and Preferred. Two problems with that.

In the CLI those fields do not exist on 7.24.1. get returns the whole property set and there is no lifetime anywhere in it, only a boolean:

> :foreach k,v in=[/ipv6/address/get [find where from-pool=pool6]] do={:put ($k . " = " . $v)}
.id = *65E
actual-interface = bridge
address = 2a0c:xxxx:xxxx:d400::1/64
advertise = false
auto-link-local = true
deprecated = false
disabled = false
dynamic = false
eui-64 = false
from-pool = pool6
global = true
interface = bridge
invalid = false
no-dad = false
slave = false
vrf = main

They do exist in WebFig, and there they show the router uptime. Same value in every row, ticking up in real time, the same for a freshly created address as for fe80:: link-locals:

I created that probe address at 16:39:08 with the router at 00:36:08 of uptime, took the screenshot 31 seconds later, and it reads 00:36:39 like everything else on the page. If the column were the age of the entry, the new one would read 00:00:31. It is the uptime.

So @rotor, whatever is stamping 8h onto your addresses, those columns will not show it to you on 7.24.1. Might be worth adding the uptime thing to your ticket as a separate item. It is trivially reproducible and it happens to be the first field anyone would look at when debugging exactly your problem.

Correction to my own post above, and @CGGXANNX was right after all on this part.

I only had WebFig in front of me earlier. Checked it in WinBox 4.3 on macOS against the same router at the same time, and the two clients disagree completely.

WinBox reads forever on every entry, which is exactly what the 7.24 changelog says it should do:

winbox - show preferred and valid lifetime of IPv6 address also on static IPs;

WebFig on the same box reads the router uptime instead, ticking up in real time, identical on every row including the link-locals.

So the feature works, it is WebFig that renders the wrong field. Which narrows what I said before: the advice to look at those columns is fine, just not in WebFig.

@rotor this is the useful bit for you. Look at your static entries in WinBox rather than WebFig and you should see the actual stamped values there, which is what you need to show the 8h in a ticket. If WinBox does show 8h on entries whose nd prefix default is 30d/7d, that is your evidence in one screenshot.

Reported the WebFig side separately in the 7.24.1 topic.

If you read my previous post, I already wrote why having the ability to "deprecate" addresses (preferred < valid) for OP's setup or most setups is useless and setting the default to have preferred == valid is much more preferable. I also wrote why keeping shorter lifetime is preferable when using RouterOS with ISPs that reassign the allocated IPv6 prefixes.

The fact is that I've been using IPv6 with RouterOS on my devices for a long time (through many many RouterOS upgrades) and never encountered the issue with deprecation of static hardcoded prefixes (not from any pool) like OP has described, and the only deviating thing in my setup compared to OP's with regards to hardcoded IPv6 -> Address assignment without pool is that I have preferred == valid in the /ipv6 nd prefix default.

That /ipv6 address reported bogus address lifetime in WinBox before 7.24 is known (since 7.21 actually). Not only the ones with "forever" lifetime. If you assign an address from a pool that has a lifetime (for example from DHCPv6 client) then you'll see the lifetime value of the addresses decreasing to zero and then start increasing, even when the DHCPv6 lease has been extended. Here's a GIF from 7.22.3:

lifetime-inc

And this is in WinBox. Probably similar to what you now see in WebFig for everything, but I never use WebFig for management. That has also been fixed in 7.24. So, I suggested to OP to upgrade to 7.24 if they want to see correctly reported lifetime for everything (except for WebFig, but didn't know about WebFig because I don't use it).

Of course these fields exist, even in version older than 7.24 here is an output from 7.22:

And here with 7.24.1:

But they obviously only contain timestamp values on non-forever lifetime. If an entry has a lifetime that would be displayed as "forever" in WinBox in 7.24, then the field will contain nothing with the type nil. If you did your test with an IPv6 -> Address table that has a mix of "forever" and real lifetimes, you'll see the fields.

You are right and I was wrong on that one. The properties are called valid and preferred, I went looking for valid-lifetime and preferred-lifetime by analogy with /ipv6 nd prefix, got the same error you get for a property that does not exist, and concluded too fast.

For anyone landing here later:

> :put [:typeof [/ipv6/address/get [find where from-pool=pool6] valid]]
nil

They exist, they are just nil when the lifetime is forever, which is why they never showed up in a get dump on a box where everything is forever. Exactly as you said.

The 7.21 background is useful too, lifetimes counting down to zero and then climbing again. That is the same shape as what WebFig still does, so it looks less like a WebFig bug and more like the old one never got fixed on that client.

On preferred == valid, I take your point that you read it as preventing the problem rather than just hiding it. Those are not mutually exclusive, it may well do both.