Routed IPv6 for a segmented IPv4-only LAN behind CGNAT
Free routed /56 over Route64's WireGuard tunnel — a native global /64 per VLAN, no VPS, with a fast fail-to-IPv4 on outage. The no-VPS companion to the CGNAT build log.
Overview
The Route64 path in the MikroTik RB5009 home-network series: recover
real, routable IPv6 over a CGNAT'd line using the free Route64
broker's WireGuard tunnel and a routed /56. Nothing recurring, no endpoint
to operate. The sibling — a self-operated VPS routing a /48 — is its own
post: Routed IPv6 over CGNAT via a VPS. Read
the index's path-choice matrix
first if you have not decided between them.
This post assumes you already have a working, segmented IPv4 LAN behind
carrier-grade NAT — VLANs, inter-VLAN firewall, DHCP — from the
VLAN companion post.
Route64 hands out a routed /56 over WireGuard. Because the
transport is plain outbound UDP, it traverses residential CGNAT — verified
on a live Converge line, not inferred. A /56 is 256 /64s: enough to give
every VLAN its own native global prefix straight from the delegation, with
no helper daemon and no second prefix for clients to choose between. The
single trade-off is designed-for: Route64 is the only IPv6 uplink, so
when it is down the LAN fails fast and cleanly back to IPv4 rather than
blackholing.
Every numbered section is paste-ready against a RouterOS v7 box that
already has the VLAN segmentation from the companion post. The italic notes
are the rationale — the trade-off being made and why.
1. Topology and what you need first
Internet (IPv4 + IPv6)
│
┌─────────────┴─────────────┐
│ Route64 PoP (WireGuard) │
│ routes a /56 to you │
└─────────────┬─────────────┘
│ WireGuard / outbound UDP
│ (only IPv6 transits the tunnel)
┌─────────────┴─────────────┐
│ Residential fiber+CGNAT │
│ (IPv4 outbound only) │
└─────────────┬─────────────┘
│
┌───────────┴───────────┐
│ home router │
│ wg-route64 ::2/64 │ ← tunnel link
│ bridge <56>:1::1 │ ← LAN GUA + ULA
│ vlan-iot <56>:10::1 │ ← IoT GUA + ULA
│ vlan-gst <56>:20::1 │ ← Guest GUA + ULA
└───────────────────────┘
This post starts where the segmentation post ends. You should already have:
the bridge/vlan-iot/vlan-guest interfaces, the IPv4 inter-VLAN firewall,
per-VLAN DHCPv4, and a working DoH resolver advertised on a ULA. If you do
not, build the VLAN companion post and the
DNS companion post first; only
the IPv6 layer differs here.
2. Conventions and placeholders
Substitute every <PLACEHOLDER> before pasting. Comments are single tokens
so the netwatch scripts need no nested quotes.
Placeholder
Meaning
<R64_PRIVKEY>
Client (home router) WireGuard private key from the Route64 tunnel page.
<R64_SRV_PUBKEY>
Route64 server public key.
<R64_POP_IP>
Route64 PoP endpoint IPv4.
<R64_POP_PORT>
Route64 PoP endpoint UDP port (also the local listen-port).
<R64_LINK>
Tunnel-link /64 prefix. Client is <R64_LINK>::2, gateway <R64_LINK>::1.
<R64_56>
Routed /56 written without its trailing subnet byte, e.g. 2001:db8:abcd:c0.
<ULA_PREFIX>
Your locally-generated ULA, e.g. fd96:7d0b:7dc2. Same one the DNS section uses.
<R64_TUNNEL_ID>
Numeric Route64 tunnel ID (DynDNS hostname).
<R64_USER> / <R64_APIKEY>
Route64 account user and API key for the DynDNS endpoint.
<R64_56> is the one to read carefully. Route64 delegates a /56 such as
2001:db8:abcd:c000::/56; the usable LAN /64s are
2001:db8:abcd:c001::/64 … 2001:db8:abcd:c0ff::/64. Write <R64_56> as
the part before the subnet byte (2001:db8:abcd:c0) so <R64_56>01::1
expands to 2001:db8:abcd:c001::1. This guide uses 01/10/20 to echo
the VLAN numbering.
3. Route64 dashboard
Create a tunnel of type WireGuard and note: the PoP endpoint:port, the
server public key, the client private key, the tunnel-link /64 (the
point-to-point pair, client ::2, gateway ::1), the routed /56
delegated for LAN use — a different prefix from the link /64 — the
numeric tunnel ID, and an API key for the DynDNS endpoint. The portal asks
for a public IPv4; behind CGNAT that value is not used (see the design
note above).
4. WireGuard client
The home router always initiates; ::/0 in allowed-address lets the peer
carry any IPv6 (the path is a routing decision, not set here). The
tunnel-link address is advertise=no — it is point-to-point, no SLAAC.
WireGuard, not 6in4: the obvious free broker is Hurricane Electric, but its
6in4 tunnel (IP protocol 41) has no UDP/TCP ports for a carrier's NAT44
to track and is dropped behind CGNAT — it transmits fine, nothing ever
returns, confirmed on the live line. Route64's WireGuard is stateful outbound
UDP: the home router always initiates and the responder replies to whatever source
sent a valid key-authenticated handshake, so the registered portal IPv4 is
hygiene, not a dependency. persistent-keepalive=15s then keeps the carrier's
NAT mapping fresh and Route64's learned endpoint under 15 s old, so the
tunnel roams across a changing CGNAT egress without re-registration; a
deliberately wrong portal pin was tested on both an established and a fully
cold handshake and both worked.
5. Default route — single uplink
There is no second IPv6 path, so the Route64 default is just the default.
netwatch probes Cloudflare's IPv6 anycast on TCP 443 — reachable
only through the Route64 default. On failure it sets ra-lifetime=0 on
every GUA-bearing VLAN. A Router Lifetime of zero tells hosts "I am not
a default router": they purge the IPv6 default and Happy Eyeballs
to IPv4 within seconds. On recovery it restores ra-lifetime=30m. The
default route stays installed throughout — only the Router Lifetime
toggles.
Home router — netwatch drains LAN RA on Route64 outage
bash
1/tool/netwatch addname=r64health type=tcp-conn \2host=2606:4700:4700::1111 port=443\3interval=5s timeout=2s comment=r64health \4 up-script="/ipv6/nd set [find interface=bridge] ra-lifetime=30m; /ipv6/nd set [find interface=vlan-iot] ra-lifetime=30m; /ipv6/nd set [find interface=vlan-guest] ra-lifetime=30m; :log info route64-up"\5 down-script="/ipv6/nd set [find interface=bridge] ra-lifetime=0s; /ipv6/nd set [find interface=vlan-iot] ra-lifetime=0s; /ipv6/nd set [find interface=vlan-guest] ra-lifetime=0s; :log info route64-down-failedtoIPv4"
The set [find interface=...] lines are no-ops until the per-VLAN ND
entries exist; they start working as soon as the
Per-VLAN IPv6 post is applied (substitution:
<GUA_LAN>=<R64_56>01, <GUA_IOT>=<R64_56>10, <GUA_GUEST>=<R64_56>20).
After the index §5 ULA-only update
strips Guest's GUA, the vlan-guest line above becomes a no-op (the VLAN
has no global default to withdraw); leave it in for symmetry or drop it.
End-to-end failover is netwatch detection (~5–7 s at interval=5s timeout=2s) plus the ~0.6 s unsolicited RA RouterOS emits whenever
ra-lifetime changes ≈ ~6–8 s, symmetric on recovery — no
dependence on ra-interval. type=tcp-conn is used because RouterOS
7.22's IPv6 ICMP netwatch stays stuck at down even for reachable v6
hosts (the v4 variant works fine); a TCP connect to Cloudflare's HTTPS
port exits via whatever default is active and confirms end-to-end v6
reachability. The default route stays installed across outages — only
the Router Lifetime toggles, which withdraws the router as a default
for all IPv6, so router-forwarded inter-VLAN IPv6 also pauses during
the outage. Same-VLAN IPv6 and the ULA resolver are on-link and keep
working throughout.
7. DynDNS hygiene (optional)
Not required — the tunnel survives a wrong pin on both established and cold
handshakes. Kept only to keep the portal accurate, on a 15-minute scheduler
(Route64 rate-limits endpoint changes to one per 10 min; nochg is
unmetered, so 15 min never trips it). start-time=startup corrects a
stale pin after a reboot.
The checks below prove the WireGuard tunnel, the default route, and the
netwatch are correct. LAN-side checks (client GUA, ping6 from a VLAN,
anti-spoof drop counter) live in the
Per-VLAN IPv6 post; the
one-glance path-agnostic check is the screenshot at the
index §7.
Tunnel + netwatch smoke tests
bash
1# Tunnel handshake is recent and bytes are moving2/interface/wireguard/peers print where interface=wg-route64
34# Egress from the router itself — proves native routing through the tunnel5/ping 2606:4700:4700::1111 count=467# Single default, via Route648/ipv6/route print where dst-address=::/0
910# netwatch health + the fail/recover log trail11/tool/netwatch print where comment=r64health
12/log/print where message~"route64-(up|down)"
Pulling the WireGuard peer's endpoint (or briefly disabling wg-route64)
should produce a route64-down-failedtoIPv4 log line within ~8 s. The
full ≈40 s client-side failover lands once per-VLAN IPv6 is in
place — see that post's verification block.
9. Caveats
Single uplink. A Route64 outage is a full IPv6 outage; the network
fails fast to IPv4 by design.
Inbound reachability is best-effort. Inbound v6 to the GUA does
work — verified end-to-end on this build — and Route64's ToS does not
forbid running listeners. What it does not promise is service levels
(§10): the path runs through a broker PoP, so reachability is only as
good as the broker's. Fine for personal-grade services; for anything
you would page on, put a self-operated relay in front.
Throughput cap. Route64's ToS §14
documents "up to 200M throughput often more," extendable via donations.
Fine for general home egress; sized below modern fiber peak.
No filesharing. Route64's ToS §6 explicitly forbids "bittorent or
similar" filesharing over the tunnel. Run those over native IPv4.
ULA addressing survives an outage; routed IPv6 does not. Hosts keep
their <ULA_PREFIX> addresses and same-VLAN/on-link IPv6 (including the
ULA DNS resolver) through a Route64 outage.
/56, not /48. 256 /64s — ample for a home's VLANs. A /48's 65k only
matters if you sub-delegate at scale, which a home LAN does not.
Free broker. No SLA (ToS §10), no portal 2FA at time of writing,
and use is governed by Route64's
terms of service. Acceptable for a home
LAN's egress IPv6; weigh it for anything you care about.
If you'd rather run this path alongside the VPS path under one
announceable /48 with BGP best-path failover, see
Multi-homing IPv6 over CGNAT on RouterOS
— the series finale. It replaces the WAN-side plumbing of this post
with a self-contained multi-homed build (own ASN, announceable /48 as
hard prerequisites); Route64 keeps its single-uplink role here and
becomes the backup BGP session there.