Networking problems with IPv6 + WireGuard

I have a OpenWrt setup with a working IPv6 support. I'm getting a /64 prefix from my ISP. It seems that the prefix and router detection work by router advertisement / neighborhood discovery protocol. (No DHCP6, apparently.) I'm using odhcpd in a "relay" mode for all settings: ra, ndp and dhcpv6.

Recently I installed Wireguard so that I could connect to my LAN from a remote network, and use the internet through my router. I generally connect from my Mac laptop. (Useful when travelling, so I get my home IP for some services.) This works fine in IPv4.

However, when trying to get IPv6 working, I'm not getting proper connectivity. I came up with two "local link addresses" to type in WireGuard config: fe80::9999::1 for the router, and fe80::9999::2 for my laptop. I am able to successfully ping the router using ping6 fe80::9999:1%utun1:

PING6(56=40+8+8 bytes) fe80::9999:2%utun1 --> fe80::9999:1%utun1
16 bytes from fe80::9999:1%utun1, icmp_seq=0 hlim=64 time=92.117 ms
16 bytes from fe80::9999:1%utun1, icmp_seq=1 hlim=64 time=56.860 ms

Note that here utun1 is the WireGuard interface on Mac. However, if I only ping with ping6 fe80::9999:1, it errors:

PING6(56=40+8+8 bytes) fe80::9999:2%utun1 --> fe80::9999:1
ping6: sendmsg: No route to host
ping6: wrote fe80::9999:1 16 chars, ret=-1

Am I correct to assume that this means that it doesn't know automatically to route fe80::9999:1 to the WireGuard interface?

Also, when I try to access IPv6 internet:

curl -6

Here's a TCPdump of the Wireguard interface; the first packet is an attempt to connect to, and the second is from the router, saying that "destination unreachable, unknown unreach code (5)":

13:44:39.423690 IP6
(flowlabel 0xd71dc, hlim 64, next-header TCP (6) payload length: 44)
fe80::9999:2.52712 > 2606:4700:3032::681c:125e.http:
Flags [SEW], cksum 0x8ef8 (correct), seq 122439060, win 65535,
options [mss 1360,nop,wscale 6,nop,nop,TS val 900769551 ecr 0,sackOK,eol],
length 0
	0x0000:  600d 71dc 002c 0640 fe80 0000 0000 0000  `.q..,.@........
	0x0010:  0000 0000 9999 0002 2606 4700 3032 0000  ........&.G.02..
	0x0020:  0000 0000 681c 125e cde8 0050 074c 4594  ....h..^...P.LE.
	0x0030:  0000 0000 b0c2 ffff 8ef8 0000 0204 0550  ...............P
	0x0040:  0103 0306 0101 080a 35b0 a70f 0000 0000  ........5.......
	0x0050:  0402 0000                                ....
13:44:39.488152 IP6
(flowlabel 0x7b359, hlim 64, next-header ICMPv6 (58) payload length: 92)
fe80::9999:1 > fe80::9999:2:
[icmp6 sum ok] ICMP6, destination unreachable, unknown unreach code (5)
	0x0000:  6007 b359 005c 3a40 fe80 0000 0000 0000  `..Y.\:@........
	0x0010:  0000 0000 9999 0001 fe80 0000 0000 0000  ................
	0x0020:  0000 0000 9999 0002 0105 f608 0000 0000  ................
	0x0030:  600d 71dc 002c 0640 fe80 0000 0000 0000  `.q..,.@........
	0x0040:  0000 0000 9999 0002 2606 4700 3032 0000  ........&.G.02..
	0x0050:  0000 0000 681c 125e cde8 0050 074c 4594  ....h..^...P.LE.
	0x0060:  0000 0000 b0c2 ffff 8ef8 0000 0204 0550  ...............P
	0x0070:  0103 0306 0101 080a 35b0 a70f 0000 0000  ........5.......
	0x0080:  0402 0000                                ....

According to the RFC, the code 5 means something like "invalid ingress/egress policy", but I'm not entirely sure what that means, I suspect that it's something like "you can't send that packet to the internet with a link local address, it will be filtered and/or your peer won't be able to send a reply with that."

So, it would seem that my laptop would need a global prefix, but it doesn't have one.

My router and the hosts directly connected to LAN are able to find a global IPv6 prefix and get a global address. They also advertise that address using Neighbour Advertisement ICMP6 messages, and respond to Neighbor Solicitation messages from the upstream router. With TCPdump, I have been able to confirm, that the Router Advertisement messages from the upstream router, containing the global prefix info, are able to reach my laptop behind WireGuard. I think this means that my odhcp "relay" settings for the WireGuard interface are successful:

config dhcp 'wg0'
	option interface 'wg0'
	option ignore '1'
	option ra 'relay'
	option dhcpv6 'relay'
	option ndp 'relay'

However, I'm not seeing any neighbour solicitation or advertisement messages from or to my laptop, I see some when connected directly to LAN though.

However, my Mac doesn't seem to pick up an address. But even if it did, I'm confused how to set the "allowed IPs" of the laptop peer on OpenWrt: if the laptop is going to "statelessly" assume any IPv6 address with the prefix, and the prefix might change dynamically according to the whims of my ISP, that means that I would need to allow any IPv6 address for my peer. That goes against most of the WireGuard setup guides I've seen, but then again, they have been mostly about IPv4 which is usually NATted, and thus using always private range.

Does this seem more of an macOS problem or is there something in my OpenWrt settings I can do? Does my setup seem sensible?

Here's a summarisation of my questions:

  1. Does ping6 not working without explicitly specifying interface that I have a local routing problem in macOS?
  2. Does the "ICMP6, destination unreachable, unknown unreach code (5)" from my router here mean what I suspect, that I need to send packets with a global IPv6 address as a source address, otherwise it never works?
  3. Should I configure the WireGuard peer settings to allow any IP from my laptop, to be able to send packets with global IPv6?
  4. Is there anything I could do to get my laptop to assume a global IPv6 address? When directly on the LAN, it works.
  5. Does the lack of neighbour solicitation / advertisement sound like a problem itself, or is it a consequence of my laptop not assuming a global address?

Oh, forgot to say!

I tried to manually set a global IPv6 address for my laptop, by combining the /64 prefix I've got and a random suffix: (Let's say 2404:7a80:9621:7100:1aa6:f7ff:fe8d:1111). Then, when doing curl -6, the packets seem to go through my router, to the upstream router! The upstream router returns a neighbour solicitation message to my router:

14:23:30.605242 IP6
(class 0xb8, hlim 255, next-header ICMPv6 (58) payload length: 32)
fe80::207:7dff:fe99:f7c5 > ff02::1:ff8d:1111:
[icmp6 sum ok] ICMP6, neighbor solicitation, length 32,
who has 2404:7a80:9621:7100:1aa6:f7ff:fe8d:1111
	  source link-address option (1), length 8 (1): 00:07:7d:99:f7:c5
	    0x0000:  0007 7d99 f7c5
	0x0000:  6b80 0000 0020 3aff fe80 0000 0000 0000  k.....:.........
	0x0010:  0207 7dff fe99 f7c5 ff02 0000 0000 0000  ..}.............
	0x0020:  0000 0001 ff8d 1111 8700 b5c7 0000 0000  ................
	0x0030:  2404 7a80 9621 7100 1aa6 f7ff fe8d 1111  $.z..!q.........
	0x0040:  0101 0007 7d99 f7c5

This is the same behaviour what happens when I connect to LAN and get an IPv6 address! However, monitoring the WireGuard interface on my router, the router does not forward that neighbour solicitation message there, so no wonder it doesn't get to my laptop.

So, it seems that my question #2 was answered: yes, I need to use a global source address. I also added my manually set global address to the allowed addresses of my peer, but I'm still confused what to do about #3 generally.

Here's some additional questions:

  1. Why doesn't the router forward the Neighbour Solicitation message to the WireGuard interface? I have the "relay" setting enabled in odhcp as I wrote earlier; the WireGuard interface is also part of my LAN zone, and the Neighbour Solicitation messages are relayed successfully in my LAN otherwise. Could it be that it doesn't realise 2404:7a80:9621:7100:1aa6:f7ff:fe8d:1111 is behind the WireGuard interface even thought the packet came from there?
  2. When I send packets with the manually set global address, would it be correct behaviour for my router to first send a neighbour solicitation back so it would know the link local address and the connected interface of that global address?

an ipv6 address has two parts, the network prefix, first 64 bits, and the host part, second 64 bits.

the network prefix defines the region where all devices can broadcast or multicast to each other. if a device is on your LAN, it can't send a broadcast and hit devices behind your wireguard... so instead you need to route... but to route means you need a different prefix...

your ISP should give you at a bare minimum a /60 prefix, so you have 16 subnets to define these vpns and VLANs and guest networks and things...

most isps should give out a /56 prefix. business customers should get a /48. A university or large company like Google should get a /32. A regional ISP should get a /32 or a few of them. larger isps get /28 or /24...

so, you see, when your ISP gives a /64 it is essentially saying it hasn't a clue what it's doing.

Indeed, my ISP is clueless about the prefix they are giving. If I don't relay ICMP6 Router/Neighbour Advertisement/Solicitation packets between my LAN and upstream router, my IPv6 connection doesn't work; the upstream router wants to directly know every host (/128) address under the prefix for it to route the packets for them downstream. However, that's the situation I have to deal with.

In my mind, if I relay the RA/NDP packets between the WireGuard interface and WAN similarily as I do it with my current LAN, it should work, similarly as it works with my current LAN. However, it doesn't work, and I'm trying to troubleshoot where the problem is, exactly.