1

I have a problem with my providers internet router using IPv6 addresses and I realized that I do not really understand how to use IPv6 addresses. To get an idea I like to ask how to use the different IPv6 address types for example to route traffic from one host to another with only one router between them.

Using three nodes:
┏━━━━━━━━━┓   addr: 2001:db8:0:10::1/64    addr: 2001.db8:0:20::1/64   ┏━━━━━━━━━┓
┃ host-a  ┃                      ╲ ┏━━━━━━━━━━┓ ╱                      ┃ host-b  ┃
┃     eth0┣════════════════════════┫eth0  eth1┣════════════════════════┫eth0     ┃
┗━━━━━━━━━┛ ╲                      ┃  router  ┃                      ╱ ┗━━━━━━━━━┛
  addr: 2001:db8:0:10::2/64        ┗━━━━━━━━━━┛         addr: 2001:db8:0:20::2/64
subnet: 2001:db8:0:10::/64                            subnet: 2001:db8:0:20::/64

How do I have to setup it?

2

I haven't found so far a simple example like this to understand the basics with reference to the sources. It's more a prove of concept but it has taken me some effort, so I will share it with the community step by step.

Preparations

I use three virtual machines on KVM (Kernel-based Virtual Machine), all with Debian 11 (bullseye, at this time testing version) and using terminology from RFC 2460:
Node is an interface enabled for IPv6.
Router is any node that forwards IPv6 packets that are not expressly addressed to it.
Host is any node that’s not a router.

We need some helper programs that should be installed as long we have internet access on the nodes before reconfiguring its network interfaces for testing. On all nodes install radvdump and on the router install radvd. Be sure that radvd is disabled, otherwise it will confuse the testing. On Debian I do all of this with:

~$ sudo apt install radvdump       # on hosts and router
router ~$ sudo apt install radvd   # only on the router
router ~$ sudo systemctl disable --now radvd.service

I use the IPv6 Address Prefix 2001:DB8::/32 Reserved for Documentation (RFC 3849) that is usable for valid global unicast addresses but not routed to the internet.

To have things by hand, here are some specifications:

There are some address types used (RFC 4291)

Unspecified          ::/128
Loopback             ::1/128
default route        ::/0
Multicast            FF00::/8
Unique Local unicast FD00::/8
Global Unicast       (everything else)
Global Anycast       (same as Global Unicast)   not used in this example

Scope of Multicast addresses (RFC 4291):

FF00::  reserved
FF01::  Interface-Local scope
FF02::  Link-Local scope
FF03::  reserved
FF04::  Admin-Local scope
FF05::  Site-Local scope
FF06:: to FF07::  (unassigned)
FF08::  Organization-Local scope
FF09:: to FF0D::  (unassigned)
FF0E::  Global scope
FF0F::  reserved

Well-known IPv6 multicast addresses (extract - complete list at IANA):

ff02::1     all nodes
ff02::2     all routers
ff02::5     all OSPF (Open Shortest Path First) routers
ff02::6     all OSPF DRs (OSPF Designated Routers)
ff02::9     all RIP (Routing Information Protocol) routers
ff02::a     all EIGRP (Enhanced Interior Gateway Routing Protocol) routers
ff02::d     all PIM (Protocol Independent Multicast) routers
ff02::f     UPNP (Universal Plug and Play) devices
ff02::11    all homenet nodes
ff02::12    VRRP (Virtual Router Redundancy Protocol)
ff02::16    all MLDv2-capable routers
ff02::1a    all RPL (Routing Protocol for Low-Power and Lossy Networks) routers (used in Internet of Things (IoT) devices)
ff02::fb    multicast DNS IPv6
ff02::101   network time (NTP)
ff02::1:2   all DHCP agents
ff02::1:3   LLMNR (Link-Local Multicast Name Resolution)
ff02:0:0:0:0:1:ff00::/104   solicited node address
ff02:0:0:0:0:1-2:ff00::/104     node information query
ff05::1:3   all DHCP server (site)
ff05::101   all NTP server (site)

I will use tcpdump to look what's going on on the network, so install it on the router. Then enable systemd-networkd by following

Section Quick Step at Use systemd-networkd for general networking, then come back here.

I will have everything disabled so we can see what is needed and enable it step by step. On host-a and host-b use this network file:

host-? ~$ sudo -Es   # if not already done
host-? ~# cat > /etc/systemd/network/04-wired.network <<EOF
[Match]
Name=eth0

[Network]
# on host-a uncomment
#Address=2001:db8:0:10::2/64
# on host-b uncomment
#Address=2001:db8:0:20::2/64
IPv6AcceptRA=no
LinkLocalAddressing=no
EOF

On the router use these ones:

router ~$ sudo -Es   # if not already done
router ~# cat > /etc/systemd/network/04-eth0.network <<EOF
[Match]
Name=eth0

[Network]
Address=2001:db8:0:10::1/64
IPv6AcceptRA=no
LinkLocalAddressing=no
EOF

router ~# cat > /etc/systemd/network/06-eth1.network <<EOF
[Match]
Name=eth1

[Network]
Address=2001:db8:0:20::1/64
IPv6AcceptRA=no
LinkLocalAddressing=no
EOF

Simple link-local connection

First I will have a look at the direct connection between host-a and the router. The router is UP and I start host-a. tcpdump shows me on subnet 2001:db8:0:10/64:

host-a ~$ sudo tcpdump -n --number --interface=eth0 ip6 2>/dev/null
    1  23:25:28.211331 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
    2  23:25:28.227326 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 2 group record(s), length 48
    3  23:25:28.671386 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
    4  23:25:28.735354 IP6 :: > ff02::1:ff00:2: ICMP6, neighbor solicitation, who has 2001:db8:0:10::2, length 32
  • With package 1-3 host-a joins as listener to the multicast group ff02::16 - all MLDv2-capable routers. Router know now that it want to receive routing messages.
  • With package 4 it asked if there is another node with ip address 2001:db8:0:10::2. It isn't because there is no reply. host-a can use the address.

Ping to router works:

host-a ~$ ping6 -n 2001:db8:0:10::1
PING 2001:db8:0:10::1(2001:db8:0:10::1) 56 data bytes
64 bytes from 2001:db8:0:10::1: icmp_seq=1 ttl=64 time=0.829 ms
64 bytes from 2001:db8:0:10::1: icmp_seq=2 ttl=64 time=0.863 ms
64 bytes from 2001:db8:0:10::1: icmp_seq=3 ttl=64 time=0.858 ms
--- snip ---

Link-local unicast addresses

In the next step I want to connect to the second interface eth1 on router. For this we need a static route:

host-a ~$ sudo ip -6 route add 2001:db8:0:20::/64 via 2001:db8:0:10::1

But ping6 -n 2001:db8:0:20::1 does not work stable:

host-a ~$ ping6 -n 2001:db8:0:20::1
PING 2001:db8:0:20::1(2001:db8:0:20::1) 56 data bytes
64 bytes from 2001:db8:0:20::1: icmp_seq=1 ttl=64 time=0.881 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=2 ttl=64 time=0.784 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=3 ttl=64 time=0.898 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=4 ttl=64 time=0.799 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=5 ttl=64 time=0.805 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=6 ttl=64 time=1.13 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=7 ttl=64 time=0.795 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=8 ttl=64 time=0.790 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=9 ttl=64 time=0.793 ms
                                ^^^^^^^^^^
64 bytes from 2001:db8:0:20::1: icmp_seq=55 ttl=64 time=1025 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=56 ttl=64 time=3.26 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=57 ttl=64 time=0.793 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=58 ttl=64 time=0.792 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=59 ttl=64 time=0.789 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=60 ttl=64 time=0.776 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=61 ttl=64 time=0.803 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=62 ttl=64 time=0.801 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=63 ttl=64 time=0.774 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=64 ttl=64 time=0.802 ms
                                ^^^^^^^^^^^
64 bytes from 2001:db8:0:20::1: icmp_seq=110 ttl=64 time=2049 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=111 ttl=64 time=1025 ms
--- snip ---
^C
--- 2001:db8:0:20::1 ping statistics ---
170 packets transmitted, 35 received, 79.4118% packet loss, time 172432ms
rtt min/avg/max/mdev = 0.774/205.826/2048.843/536.218 ms, pipe 3

If you follow the seq number you will see with ping delay of 1 sec, that it is interrupted after about 10 sec and repeated after about 45 sec with a packet loss of about 80%. That cannot be called stable. I do not really understand it but it's good to know about this behavior when looking for troubleshooting. Anyway, it's out of specification because for connections on the link (2001:db8:0:10:2 to 2001:db8:0:10:1) there must be used link-local addresses as specified at RFC 4291 - Link-Local IPv6 Unicast Addresses:

Link-Local addresses are designed to be used for addressing on a single link for purposes such as automatic address configuration, neighbor discovery, or when no routers are present.

Check for a link-local address on the interface. There is no one:

host-a ~$ ip -6 -br addr
lo               UNKNOWN        ::1/128
eth0             UP             2001:db8:0:10::2/64

I enable it with setting LinkLocalAddressing=ipv6 in all /etc/systemd/network/*.network files on all nodes, reboot, check and ping:

host-a ~$ ip -6 -br addr
lo               UNKNOWN        ::1/128
eth0             UP             2001:db8:0:10::2/64 fe80::5054:ff:febc:adbe/64

router ~$ ip -6 -br addr
lo               UNKNOWN        ::1/128
eth0             UP             2001:db8:0:10::1/64 fe80::5054:ff:fe0f:194e/64
eth1             UP             2001:db8:0:20::1/64 fe80::5054:ff:fe0f:194e/64

host-b ~$ ip -6 -br addr
lo               UNKNOWN        ::1/128
eth0             UP             2001:db8:0:20::2/64 fe80::5054:ff:fe9b:34b9/64

host-a ~$ sudo ip -6 route add 2001:db8:0:20::/64 via 2001:db8:0:10::1
host-a ~$ ping6 -n 2001:db8:0:20::1
PING 2001:db8:0:20::1(2001:db8:0:20::1) 56 data bytes
64 bytes from 2001:db8:0:20::1: icmp_seq=9 ttl=64 time=2.08 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=10 ttl=64 time=0.780 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=11 ttl=64 time=0.783 ms
--- snip ---

Works.

Static Routing

If I try to connect from host-a to host-b then ping6 -n 2001:db8:0:20::2 fails. That is because the router does not forward packages between its interfaces. We have to enable it. Just append IPForward=ipv6 to a *.network file. We also need a static route on host-b so it knows where to send the replies to host-a. We will do it persistent now. So you will get following .network files:

host-a

host-a ~$ cat /etc/systemd/network/04-wired.network
[Match]
Name=eth0

[Network]
Address=2001:db8:0:10::2/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6

[Route]
Destination=2001:db8:0:20::/64
Gateway=2001:db8:0:10::1

router

router ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0

[Network]
Address=2001:0DB8:0:10::1/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6
IPForward=ipv6

router ~$ cat /etc/systemd/network/06-eth1.network
[Match]
Name=eth1

[Network]
Address=2001:0DB8:0:20::1/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6
IPForward=ipv6

host-b

host-b ~$ cat /etc/systemd/network/04-wired.network
[Match]
Name=eth0

[Network]
Address=2001:db8:0:20::2/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6

[Route]
Destination=2001:db8:0:10::/64
Gateway=2001:db8:0:20::1

Router Advertisement

Static routing is not very comfortable. This is usually done automatically. A host can ask a router for its configuration. To support it you have to enable IPv6AcceptRA=yes in its /etc/systemd/*.network file. If you do it you will find with tcpdump on boot up a host:

host-a ~$ sudo tcpdump -n --number --interface=eth0 ip6 2>/dev/null
 1  18:06:20.965014 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
 2  18:06:20.976965 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 2 group record(s), length 48
 4  18:06:21.701148 IP6 :: > ff02::1:ffbc:adbe: ICMP6, neighbor solicitation, who has fe80::5054:ff:febc:adbe, length 32
 5  18:06:21.861153 IP6 :: > ff02::1:ff00:2: ICMP6, neighbor solicitation, who has 2001:db8:0:10::2, length 32
 7  18:06:22.725190 IP6 fe80::5054:ff:febc:adbe > ff02::16: HBH ICMP6, multicast listener report v2, 3 group record(s), length 68
 9  18:06:24.225649 IP6 fe80::5054:ff:febc:adbe > ff02::2: ICMP6, router solicitation, length 16
10  18:06:27.851217 IP6 fe80::5054:ff:febc:adbe > ff02::2: ICMP6, router solicitation, length 16
11  18:06:35.087287 IP6 fe80::5054:ff:febc:adbe > ff02::2: ICMP6, router solicitation, length 16
--- snip ---

In addition to the already known packages multicast listener report v2 and neighbor solicitation we find router solicitation packages. With this message the host is asking to router for an interface configuration. But the router does not response so the host is continuing asking. On Linux an IPv6 router does not reply to router solicitations out of the box. It needs an additional service radvd to manage this. We have already installed it but disabled it. Using the simple example from Debian /usr/share/doc/radvd/examples/simple-radvd.conf we now configure and enable it:

router ~$ sudo -Es   # if not already done
router ~# cat > /etc/radvd.conf <<EOF
interface eth0
{
   AdvSendAdvert on;
   prefix 2001:db8:0:10::/64
   {
   };
};

interface eth1
{
   AdvSendAdvert on;
   prefix 2001:db8:0:20::/64
   {
   };
};
EOF

~# systemctl enable radvd.service

Because we expect that the hosts get their configuration from the router now, we have to remove the manual settings from its .network files. They should look as follows:

host-a and host-b:

host-? ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0

[Network]
IPv6AcceptRA=yes
LinkLocalAddressing=ipv6

router:

router ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0

[Network]
Address=2001:db8:0:10::1/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6
IPForward=ipv6

router ~$ cat /etc/systemd/network/06-eth1.network
[Match]
Name=eth1

[Network]
Address=2001:db8:0:20::1/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6
IPForward=ipv6

Reboot all nodes and you should have an autoconfigured running simple network.

Simplifications

To show what's needed I explicitly wrote the options into the network configuration files. But most of them are default settings, so we can just omit them.

host-a and host-b:

host-? ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0

router:

router ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0
[Network]
Address=2001:db8:0:10::1/64
IPForward=ipv6

router ~$ cat /etc/systemd/network/06-eth1.network
[Match]
Name=eth1
[Network]
Address=2001:db8:0:20::1/64
IPForward=ipv6

Debug

You may need to check if the host gets the correct settings from the router, or there is a router, for example from you internet provider, from that you must know what exactly settings it sends. Then you can start radvdump to view the received Router Advertisement. If you start it, just wait a little bit until a Router Advertisement is received. This is it with the default settings from router:

host-a ~$ sudo radvdump
#
# radvd configuration generated by radvdump 2.18
# based on Router Advertisement from fe80::5054:ff:fe0f:194e
# received by interface eth0
#

interface eth0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag off;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 1800;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvSourceLLAddress on;

        prefix 2001:db8:0:10::/64
        {
                AdvValidLifetime 86400;
                AdvPreferredLifetime 14400;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition

}; # End of interface definition

Name Resolution

Name resolution over the complete network is only possible with a DNS server. Its setup is a new question and out of scope here. Not having a DNS server available on this simple network is also the reason why we always use ping6 with option -n which suppresses querying a DNS server to translate the ip address. Without -n, ping6 tries to connect to a DNS server for each request with a timeout. This slows down pinging drastically and isn't usable.

For the local link it is no problem to enable multicast DNS (mDNS) so you have name resolution for the direct connected nodes. Just add option

MulticastDNS=yes

to the [Network] section on every nodes /etc/systemd/network/*.network file. Then you should be able to

host-a ~$ ping6 -n router.local
PING router.local(fe80::5054:ff:fe0f:194e%eth0) 56 data bytes
64 bytes from fe80::5054:ff:fe0f:194e%eth0: icmp_seq=1 ttl=64 time=2.37 ms
64 bytes from fe80::5054:ff:fe0f:194e%eth0: icmp_seq=2 ttl=64 time=0.799 ms
64 bytes from fe80::5054:ff:fe0f:194e%eth0: icmp_seq=3 ttl=64 time=0.823 ms
--- snip ---

But ping6 -n host-b.local will not work from host-a because mDNS is not routable.


References:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.