6

I'm having trouble connecting a vm to the IPv6 internet through a virtual tap device on the host. I.e., I cannot ping ipv6.google.com or the public IPv6 host global primary interface address. Ex:

-bash-4.2$ ping6 ipv6.google.com
    PING ipv6.google.com(sea15s11-in-x0e.1e100.net) 56 data bytes
    From 2600:1f14:680:xxxx:66a3:79d5:6c1d:14c icmp_seq=1 Destination unreachable: Address unreachable
    From 2600:1f14:680:xxxx:66a3:79d5:6c1d:14c icmp_seq=2 Destination unreachable: Address unreachable
    From 2600:1f14:680:xxxx:66a3:79d5:6c1d:14c icmp_seq=3 Destination unreachable: Address unreachable
    ^C
    --- ipv6.google.com ping statistics ---
    4 packets transmitted, 0

 received, +3 errors, 100% packet loss, time 3082ms

or to the host's global ipv6 address, I get the same error.

Simple topology:

   router -----(eth0)----- host ----(tap device)---- vm

It appears there is some issue with neighbor discovery on the host, when I tcpdump the tap interface from the host's tap endpoint I receive the solicitation messages but nothing is returned:

[user ~]$ sudo tcpdump ip6 -vv -i tp-0gn-0000go-0    
tcpdump: listening on tp-0gn-0000go-0, link-type EN10MB (Ethernet), capture size 262144 bytes
    01:45:16.596378 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) 2600:1f14:680:xxxx:66a3:79d5:6c1d:14c > ff02::1:ff00:200e: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has sea15s11-in-x0e.1e100.net
          source link-address option (1), length 8 (1): 02:fc:80:d4:52:b6
            0x0000:  02fc 80d4 52b6
    01:45:17.610410 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) 2600:1f14:680:xxxx:66a3:79d5:6c1d:14c > ff02::1:ff00:200e: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has sea15s11-in-x0e.1e100.net
          source link-address option (1), length 8 (1): 02:fc:80:d4:52:b6
            0x0000:  02fc 80d4 52b6
    01:45:18.634402 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) 2600:1f14:680:xxxx:66a3:79d5:6c1d:14c > ff02::1:ff00:200e: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has sea15s11-in-x0e.1e100.net
          source link-address option (1), length 8 (1): 02:fc:80:d4:52:b6
            0x0000:  02fc 80d4 52b6

Note: I'm able to ping ipv6.google.com from the host:

[user ~]$ ping6 ipv6.google.com
PING ipv6.google.com(sea15s11-in-x0e.1e100.net (2607:f8b0:400a:808::200e)) 56 data bytes
64 bytes from sea15s11-in-x0e.1e100.net (2607:f8b0:400a:808::200e): icmp_seq=1 ttl=39 time=9.93 ms
64 bytes from sea15s11-in-x0e.1e100.net (2607:f8b0:400a:808::200e): icmp_seq=2 ttl=39 time=10.1 ms
64 bytes from sea15s11-in-x0e.1e100.net (2607:f8b0:400a:808::200e): icmp_seq=3 ttl=39 time=10.1 ms

It looks like there's an issue with the neighbor discovery. I'm not sure if I'm facing issues with DAD, NUD, or something else, or potentially not a neighbor discovery issue at all?

I currently only have the router in ip -6 neigh show, but my impression of the neighbor discovery cache was just to be a cache, and that the routes should still be intact and discoverable otherwise (though this is my very limited understanding). Maybe I'm missing some neighbor discovery/advertisement kernel parameters?

[user ~]$ ip -6 neigh show
fe80::460:a1ff:fec3:9cb6 dev eth0 lladdr 06:60:a1:c3:9c:b6 router STALE 

I have a hunch that I'm missing some net.ipv6 kernel parameters here, but I'm not really sure where to start with modifying them. Any suggestions are much appreciated. Full network setup information can be found below. Note that I manually configured the vm global address so it is very similar to the host, one is :XXXb/128 and one is :XXXc/128.

VM endpoint - interface:

-bash-4.2$ ip a s eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 02:fc:80:d4:52:b6 brd ff:ff:ff:ff:ff:ff
    inet 169.254.18.177/30 brd 169.254.18.179 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2600:1f14:680:xxxx:66a3:79d5:6c1d:14c/128 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::fc:80ff:fed4:52b6/64 scope link 
       valid_lft forever preferred_lft forever

and relevant VM routes:

-bash-4.2$ ip -6 r s
2600:1f14:680:6f00:66a3:79d5:6c1d:14c dev eth0  proto kernel  metric 256  pref medium
fe80::/64 dev eth1  proto kernel  metric 256  pref medium
fe80::/64 dev eth0  proto kernel  metric 256  pref medium
default dev eth0  metric 1024  pref medium

Host - the tap and primary interfaces look like:

[user ~]$ ip a s tp-0gn-0000go-0
2393: tp-0gn-0000go-0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether d2:d5:4e:f3:de:ab brd ff:ff:ff:ff:ff:ff
    inet 169.254.18.178/30 scope global tp-0gn-0000go-0
       valid_lft forever preferred_lft forever
    inet6 fe80::d0d5:4eff:fef3:deab/64 scope link 
       valid_lft forever preferred_lft forever
[user ~]$ ip a s eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 06:b6:f7:16:ac:04 brd ff:ff:ff:ff:ff:ff
    inet 172.30.255.4/28 brd 172.30.255.15 scope global dynamic eth0
       valid_lft 2994sec preferred_lft 2994sec
    inet6 2600:1f14:680:6f00:66a3:79d5:6c1d:14b/128 scope global dynamic 
       valid_lft 405sec preferred_lft 105sec
    inet6 fe80::4b6:f7ff:fe16:ac04/64 scope link 
       valid_lft forever preferred_lft forever

and the relevant routes:

[user ~]$ ip -6 r s
2600:1f14:680:6f00:66a3:79d5:6c1d:14b dev eth0 proto kernel metric 256 expires 389sec pref medium
2600:1f14:680:6f00:66a3:79d5:6c1d:14c dev tp-0gn-0000go-0 metric 1024 pref medium
2600:1f14:680:6f00::/64 dev eth0 proto kernel metric 256 pref medium
unreachable 3ffe:ffff::/32 dev lo metric 1024 error 4294967183 pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
default via fe80::460:a1ff:fec3:9cb6 dev eth0 proto ra metric 1024 expires 1798sec hoplimit 64 pref medium

ip6tables filter is allowing everything

[user ~]$ sudo ip6tables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination 

and host is amazon-linux, similar to centos/rhel/fedora, cat /etc/os-release:

NAME ="Amazon Linux"
VERSION="2"
ID_LIKE="centos rhel fedora"

Any suggestions are much appreciated. Let me know if I'm missing any necessary information or anything conceptually. Thanks in advance.


Update: Also I should note that I don't get any tcpdump packets when listening on eth0 of the host and trying to ping ipv6.google.com from the vm. As can be seen in the first tcpdump, the packets are first being sent to the all node solicitation multicast address which should be routed through eth0 (based off the local routing table), but I never see the packets go through eth0 via tcpdump. I currently have net.ipv6.conf.all.forwarding=1, net.ipv6.conf.all.accept_ra=2, and net.ipv6.conf.all.accept_ra_from_local=1.


Update #2: I came across this article. I added net.ipv6.conf.all.proxy_ndp=1 and added a proxy neighbor, ip -6 neigh add proxy <host eth0 global ip6 addr> dev <tap device> which allows me to ping the host's eth0 global address from the vm. Still no luck connecting to ipv6.google.com from the vm though I feel I'm getting closer.


Update #2.5: I think the previous update is irrelevant. I think the core of the issue is that the vm isn't aware of any router and so it's sending out neighbor solicitations for a global ipv6 address. Which I believe shouldn't be the case, but this is just my hunch. I've yet to come across a good resource that explicitly states when neighbor solicitations vs router solicitations vs echo requests should be sent.


Update #3: I axed the manual assigning of addresses and am trying to get the vm to communicate with the DHCP server (this is in an EC2 vpc btw) for it's address. I added a DHCPv6 relay in the host, however it seems the relay message are being sent to the DHCPv6 server and never coming back. I'd be happy to post more information/tcpdumps regarding this if others are interested.

0

2 Answers 2

5
+50

Here's what I think is wrong and how to address it.

VM side

The first issue is this one on the VM:

default dev eth0  metric 1024  pref medium

This makes believe the VM's routing stack the whole Internet is available directly on the link to eth0. So for any IPv6 destination it will send an NDP request, but nothing (including the host) will answer it.

To fix this, the VM just has to use the host as router, with its link-local address. It can simply be configured manually on the VM:

ip -6 route delete default dev eth0
ip -6 route add default via fe80::d0d5:4eff:fef3:deab dev eth0

For the VM side that's it.

Alternate settings on VM (and host's tp-0gn-0000go-0)

If you don't want to use the link-local address (eg: if it might change on reboot because of a random MAC address on host's tp-0gn-0000go-0), you can instead use the global IP address of the host by duplicating it on tp-0gn-0000go-0.

On the host, also add its IP address on tp-0gn-0000go-0 as /128 (or else with noprefixroute if that weren't only a single /128 already):

ip -6 address add 2600:1f14:680:6f00:66a3:79d5:6c1d:14b/128 dev tp-0gn-0000go-0

On the VM, instead of above:

ip -6 route delete default dev eth0
ip -6 route add 2600:1f14:680:6f00:66a3:79d5:6c1d:14b/128 dev eth0
ip -6 route add default via 2600:1f14:680:6f00:66a3:79d5:6c1d:14b dev eth0

Host side (on eth0)

The host's own router isn't aware of the VM because the VM has no presence on the host's eth0 side. When a packet from Internet arrives to the host's router, it will do an Neighbour Solicitation, but nothing will answer it, including the host (unless this router could be configured otherwise of course, which would simplify everything). The host has to be configured with ND Proxy to actually answer this solicitation so that in the end the it will send the packet to the host.

To do this, on the host, enable proxy_ndp on eth0, and add a specific NDP proxy entry (which has effect only if proxy_ndp is enabled on the interface):

sysctl -w net.ipv6.conf.eth0.proxy_ndp=1
ip -6 neighbour add proxy 2600:1f14:680:6f00:66a3:79d5:6c1d:14c dev eth0

(The neighbour proxy entry can be displayed with ip neighbour show proxy and deleted for example with ip neighbour flush proxy. It's not displayed by default with ip neighbour or deleted with ip neighbour flush all)

The host will now receive packets for 2600:1f14:680:6f00:66a3:79d5:6c1d:14c and will route them to the VM as defined in its routing table.


Both directions are now forwarded correctly: it should work.

I didn't attempt something like using a router advertisement daemon (radvd) to autoconfigure the VM's route, because as it's related to SLAAC which is supposed to be used in something not smaller than /64 I'm not sure it would have worked.


UPDATE: as mentioned by @uMdRupert, whenever forwarding is activated (net.ipv6.conf.all.forwarding=1) this setting is also needed on the host if configured by Router Advertisements:

sysctl -w net.ipv6.conf.eth0.accept_ra=2

The reason is that by default a Linux IPv6 router ignores Routing Advertisements (I guess the rationale is: because it was already configured as a router, there's no need to change this configuration). So simply activating IPv6 forwarding on an host which received its default route by Router Advertisements (as seen with proto ra [...] expires 1798sec in such route), might lose this route some time later (or at reboot once configuration was changed) and become unreachable. This must be overruled to still allow accepting such RA on eth0 by setting accept_ra to 2.

3
  • Was able to get this working using both the global address (from the host eth0) as you suggested and using a unique local address - but I don't have access to the router so latter doesn't really help me. One thing I should note for anyone that comes across this is that to get forwarding to work as intended from the tap to the router via eth0 I had to set net.ipv6.conf.eth0.accept_ra=2 in the host. Thanks again for all your help, extremely helpful and I learned a lot from your answers.
    – waffles
    Commented Jun 18, 2020 at 0:03
  • Also, for anyone that may come across this, I was able to get things working using a unique local address along with NAT (which I know is discouraged in IPv6 but this is a sub-optimal network setup to begin with). ip6tables -t nat -A POSTROUTING -j MASQUERADE
    – waffles
    Commented Jun 18, 2020 at 20:08
  • However, the above NAT solution only works for me if I have this rule in my filter table -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT (and I have no idea why).
    – waffles
    Commented Jun 25, 2020 at 19:35
2

You can create a bridge and connect your eth0 and tp-0gn-0000go-0 to it.

ip link add name br0 type bridge
ip link set dev br0 up
ip link set dev eth0 master br0
ip link set dev tp-0gn-0000go-0 master br0

(be careful, when eth0 joins the bridge it will lose the IP, if you are doing this remotely you have to make sure it will get one)

At this moment the bridge is setup. You can now assign the address to eth0 using whichever method you are using (if SLAAC, then you have nothing to do), if DCHPv4 then dhcp eth0,...

Start the VM and you should be all set.

3
  • That doesn't explain why forwarding doesn't work.
    – dirkt
    Commented Jun 14, 2020 at 5:49
  • No, because the question asked just how to do it. But that's ok, it might help somebody else in the future. Commented Jun 14, 2020 at 15:21
  • I agree that this should be the solution. However, I ran into some issues with bridging due to other virtual devices on the host (and losing connection like you suggested). Currently I'm looking into other network internal routing solutions, such as RIPng, and adding a DHCPv6 relay. But if those don't work I'll likely come back to bridging. Thanks.
    – waffles
    Commented Jun 14, 2020 at 18:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .