machines/stellio: deprecate withdrawn DHCPv6-PD prefixes (interim copy of cnx-network fa11327)

After the reboot vlan-lan got its /64 at boot, but IPv6 still failed for
clients: the first PPPoE session lands on another AIS BNG, is torn down
minutes later, and the reconnect brings a different /64 (07:53
2405:9800:b911:91e3 -> 07:57 b910:1f5d). networkd announces the new
prefix at once but never deprecates the old one (systemd#29651), so
clients keep it preferred for up to 30 minutes and source selection
sends every connection from a dead address.

router-radv-deprecate.service watches vlan-* for networkd removing a
dynamic address and sends RAs with that /64 at preferred lifetime 0 via
a short-lived radvd (SIGKILLed: no shutdown RA). Identical script to the
upstream commit; drop once the cnx-network input is bumped past it.

Verified on stellio: the stale prefix flipped to deprecated on the
client at the first RA and pings resumed; a synthetic dynamic address
removed from vlan-lan produced the same RA end to end.
This commit is contained in:
kurogeek committed 2026-10-08 08:13:23 +00:00
1 parent b5077983ea
commit 31ebf2d081
1 file changed
+59
+59
View File
@@ -108,6 +108,65 @@
systemd.network.networks."40-vlan-lan".dhcpPrefixDelegationConfig.SubnetId = lib.mkForce "0x0";
systemd.network.networks."40-vlan-mgmt".dhcpPrefixDelegationConfig.SubnetId = lib.mkForce "0x1";
# Interim copy of cnx-network-clan fa11327 (router/ipv6: deprecate
# withdrawn DHCPv6-PD prefixes); drop when the input is bumped past it.
# networkd never deprecates a prefix it stops announcing (systemd#29651):
# on every reboot the first PPPoE session lands on another AIS BNG, is
# torn down minutes later, and the reconnect brings a different /64.
# networkd announces the new prefix at once but clients keep the old
# one preferred for the rest of its 1800 s lifetime, so RFC 6724 source
# selection picks the dead prefix and every v6 connection fails for up
# to 30 minutes (2026-10-08 07:57). Watch vlan-* for networkd removing a
# dynamic (PD) address and send a few RAs with that /64 at preferred
# lifetime 0; radvd is only the packet generator, killed hard after ~7 s
# so it sends no shutdown RA (router lifetime 0) and does not keep
# deprecating a prefix the ISP re-delegates on reconnect.
systemd.services.router-radv-deprecate = {
description = "Deprecate withdrawn DHCPv6-PD prefixes on the VLANs";
wantedBy = [ "multi-user.target" ];
after = [ "systemd-networkd.service" ];
path = [
pkgs.iproute2
pkgs.ipcalc
pkgs.radvd
];
serviceConfig = {
Restart = "always";
RestartSec = 2;
RuntimeDirectory = "router-radv-deprecate";
};
script = ''
conf=$RUNTIME_DIRECTORY/radvd.conf
pid=$RUNTIME_DIRECTORY/radvd.pid
# "Deleted 4: vlan-lan inet6 2001:db8::1/64 scope global dynamic mngtmpaddr \ valid_lft ..."
ip -o -6 monitor address | while read -r event _ dev _ addr _ scope rest; do
[[ $event == Deleted && $dev == vlan-* && $scope == global && $rest == *dynamic* ]] || continue
prefix="$(ipcalc --no-decorate -n "$addr")/''${addr##*/}"
echo "$dev: deprecating withdrawn prefix $prefix"
cat > "$conf" <<EOF
interface $dev {
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 4;
AdvDefaultLifetime 1800;
AdvCurHopLimit 0;
prefix $prefix {
AdvOnLink on;
AdvAutonomous on;
AdvPreferredLifetime 0;
AdvValidLifetime 7200;
};
};
EOF
rm -f "$pid"
radvd -C "$conf" -p "$pid" -n -m stderr &
sleep 7
kill -KILL $! 2>/dev/null || true
wait $! || true
done
'';
};
systemd.services.heartbeat-push = {
description = "Heartbeat push to uptime monitor";
serviceConfig = {