Use a genuinely random ULA /48 for auto-allocated IPv6 subnets

The old fd00:168:0::/48 base was never actually generated via RFC
4193's randomization procedure -- just a memorable placeholder chosen
to visibly pair with the IPv4 10.168.x.x scheme. Replaced with
fdf0:f243:f06f::/48, a real randomly-generated ULA prefix.

That /48 consumes all three "identity" hextets a ULA prefix has room
for, leaving only the subnet-id (4th) hextet -- the same one the
per-network auto-allocation index already lived in -- with nowhere
left to also place a fixed "168" marker without colliding with either
the random prefix or the index itself. Per the user's own choice
(offered two options): the per-network index is now offset by a
constant 168 instead of matching IPv4's index number-for-number, so
the first auto-allocated network's IPv6 block is
fdf0:f243:f06f:168::/64 (paired with 10.168.0.0/24), second is
...:169::/64 (paired with 10.168.1.0/24), and so on -- deterministic
and still visibly project-stamped, just via a constant offset instead
of an identical digit.

network_subnet.cpp's new ipv6_ula_prefix48/ipv6_subnet_id_base
constants hold the new prefix and offset.

Verified as root via the doas rule: two freshly created extern
networks got fdf0:f243:f06f:168::/64 and fdf0:f243:f06f:169::/64
exactly as expected, correctly paired with their IPv4 subnets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
2026-08-30 18:49:15 +00:00
parent 8a0d800478
commit cf166ed3b4
5 changed files with 100 additions and 33 deletions
+22 -10
View File
@@ -1469,16 +1469,28 @@ Source layout (all under `src/`):
comparison generic over that byte length — IPv4 and IPv6 overlap checking
are the same algorithm, not two parallel implementations.
`allocate_ipv4_subnet()`/`allocate_ipv6_subnet()` (`commands.cpp`'s
`create_network_command()`) scan `10.168.<n>.0/24`/`fd00:168:0:<n>::/64` for
`n` in `0..255` and return the first one that doesn't overlap *any* existing
network's subnet (via the overlap checks above, not just other
auto-allocated ones — a manually `--subnet`-overridden network is checked
too). The same `n` range for both is deliberate, so the common case (no
manual overrides) allocates visibly paired v4/v6 blocks per network — though
since IPv6 hextets are hexadecimal, `n >= 10` renders as a valid but
numerically-different-from-`n` address (e.g. `n=15` becomes `...:15::/64`,
which is hex `0x15` = 21) — purely cosmetic, allocation correctness doesn't
depend on the two matching numerically. `ipv4_gateway_address()`/
`create_network_command()`) scan `10.168.<n>.0/24`/
`fdf0:f243:f06f:<168+n>::/64` for `n` in `0..255` and return the first one
that doesn't overlap *any* existing network's subnet (via the overlap
checks above, not just other auto-allocated ones — a manually
`--subnet`-overridden network is checked too). `fdf0:f243:f06f::/48` is a
randomly generated ULA (RFC 4193) — **replaced the original
`fd00:168:0::/48`, which was never actually randomly generated, just a
memorable placeholder, at the user's own request** ("since this is an
ULA, let's use a randomly generated prefix"); the `168` offset on the
subnet-id hextet (confirmed with the user via `AskUserQuestion`, over the
alternative of dropping it and starting both at `0`) keeps the same
project-recognizable stamp the old scheme's fixed 2nd hextet had, just as
a constant offset now rather than a numerically-identical index — the
same `n` range still drives both v4 and v6 allocation, so the common case
(no manual overrides) still allocates deterministically paired blocks per
network, just offset by `168` on the v6 side instead of matching exactly.
Since IPv6 hextets are hexadecimal, `n + 168 >= 10` (i.e. always, given
the offset) renders as a valid but numerically-different-from-`n + 168`
address when read back as hex (e.g. `n=15``183` decimal → renders as
`...:183::/64`, which is hex `0x183`, not `183`) — purely cosmetic,
allocation correctness doesn't depend on this matching numerically at
all. `ipv4_gateway_address()`/
`ipv6_gateway_address()` (`network_bridge.cpp`'s `provision_bridge()`) and
`ipv4_host_address()`/`ipv6_host_address()` (`network_join.cpp`'s
`pick_free_address()`, see below — `n = 2, 3, ...` for individual