Hi, while debugging DHCPv6 issues on my router, I noticed that IPv6 messages generated on the router have the wrong source address.
Disclaimer, I replaced the actual IPv6 prefixes with random values. Call me stupid or overly cautious. Fake addresses start with 1234:4567
.
The problem
Here is the output of ip -6 addr
:
[snip]
3: br-lan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 1234:4567:1111::1/60 scope global dynamic
valid_lft 86222sec preferred_lft 3422sec
inet6 fdec:69e6:7d50::1/60 scope global
valid_lft forever preferred_lft forever
inet6 fe80::f29f:c2ff:fe60:91e0/64 scope link
valid_lft forever preferred_lft forever
5: eth0.2@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 1234:4567:89ab:cdef:f29f:c2ff:fe60:91e1/64 scope global dynamic
valid_lft 2591818sec preferred_lft 604618sec
inet6 1234:4567:89ab:cdef:1559:813d:406f:6f4d/128 scope global dynamic
valid_lft 86222sec preferred_lft 3422sec
inet6 fe80::f29f:c2ff:fe60:91e1/64 scope link
valid_lft forever preferred_lft forever
My ISP assigns 1234:4567:89ab:cdef/64
to the public router interface and passes 1234:4567:1111/48
down via DHCPv6-PD.
Now i capture the a ICMPv6 ping to ipv6.google.com issued on the router itself using wireshark on the router. The roundtrip looks like this:
43 173.811099 1234:4567:1111::1 2a00:1450:400a:802::200e ICMPv6 118 Echo (ping) request id=0x1a40, seq=1, hop limit=64 (reply in 44)
44 173.812327 2a00:1450:400a:802::200e 1234:4567:1111::1 ICMPv6 118 Echo (ping) reply id=0x1a40, seq=1, hop limit=55 (request in 43)
Notice how the source address is 1234:4567:1111::1
. My ISP assigns 1234:4567:1111::/48
to me using DHCPv6-PD. But notice how the address is only assigned to br-lan
and not to eth0.2
. Clearly the ping paket left the router on eth0.2
(in fact that was the only interface i pointed wireshark at). So i'd expect it to pick one of the addresses assigned to eth0.2
, some address in 1234:4567:89ab:cdef/64
.
The problem is not unique to ping pakets. DHCPv6 pakets to my ISP also are sent from an address from the DHCPv6-PD prefix and not from an address of the external interface. (My ISP says this might be the source of my DHCPv6 problems, but that is a different story).
Investigation
After reading up on IPv6 source address selection, a surprisingly interesting and complicated topic, I confirmed that Linux should pick an address from the outgoing interface. So let's check the routing table for IPv6:
default from 1234:4567:89ab:cdef:1559:813d:406f:6f4d via fe80::ca9c:1dff:fe93:343f dev eth0.2 proto static metric 512 pref medium
default from 1234:4567:89ab:cdef::/64 via fe80::ca9c:1dff:fe93:343f dev eth0.2 proto static metric 512 pref medium
default from 1234:4567:1111::/48 via fe80::ca9c:1dff:fe93:343f dev eth0.2 proto static metric 512 pref medium
1234:4567:89ab:cdef::/64 dev eth0.2 proto static metric 256 pref medium
1234:4567:1111::/64 dev br-lan proto static metric 1024 pref medium
[snip]
Woah...the default routes are gated using source rules.
Conclusion: in order to know my outgoing interface I have to know my source address, but to know the source address I have to know the outgoing interface.
Trying to fix it
Now, I tried to replace the 3 default route entries with just a single one:
default via fe80::ca9c:1dff:fe93:343f dev eth0.2 proto static metric 512 pref medium
When I retried the ping -6 ipv6.google.com
Wireshark would show:
259 1507.821756 1234:4567:89ab:cdef:f29f:c2ff:fe60:91e1 2a00:1450:400a:802::200e ICMPv6 118 Echo (ping) request id=0xc943, seq=0, hop limit=64 (reply in 260)
260 1507.822988 2a00:1450:400a:802::200e 1234:4567:89ab:cdef:f29f:c2ff:fe60:91e1 ICMPv6 118 Echo (ping) reply id=0xc943, seq=0, hop limit=55 (request in 259)
Notice how the source address is now actually from a prefix assigned to eth0.2
. Great!
Actual questions
- Can the observed behaviour be considered a bug?
- Why is OpenWrt using complicated source routes instead of a single default route?