Two LANs, one WireGuard hub, and an IPv6 address plan that has to answer three separate questions: which prefix, which address belongs to each router, and which peer owns which subnet. Getting one of them wrong produces a tunnel that handshakes and routes nothing. The plan below works through a private two-site design in that order. It deliberately does not provide IPv6 Internet access.

Decide What the Prefix Is For

A unique-local prefix from fd00::/8 is the natural choice when the goal is private reachability between sites. It stays stable regardless of which ISP serves each location, and it is not advertised to the global routing table, so it cannot leak your internal numbering onto the Internet.

The same property is the limitation. A ULA gives no public return path, because no upstream knows to route it to your gateway. If the requirement includes IPv6 Internet egress, you need an allocation from a provider that routes it to you, and that is a separate design decision with its own prefix size.

Do not assume every deployment needs a /48. RFC 6177 moved away from a single mandatory end-site size, and the right size follows the number of networks you will operate. Generate the ULA with the RFC 4193 procedure rather than inventing a prefix that another organization may also use. Write the result into the address plan before any device is configured, because a prefix nobody recorded is a prefix somebody will reuse.

Carve the Prefix Into Roles

The example below uses fd7c:2e91:4a00::/48, and the table is organised by where each block is written rather than by site. Treat the values as a sample and generate your own before deploying anything.

Where it is writtenWhat appears thereBlock usedWhat it establishes
Hub wg0.conf, interface addressA single host address on the tunnelfd7c:2e91:4a00:ff00::1/128The hub's own tunnel identity
Hub wg0.conf, Site A peerTwo ranges in one AllowedIPs line::2/128 and :10::/64That A owns its tunnel address and its LAN
Hub wg0.conf, Site B peerTwo ranges in one AllowedIPs line::3/128 and :20::/64That B owns its tunnel address and its LAN
Site A wg0.conf, interface addressA single host address on the tunnelfd7c:2e91:4a00:ff00::2/128A's identity as the hub sees it
Site B wg0.conf, interface addressA single host address on the tunnelfd7c:2e91:4a00:ff00::3/128B's identity as the hub sees it

The LAN blocks are /64 because ordinary SLAAC expects a 64-bit interface identifier. Splitting a LAN into a smaller block breaks that assumption on most hosts, and the operational cost is not worth the address savings in a private plan. Both LAN blocks come from the middle of the /48, leaving the low range free for later sites.

The tunnel addresses are an administrative bucket, not an on-link network. Each interface gets a single /128 from it, so no host assumes a shared subnet on the tunnel and no neighbour discovery runs where it is not needed. Keep the roles disjoint and record them in the organization's address plan so a later site does not reuse a block.

Give Each Router One Tunnel Address

Assign one /128 per router and keep the existing keys and endpoints of the working hub. The three values below differ only in the host part.

Hub:    Address = fd7c:2e91:4a00:ff00::1/128
Site A: Address = fd7c:2e91:4a00:ff00::2/128
Site B: Address = fd7c:2e91:4a00:ff00::3/128

The /128 describes the single address assigned to that interface. It says nothing about how many destinations the peer may route, because reachability is expressed in AllowedIPs rather than in the interface prefix.

Each site router also needs an address in its LAN block and a way to distribute it, usually router advertisements for SLAAC. WireGuard does not send those advertisements for you, and it does not bridge the two LANs. The site router is a router in both directions.

Put Subnet Ownership in AllowedIPs

The hub's peer entry for Site A owns A's tunnel address and A's LAN, and that pairing is the whole ownership statement. Nothing else in that peer section needs to describe the site.

AllowedIPs = fd7c:2e91:4a00:ff00::2/128, fd7c:2e91:4a00:10::/64

The peer entry for Site B owns the matching pair, and symmetry here matters more than brevity. Read the two lines together and the ownership split becomes visible at a glance.

AllowedIPs = fd7c:2e91:4a00:ff00::3/128, fd7c:2e91:4a00:20::/64

These are two separate peer sections, not two lists competing for one peer. WireGuard uses AllowedIPs in both directions: it chooses the peer for an outbound destination, and it rejects an inbound packet whose source does not match the range assigned to the peer that decrypted it. Giving the whole /48 to both peers would disable that second check and blur which site owns which block. That distinction is what makes a wrong source address visible instead of silently accepted.

Each site needs only the destinations reachable through its hub peer: Site A lists the hub address, Site B's tunnel address and B's LAN, and Site B lists the reciprocal set. The lists are asymmetric on purpose, because each router sees a different side of the topology. Write both from the address plan rather than from the other site's configuration, which is how a stale range survives a renumbering.

With wg-quick, these ranges also create routes. With systemd-networkd or a manual setup, add the equivalent routes yourself; otherwise the tunnel carries packets that the kernel never sends into it.

Complete Forwarding and Test Both Directions

Enable IPv6 forwarding on the hub and on both site routers, and allow the required inter-site services in the forward policy. Keep ICMPv6 Packet Too Big messages usable, because dropping them breaks path MTU discovery and produces large-transfer stalls that look like application faults. A hub that forwards without the firewall allowance drops the packet between routing and the tunnel. That failure mode is worth knowing, because the tunnel stays up and only some flows die.

sysctl net.ipv6.conf.all.forwarding
ip -6 route
ping -6 -c 3 fd7c:2e91:4a00:20::1

Test from a host in Site A to a host in Site B and then in the opposite direction. A ping to the hub's tunnel address exercises only the first hop and will pass even when the LAN routes are wrong. The host-to-host flow is the one that proves the plan.

The outer WireGuard connection can run over IPv4 while the inner traffic is IPv6, and that independence is useful during migration. If global IPv6 egress is added later, revisit the provider allocation, source address selection and return routing as a new design step; the private plan above remains valid for site-to-site traffic.

Amanqephe ahlobene