构建一个主要使用IPv6的家庭网络
Building a Mostly IPv6 Only Home Network

原始链接: https://varunpriolkar.com/2026/03/building-a-mostly-ipv6-only-home-network/

## IPv6 专用家庭网络:实用实施 本文详细介绍了向 IPv6 专用家庭网络过渡的过程,承认 IPv4 仍然普遍存在。作者通过利用 NAT64、DNS64 和 464XLAT 等技术,成功地实现了 IPv6 专用设备访问 IPv4 服务。 为了获得静态 IPv6 前缀,从 Free Range Cloud 租用了一个 /48,并通过 WireGuard 隧道传输,以克服 ISP 的动态分配。OPNSense 防火墙管理此隧道的基于策略的路由 (PBR)。IPv6 寻址使用 SLAAC 与 DHCPv6 进行有状态分配,并为每个 Docker 主机提供一个专用的 /56,以及 /64 网络。 重要的是,NAT64/DNS64(使用 Jool 实现)将 IPv6 请求转换为 IPv4,使用 /96 前缀。464XLAT 结合 DHCPv4 选项 108 和 PREF64,允许缺乏原生 IPv6 支持的设备工作,同时优先使用 IPv6。Docker 网络已完全过渡到 IPv6,需要配置调整和 FRR 路由宣告。 虽然总体上是成功的,但一些设备(Eufy、HP 打印机、某些 Docker 容器)缺乏完整的 IPv6 支持仍然存在挑战。一个临时的基于 IPv4 的 VPN 解决方案(Zerotier)提供了从 IPv4 网络访问 IPv6 服务的途径。此设置展示了一条通往未来可证明的、以 IPv6 为中心的家庭网络的可行路径。

最近的 Hacker News 讨论集中在完全过渡到 IPv6 家庭网络的实用性上。一位用户详细介绍了构建这样一个网络的过程(链接:varunpriolkar.com),但评论者对 IPv4 在短期内完全关闭表示怀疑,预计至少还需要十年双栈设置。 提出的担忧包括许多互联网服务仍然依赖 IPv4,导致频繁的回退和中断。一些人质疑 IPv6 除了公网 IP 之外的好处,认为可能会带来麻烦。然而,另一些人强调了诸如简化站点到站点的 VPN 以及关键的*更快的速度*等优势,因为可以绕过 IPv4 中心化隧道和 CG-NAT 的瓶颈。尽管有所改进,IPv6 的广泛采用仍然面临互联网准备不足的障碍。
相关文章

原文

I wanted to switch my home network to IPv6 only design. However the reality is still that there are lot of devices today that only support IPv4 and large parts of Internet are still IPv4 only.

Because of this, many networks still deploy dual stack everywhere. However, I believe there is a case to be made for deploying IPv6 only in your network. Technologies like NAT64, DNS64 and 464XLAT make it possible for IPv6 only networks to access IPv4 services through translation. In this post I explore these technologies and the practical aspects of designing such a IPv6 only home network.

Getting a /48 over dedicated tunnel

My upstream Telus gives me multiple /64s using DHCP-PD. However the issue is that these are dynamic prefixes with a 10 minute lease timer. So if I turn off my router for more than that, there is good chance that the prefix could change. I wanted static prefix for my LAN and I also wanted more prefixes that I could then delegate to other things like Docker and VPN.

So I decided to use Free Range Cloud service provider to lease a /48, which cost me around C$10/year. I also used their tunnel service to then route this over to me. They have a POP in Vancouver, Canada and since I am in Kelowna, this is around 15 ms of latency away. However since most traffic flows through Vancouver anyways, this didn’t add much in terms of latency in reality.

I added the Wireguard details on OPNSense firewall and it started working. I decided to use IPv6 on tunnel and got a IPv6 address from WAN to connect from. This way I am not reliant on IPv4 for connectivity and don’t need to worry about MTU and fragmentation and can just let IPv6 PMTU take care of it.

PBR on OPNSense for LAN traffic

I then added a simple Policy Based Routing(PBR) rule on LAN interface to send traffic through the tunnel. Since there is no rdns setup, I used All Knowing DNS Server to setup rdns for the IP block. I have talked about how to set this up in this post.

IPv6 Addressing – SLAAC and DHCPv6

I decided to use a /64 out of the /48 for my main /48 subnet for my LAN. I then ran SLAAC(Router Advertisements) in Assisted mode with M+O+A flags set. This way clients can get IPv6 address in a stateless way, but I also ran Kea DHCPv6 server which would hand out addresses in stateful way. Unbound then picked up the static leases and then ensured that DNS resolution worked.

I also used /56 on every Docker host, with /64 for each Docker network that was then advertised to my firewall, but I’ll talk about this setup in later section.

  • Main subnet ➡️ 2602:fed2:7e02::/48
  • LAN subnet ➡️ 2602:fed2:7e02:69::/64
  • Example IP for Docker host on LAN ➡️ 2602:fed2:7e02:69::22
  • /56 for Docker host(/64 for each network) ➡️ 2602:fed2:7e02:2200::/56

I also used static addressing for each physical host so that I would not have issues during an outage.

Labeling each device with static address and adding it in authoratative DNS server

At this stage, I am still running a DHCPv4 server to give private IPv4 address to devices. I moved all my servers to IPv6 only, but my printer, vacuum robot and laptop still gets a IPv4 address using DHCPv4 server. My phone still got an IPv4 address at this stage, but this will change in the later section.

IPv6 test results

After setting all of this up, going to a IPv6 test website should give an output like this.

Accessing IPv4 Services over IPv6 – DNS64 and NAT64

There is a way to statefully access IPv6 services over IPv4. The process involves allocating a /96 from the IPv6 pool to hold the entire IPv4 address space. Both of these hold 32 bits of address space so can be used for translation. I used the default 64:ff9b::/96 address pool to keep things simple.

Diagram showing how NAT64+DNS64 is setup

I used a software called Jool on a VM to do the translation. There are other options too like Tayga64, but it works in userspace, unlike Jool that is a kernel module. I passed through an interface bridged to my WAN to this VM so that I could get a public IPv4 address to avoid double NAT. There was also a default route with higher metric to OPNSense as a fallback. You need DNS64 server that can translate DNS records with only A record to AAAA records. Unbound on OPNSense can do this or you can also use NAT64 version of public resolvers. I also changed lowest-ipv6-mtu to 1470 – the value of CLAT interface on Android.

The other thing I had to do was use FRR routing to advertise this prefix to OPNSense. You could get away with static route, but this way I can scale out Jool instances if needed in future.

!
ipv6 route 64:ff9b::/96 Null0
!
router bgp 64420
 bgp router-id 192.168.69.24
 bgp log-neighbor-changes
 no bgp ebgp-requires-policy
 neighbor 2602:fed2:7e02:69::1 remote-as 64420
 neighbor 2602:fed2:7e02:69::1 description OPNSense
 !
 address-family ipv4 unicast
  neighbor 2602:fed2:7e02:69::1 next-hop-self
 exit-address-family
 !
 address-family ipv6 unicast
  network 64:ff9b::/96
  redistribute static
  neighbor 2602:fed2:7e02:69::1 activate
 exit-address-family
exit
!

Once you configure the peer on OPNSense, the route should be visible in the routing table.

FRR injecting 64:ff9b::/96 route into OPNSense routing table

I also had to had a firewall rule above the PBR rule we added above to make it use the system routing table. After this, you can visit any IPv4 website and it should be accessible over IPv6. I use the plugin IPvFoo to know what is going on underneath while visiting the website.

IPvFoo confirming DNS64+NAT64 works

Disabling IPv4 – 464XLAT, PREF64 and DHCPv4 Option 108

Once you have NAT64 and DNS64 working, you could just turn off the DHCPv4 server and IPv4 address on the firewall. However, lot of devices like printer, vacuum robot and IPv4 literal addresses would simply not work. This is where DHCPv4 Option 108 comes into the picture. This simply tells the client that IPv6 only mode is preferred. Clients that do not honor this will get a IPv4 address as normal. Those who do have an option can setup 464XLAT to handle IPv4 literals and disable IPv4 addresses.

Borrowed from Microsoft website – How 464XLAT works

Remember the /96 prefix that we had configured earlier for DNS64? This can be discovered using PREF64 option on SLAAC server. The other way to discover this is by querying ipv4only.arpa domain name. 464XLAT used for handling IPv4 literals has two sides – client side stateless CLAT and stateful NAT64 on server that we setup with Jool above. To summarise, we use the following.

  • 464XLAT – Provides private IP interface on end machine for IPv4 literals. Contains CLAT – Stateless NAT64 on host side and PLAT – Stateful NAT64 on Jool.
  • DHCPv4 Option 108 – Tells client that IPv6 only network is preferred. Client usually enables CLAT if available and disables IPv4 address from DHCPv4. On Linux, you could not enable CLAT and just disable IPv4 if you want.
  • PREF64 – Get the /96 prefix used for DNS64 from Router Advertisement.
  • Query ipv4only.arpa – Used as fallback by some clients to find NAT64 /96 prefix if PREF64 is not available.

Support for OS is as follows.

  • Android – CLAT enabled with DHCPv4 Option 108. Uses PREF64 as main DNS64 prefix discovery method. Queries ipv4only.arpa as fallback method. DNS64 can be disabled if PREF64 is enabled.
  • Linux – CLAT support recently merged in Network Manager – link. Requires DHCPv4 Option 108. Requires PREF64 as DNS64 prefix discovery method. Tested on Ubuntu 26.04: CLAT support hasn’t made it yet, but IPv6 only option works with ipv4.dhcp-ipv6-only-preferred.
  • Windows – CLAT not yet enabled, but in preview. Requires DHCPv4 Option 108. Requires PREF64 as DNS64 prefix discovery method. On latest Windows 11, still get IPv4 address.
  • Apple – CLAT activated with DHCPv4 Option 108 and PREF64. iOS v16 or above supports ipv4only.arpa as fallback for activating CLAT. DNS64 is required for iOS and Safari. I don’t have Apple devices so not tested.

IPv6 on Docker

IPv6 support on Docker is greatly improved. I am using Docker Compose type setup on most of my servers and wanted to avoid having any IPv4 IP addresses even for internal networking unless really necessary. I also wanted to route my Docker GUA prefixes without NAT66 and not use ULA addresses. I added this in my /etc/docker/daemon.json file.

{
  "ipv6": true,
  "default-address-pools": [
    { "base": "2602:fed2:7e02:2200::/56", "size": 64 }
  ],
  "default-network-opts": {"bridge":{"com.docker.network.enable_ipv6":"true"}},
  "bridge": "none",
  "experimental" : true,
  "ip6tables" : false
}

The DHCPv6 address this server got from the DHCP server was 2602:fed2:7e02:69::22 so I decided to use 2602:fed2:7e02:2200::/56 for Docker networks, with /64 being allocated for each network. After that, you can use prefixes in your Docker compose like this.

services:
  upstream:
...
    networks:
      blocky:
 
networks:
  blocky:
    driver: bridge
    enable_ipv4: false
    enable_ipv6: true

You can also pick the /64 to use for that particular network and static addresses for each container if you like. Most services worked fine, but I had issues with a couple and I opened GitHub tickets for them. Most notable of these were Flood and Nextcloud.

I then announced the prefixes to OPNSense using FRR.

!
ipv6 prefix-list home_subnet seq 5 deny 2602:fed2:7e02:69::/64
ipv6 prefix-list home_subnet seq 10 permit 2602:fed2:7e02::/48 le 64
!
route-map HOME_SUBNET permit 10
 match ipv6 address prefix-list home_subnet
exit
!
password zebra
!
router bgp 64420
 bgp router-id 192.168.69.23
 bgp log-neighbor-changes
 no bgp ebgp-requires-policy
 neighbor 2602:fed2:7e02:69::1 remote-as 64420
 neighbor 2602:fed2:7e02:69::1 description OPNSense
 !
 address-family ipv4 unicast
  neighbor 2602:fed2:7e02:69::1 next-hop-self
 exit-address-family
 !
 address-family ipv6 unicast
  redistribute connected route-map HOME_SUBNET
  neighbor 2602:fed2:7e02:69::1 activate
 exit-address-family
exit
!
end

The routes should then show up in OPNSense and work. You may have to create a firewall rule to skip the PBR we set earlier for these routes.

That’s it. I don’t have IPv4 addresses on servers, unless they really need it. Exceptions would be a server that makes connections to Speedtest server to get download speed and Torrent server.

Accessing IPv6 Services over IPv4 Internet

I mostly only have IPv6 deployed on servers and no IPv4. I needed a way to get IPv6 services working over IPv4 internet. Easiest way I found was to just use a VM with a IPv4 address and proxy traffic back over IPv6 VPN. I used Zerotier, which provided ULA IPv6 address over SDN VPN. I disabled IPv4. I simply set routes to appropriate devices over this.

Routing global addresses over Zerotier VPN and terminating SSL over VM is a simple solution

I can now use Caddy to terminate SSL on VM edge or use TCP proxy to send traffic from outside world to internal IPv6 network. There are other solutions like using Cloudflare tunnel, but I found this solution simple and it works well.

List of Issues I found

These are the list of issues I’ve found with running IPv6 so far.

  • Slack timeouts with DNS64+NAT64 Ticket opened and forwarded to internal team for resolution.
  • No IPv6 on Eufy vacuum robot Ticket opened and forwarded to internal team for resolution.
  • No IPv6 only mode and broken intra subnet IPv6 routing on HP printer – Ticket opened and forwarded to internal team for resolution.
  • Broken IPv6 on Docker containers – Flood(fixed), Nextcloud AIO.
  • Disable IPv4 on default Docker bridge – Issue.

I’ll post issues if they come up in my testing.

联系我们 contact @ memedata.com