Learning IPv6 can be a chicken and egg situation where you have to either start from configuring the router or configuring a specific host on the network.

Since the router configuration involves different concept, let’s assume you are on a host that already has IPv6.

These notes assume that you have some understanding of how IPv4 works.

IPv6 host addresses and routes

Similar to IPv4, let’s query the local network interfaces (ip a for short), assuming we know nothing about the setup:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1492 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:52:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 192.168.55.19/24 brd 192.168.55.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2607:123:123:2911::14/128 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe61:afd8/64 scope link 
       valid_lft forever preferred_lft forever

Here we can discover a few things:

  • ::1/128 is the IPv6 localhost equivalent to 127.0.0.1
  • /128 mask is the smallest mask. Similar to /32 in IPv4 (because IPv4 is 32 bits and IPv6 is 128 bits).
  • On the eth0 interface, there are two inet6 addresses: 2607:123:123:2911::14/12 is a publicly routable IP address from my ISP (note the scope global after the IP). The fe80::5054:ff:fe61:afd8/64 address is a “link-local” address, meaning it is only used to talk to other hosts on the same physical network. We will talk more about this later, link-local is sort of confusing, not really required, but really useful.

Now we can list the routes. Unlike ip a, here we must specify ip -6 route to show IPv6 routes:

1
2
3
4
5
$ ip -6 route
::1 dev lo proto kernel metric 256 pref medium
2607:123:123:2911::14 dev eth0 proto kernel metric 256 pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
default via fe80::da5e:d3ff:fee6:c985 dev eth0 metric 1024 pref medium

Let’s review line by line:

  • ::1 is our localhost route, which we can ignore.
  • 2607:123:123:2911::14 is the global IP on the local interface, again, nothing much of interest
  • fe80::/64 is for the link-local address we saw earlier, which we will ignore for now.
  • default via fe80::da5e:d3ff:fee6:c985 dev eth0 is the interesting bit. It is the default route - any traffic to an unknown address will be sent to this host - but also it is a link-local address, which might seem a bit weird when not familiar with IPv6. This is probably where 99% of people step back from their computer and say that IPv6 is just weird. We will explain this in detail in the next section.

A default route can use the normal global IPv6 address of the router, or it can use its link-local address. Both are equivalent. The only thing that matters is that the routing table must how to contact the router. So if the default route is 2607:123:123:fff::1, then the routing table would have:

1
2
2607:123:123:fff::1 dev eth0
default via 2607:123:123:2911::1 dev eth0

OVH hosting, for example, uses rather odd IPv6 routing rules like that.

Every host using IPv6 will automatically assign itself what is called a “link-local” address for each network interface (except localhost). The address is assumed to be unique because it is derived from the MAC address of the network interface (see: RFC4291 Appendix A). Generating addresses this way is also known as EUI-64 addresses, but we will avoid that rabbit hole.

Why bother? You can use IPv6 without ever using link-local addresses. As seen in the previous section, the default route can use a global “normal” IPv6 address (and to be honest, it’s probably the most recommended way, for static routing at least).

Have you ever connected hosts to a network that had a static IP configuration, only to discover that their static config uses a different network, so now you can’t easily talk to that host? Well ok, it’s a weird use-case, but it happened to me a lot when playing with wireless access points or with domestic routers.

For example, if I boot my router in failsafe mode, because its network configuration is broken, it will reset itself on a fixed IP address. Sometimes that’s 192.168.1.1 or maybe 192.168.0.1. The point is, that you have to sort of guess, try, and reconfigure your network a few times before getting it right, to then be able to ssh into the router or access the web interface.

With IPv6, we can query hosts on the network, and ssh directly using the link-local address:

1
2
3
4
5
# ping6 ff02::1%eth0
64 bytes from fe80::da5e:d3ff:fee6:c985%eth0: icmp_seq=1 ttl=64 time=0.029 ms
64 bytes from fe80::5054:ff:fe61:afd8%eth0: icmp_seq=1 ttl=64 time=0.189 ms
64 bytes from fe80::3205:5cff:fe5f:d662%eth0: icmp_seq=1 ttl=64 time=34.5 ms
[...]

All hosts connected to that network interface will respond.

The ff02::1 is a special address to query all hosts. Another is ff02::2 to query only routers. The %eth0 is a special syntax to specify on which network interface to send the request. After all, ff02::1 does not have an entry in the routing table, so the host would not know on which interface to send it.

We can then use that link-local address for ssh:

1
ssh root@fe80::5054:ff:fe61:afd8%eth0

If the ssh server is listening on all interfaces, then it will respond to the connection request.