IPv6 addressing is about more than just a longer 128-bit address length. The working groups that defined IPv6 were trying to solve some of the problems that programmers, network administrators, and network engineers were encountering with IPv4. The way that IPv6 prefixes and addresses are assigned and configured differs significantly from IPv4. IPv6 offers some improvements and some new challenges.
A good way to learn about IPv6 network addressing is to work through some practical exercises. In this post, we will use the CORE Network Simulator to simulate a simple IPv6 network and examine link-local and global IPv6 addresses. We will work through a practical tutorial that will show how to manually configure IPv6 addresses and very basic IPv6 routing.
IPv6 addressing basics
We expect that the reader already understands the basic IPv6 address format. If you need a quick refresh on the topic, some excellent resources are:
- Section 2.3 of Peter Bieringer’s IPv6 HOWTO document hosted at the Linux Documentation Project.
- Section 5.3.2 of Lawrence Hughes’ open-source book, IPv6 The Second Internet, available from the IPv6 Forum.
- Wikipedia offers a good overview of IPv6 addressing and other IPv6 topics.
Set up the network configuration
Use the CORE Network Simulator to set up the network shown in the diagram below with one router, two switches, and four hosts. We will investigate IPv6 addressing fundamentals using this simple network.
To make the network diagram easier to read, use the View → Show menu command to hide all information except node names (to clean up the display). Also, you can click on Selection Tool and grab the text that represents each node name and move it to a spot where it is not hidden by the link. Then, use the Configure right-click menu command on each node to change the node name so that the network look like the following image:
Configure the simulated nodes
We want to study the same procedures we would use in a real network without allowing the CORE Network Emulator to set the network configurations for us, so we will clear the IP addresses that the CORE Network Emulator configures by default on every interface before starting the simulation.
Right-click on each router and host and select the Configure contextual menu command. Then, clear the IPv4 address and IPv6 address field on every node.
Also, since we will not use dynamic routing in this scenario, we will change the settings on the router r1 so that dynamic routing protocols are not started when the node starts up.
In the Configure dailogue box, after clearing the IP addresses on both of the router’s interfaces, click on the Services… button, then clear the OSPFv2 and OSPFv3 services. Also clear the radvd service (because we will explore stateless address autoconfiguration in a later post). Then press the Apply button.
Start the simulation
Start the network emulation by clicking in the start the session icon in the tool bar or by clicking on the menu command, Session → Start.
Examine the link-local unicast IPv6 addresses
After we start the network simulation we created, we expect to observe that the interfaces on each simulated router and on each simulated host have link-local IPv6 addresses automatically configured.
We will also run some simple network tests and observe the results. With the current configuration, nodes on the same link should be able to communicate with each other but nodes that are separated by the router should not be able to communicate with each other1. For example, host h1 should be able to ping host h2, but not host h4.
Link-local unicast IPv6 address, defined
When an IPv6 interface starts up, it is required to automatically configure itself with a link-local unicast IPv6 address2. Link-local IPv6 addresses consist of a specific 64-bit IPv6 prefix,
fe80::/64, and a unique 64-bit interface identifier derived from the MAC address of the interface3.
Link-Local unicast IPv6 addresses are created for purposes such as auto-address configuration and neighbor discovery on a single link. A link may be a point-to-point connection between two interfaces or a switched layer-2 domain such as an Ethernet network.
Link-local unicast addresses only work on the link on which they are configured because IPv6 routers are required to not forward any packets with link-local source or destination addresses to other links.
Using the ifconfig Observer Widget
We can use the Core Network Emulator’s Observer Widget tool to view the interface configuration on each node and take note of the IPv6 address on each interface. Click on the Observer Widget tool (the magnifying glass icon in the toolbar) and select the ifconfig widget. Then, hover the mouse pointer over each node to see the displayed interface configuration.
Using the ip command
Alternatively, we can open up a terminal window on each node running in the simulated network and use normal Linux commands to view the configuration.
Double-click on any node to open a terminal window (for example, host h1). Then, execute the command:
root@h1:~# ip addr show 1: lo:
mtu 65536 qdisc noqueue state UNKNOWN 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 8: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:00:00:aa:00:01 brd ff:ff:ff:ff:ff:ff inet6 fe80::200:ff:feaa:1/64 scope link valid_lft forever preferred_lft forever
Record all IPv6 addresses
Write down the IP addresses and MAC addresses on each node in a table for future reference. This will be useful when we are running programs like ping where we need to know the IPv6 address of the destination node. Knowing the MAC addresses is useful when we are analyzing packets in the Wireshark protocol analyzer.
In our example, the CORE Network Emulator assigns MAC addresses, in numerical order4, starting with 00:00:00:aa:00:00 and incrementing by one for every other interface attached to a link.
After inspecting each node using either the Observer Widget or the Linux ip command, we generate the following table:
|Node name||Interface||MAC address||IPv6 addresses|
Network tests with link-local addresses
Before we configure the network, let’s see how the IPv6 network works in it’s initial state.
From host h1, ping host h2, the eth0 interface on router r1, and host h4.
Host h1 interface eth0 to Host h2 interface eth0
We see that Host h1 can send and receive IPv6 data packets to and from Host h2 using the ping command with the link-local IPv6 address. The interfaces of both hosts are on the same link.
root@h1:~# ping6 -c 1 -I eth0 fe80::200:ff:feaa:2 PING fe80::200:ff:feaa:2(fe80::200:ff:feaa:2) from fe80::200:ff:feaa:1 eth0: 56 data bytes 64 bytes from fe80::200:ff:feaa:2: icmp_seq=1 ttl=64 time=0.105 ms --- fe80::200:ff:feaa:2 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.105/0.105/0.105/0.000 ms
Note that we use the
-I eth0 option to indicate that the destination address is reachable through interface eth0. Link-local IPv6 addresses are not routable so the source system does not know which interface on which to send the ping packet. We need to specify the source interface when using link-local IPv6 addresses.
On a Linux system, the same command can be written with a “zone” suffix as:
$ ping6 fe80::200:ff:feaa:2%eth0
-I eth0 option is supported the same way on most operating systems, while the “zone” suffix is not.
Host h1 interface eth0 to Router r1 interface eth0
We see that Host h1 can send and receive IPv6 data packets to and from the eth0 interface on Router r1 using the ping command with the link-local IPv6 address. These interfaces are on the same link.
root@h1:~# ping6 -c 1 -I eth0 fe80::200:ff:feaa:0 PING fe80::200:ff:feaa:0(fe80::200:ff:feaa:0) from fe80::200:ff:feaa:1 eth0: 56 data bytes 64 bytes from fe80::200:ff:feaa:0: icmp_seq=1 ttl=64 time=0.335 ms --- fe80::200:ff:feaa:0 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.335/0.335/0.335/0.000 ms
Host h1 interface eth0 to Host h4 interface eth0
We see that Host h1 cannot send and receive IPv6 data packets to and from Host h4. These interfaces are on different links.
To reach Host h4, a data packet from Host h1 must first arrive at the router’s interface eth0 and then be forwarded on to the router’s interface eth1 toward Host h4. By definition, the router is not allowed to do this because the source IPv6 address of the ICMP (ping6) packet is a link-local address.
root@h1:~# ping6 -c 1 -I eth0 fe80::200:ff:feaa:5 PING fe80::200:ff:feaa:5(fe80::200:ff:feaa:5) from fe80::200:ff:feaa:1 eth0: 56 data bytes From fe80::200:ff:feaa:1 icmp_seq=1 Destination unreachable: Address unreachable --- fe80::200:ff:feaa:5 ping statistics --- 1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms root@h1:~#
Experiment with globally reachable unicast IPv6 addresses
With no additional configuration, devices on the same subnet can reach each other using IPv6 but in order for nodes on one subnet to communicate with nodes on another subnet and with nodes in other networks, a unique and reachable unicast IPv6 prefix must be assigned to each subnet.
The IPv6 protocol expects that more than one IPv6 address may be added to each interface. In this case, we already have a link-local address on each interface in the simulation and we will now add a globally unique reachable address to each interface.
The documentation IPv6 prefix
The authorities that assign IPv6 addresses have thoughtfully reserved a special prefix for use in documentation and in examples like this one, so if we ever connect this simulation to a real IPv6 networks it will not cause any problems. The prefix allocated for documentation purposes is
So, in our example, we will assign the following prefixes to each subnet:
|Subnet||Router Interface||Subnet prefix|
|Subnet 1||Router r1 eth0||2001:DB8:0:1::/64|
|Subnet 2||Router r1 eth1||2001:DB8:0:2::/64|
We use the Background Annotation Tools (from the tool bar) to mark up the Core Network Emulator canvas so we have a visual reminder of the subnets and addresses we will use.
Manually configure global IPv6 addresses
Now, let’s assign addresses to each interface. We do this manually because we imagine we’re a network administrator who wants to configure addresses that are easy to remember and who wants the default router interfaces on a subnet to end in “1”, as some network admins would, in an IPv4 network. So, we do not use stateless autoconfiguration of IPv6 addresses in this example (we’ll discuss it in a later post).
On each host and router, enter the commands as follows to manually configure a global IPv6 address on each interface. Each host also needs a default route configured:
root@r1:~# ip -6 addr add 2001:DB8:0:1::1/64 dev eth0 root@r1:~# ip -6 addr add 2001:DB8:0:2::1/64 dev eth1 root@h1:~# ip -6 addr add 2001:DB8:0:1::100/64 dev eth0 root@h1:~# ip -6 route add ::/0 via 2001:db8:0:1::1 dev eth0 root@h2:~# ip -6 addr add 2001:DB8:0:1::101/64 dev eth0 root@h2:~# ip -6 route add ::/0 via 2001:db8:0:1::1 dev eth0 root@h3:~# ip -6 addr add 2001:DB8:0:2::100/64 dev eth0 root@h3:~# ip -6 route add ::/0 via 2001:db8:0:2::1 dev eth0 root@h4:~# ip -6 addr add 2001:DB8:0:2::101/64 dev eth0 root@h4:~# ip -6 route add ::/0 via 2001:db8:0:2::1 dev eth0
After manually configuring the IPv6 interface addresses, we can inspect each interface using the
ip addr show command (or Core Network Emulator’s Observer Widget tool) and see that the IPv6 addresses are configured on each interface and we see that the prefixes 2001:DB8:0:1::/64 and 2001:DB8:0:2::/64 have a global scope (as opposed to a link scope). For example, on router r1:
root@r1:~# ip -6 addr show 1: lo:
mtu 65536 inet6 ::1/128 scope host valid_lft forever preferred_lft forever 6: eth0: mtu 1500 qlen 1000 inet6 2001:db8:0:1::1/64 scope global valid_lft forever preferred_lft forever inet6 fe80::200:ff:feaa:0/64 scope link valid_lft forever preferred_lft forever 12: eth1: mtu 1500 qlen 1000 inet6 2001:db8:0:2::1/64 scope global valid_lft forever preferred_lft forever inet6 fe80::200:ff:feaa:3/64 scope link valid_lft forever preferred_lft forever root@r1:~#
Network tests with global addresses
We verify that all addresses are configured according to our address plan. We see each interface now has a link local IPv6 address and a global IPv6 address.
|Node name||Interface||MAC address||IPv6 addresses|
Host h1 to Host h3
Because packets with global address prefixes in the source and destination address fields can be forwarded by a router, we expect that a node in Subnet 1 should be able to communicate with a Node in Subnet 2 (because both subnets are directly connected to the router). We test that using the ping6 command to test if Host h1 can reach host h3:
root@h1:~# ping6 -c 1 2001:DB8:0:2::100 PING 2001:DB8:0:2::100(2001:db8:0:2::100) 56 data bytes 64 bytes from 2001:db8:0:2::100: icmp_seq=1 ttl=63 time=0.280 ms --- 2001:DB8:0:2::100 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.280/0.280/0.280/0.000 ms root@h1:~#
Note that we no longer need to specify the source interface when using global addresses because there is no ambiguity about to which subnet a global prefix is associated.
Inspect IPv6 packets
We will use the Wireshark packet analyzer to capture and view IPv6 packets on the interfaces of router r1. This will give us some insight into how the IPv6 protocol resolves addresses in a local subnet and between two subnets.
First we start Wireshark using the contextual menu in the CORE Network Emulator. Right-click on router r1 and select Wireshark and then eth0. Repeat the process and also select eth1.
Now we should see two Wireshark windows open, each one displaying data on a different interface.
SSH session between two nodes on same link
Now, start a connection within subnet 1, between host h1 and the eth0 interface on router r1. For this example, we will start an SSH session between h1 and r1.
First, we need to enable the SSH server deaemon on the router r1 with the command:
root@r1:~# /etc/init.d/ssh start
Then, on host h1, start the SSH session to r1. Since each node is linux container, use your own userid (in my case it is brianl) and your user password to access the remote node because Xubuntu will not allow you access to the root user on the Linux container.
root@h1:~# ssh brianl@2001:DB8:0:1::1 brianl@2001:db8:0:1::1's password: Welcome to Ubuntu 13.10 (GNU/Linux 3.11.0-19-generic x86_64) * Documentation: https://help.ubuntu.com/ 0 packages can be updated. 0 updates are security updates. brianl@r1:~$ ls Desktop Downloads Music Public Videos Documents Dropbox Pictures Templates brianl@r1:~$ exit logout Connection to 2001:DB8:0:1::1 closed. root@h1:~#
Looking at the Wireshark window on r1 interface eth0, we see some new destination addresses (these are multicast addresses used by the Neighbor Discovery Protocol) and we see the two systems communicate to match the MAC address of router r1‘s interface eth0 with the destination IPv6 address.
We’ll cover more about the Neighbor Discovery Protocol, which in this case operates like the IPv4 ARP protocol, in a future post.
SSH session between two nodes on different links
Next, we’ll open an SSH session that passes between the two subnets, from host h1 to host h4.
We need to enable the SSH server on host h4, first:
root@h4:~# /etc/init.d/ssh start
Then, we start the SSH session from host h1 to host h4:
root@h1:~# ssh brianl@2001:DB8:0:2::101 brianl@2001:db8:0:2::101's password:
Looking at the Wireshark window that is capturing traffic on r1 interface eth1, we see that the Neighbor Discovery Protocol uses both multicast addresses and the link local addresses as part of the process to resolve the destination MAC address with the destination IPv6 address.
Again, we’ll discuss the neighbor discovery process more in another post.
Finish and clean up
We end the simulation and save the configuration. Click on the red Stop the session button on the tool bar or use the menu command:
Session → Stop
Then save the configuration using the menu command:
File → Save
We set up a small IPv6 network on the CORE Network Emulator. We observed how an IPv6 network automatically configures link local addresses on its interfaces but that traffic sourced from or destined to a link local cannot pass through a router. We practiced manually configuring global addresses on all the nodes and we practiced setting up IPv6 default routes on the hosts. We observed that global IPv6 addresses are reachable through a router.
While we were doing this, we also practiced using the CORE Network Emulator and used some features we have not tried before, such as the Background Annotation Tool and the Services configuration panel.
We saved the network configuration for future use.
Until additional global addresses or site-local addresses are also configured on the interfaces ↩
The formula for converting a 48-bit Ethernet MAC address into a 64-bit IPv6 interface identifier is described in RFC 2373, appendix A. Basically, it adds two bytes in the middle of the Ethernet MAC address so it fills in in the 64-bit interface identifier field, then flips the seventh bit of the 64-bit address. This creates an address that looks like an EUI-64 address format and the flipped seventh bit indicates the device indicator is not a standard hardware address but has been modified by the system. ↩
One can also manually set the MAC address of each interface to another value by using the CORE Network Emulator’s Configure menu command ↩
Defined in RFC 3849 — IPv6 Address Prefix Reserved for Documentation. Network admins are expected to filter this prefix so it is not advertised to other networks. ↩