Testing IPv6 addressing in a network simulator – Part 1

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.

IPv6-simple-LAN-000

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:

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.

Simple IPv6 network
Simple IPv6 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.

Delete IP addresses by clicking on the trash icon next to each field
Delete IP addresses by clicking on the trash icon next to each field

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.

Clear dynamic routing protocol services from the open-source router
Clear dynamic routing protocol services from the router r1

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 other ((Until additional global addresses or site-local addresses are also configured on the interfaces)). 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 address ((RFC 2373, section 2.5.8, describes link-local unicast addresses.)). 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 interface ((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.)).

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 ifconfig Observer Widget
Using the ifconfig Observer Widget

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 order ((One can also manually set the MAC address of each interface to another value by using the CORE Network Emulator’s Configure menu command)), 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
Router r1 eth0 00:00:00:aa:00:00 fe80::200:ff:feaa:0/64
eth1 00:00:00:aa:00:03 fe80::200:ff:feaa:3/64
Host h1 eth0 00:00:00:aa:00:01 fe80::200:ff:feaa:1/64
Host h2 eth0 00:00:00:aa:00:02 fe80::200:ff:feaa:2/64
Host h3 eth0 00:00:00:aa:00:04 fe80::200:ff:feaa:4/64
Host h4 eth0 00:00:00:aa:00:05 fe80::200:ff:feaa:5/64

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

But the -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 2001:0DB8::/32. ((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.))

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.

Marked up canvas showing subnets and prefix addresses
Marked up canvas showing subnets and prefix addresses

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
Router r1 eth0 00:00:00:aa:00:00 fe80::200:ff:feaa:0/64
2001:DB8:0:1::1/64
eth1 00:00:00:aa:00:03 fe80::200:ff:feaa:3/64
2001:DB8:0:2::1/64
Host h1 eth0 00:00:00:aa:00:01 fe80::200:ff:feaa:1/64
2001:DB8:0:1::100/64
Host h2 eth0 00:00:00:aa:00:02 fe80::200:ff:feaa:2/64
2001:DB8:0:1::101/64
Host h3 eth0 00:00:00:aa:00:04 fe80::200:ff:feaa:4/64
2001:DB8:0:2::100/64
Host h4 eth0 00:00:00:aa:00:05 fe80::200:ff:feaa:5/64
2001:DB8:0:2::101/64

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.

Start Wireshark

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.

Start Wireshark on both router interfaces, eth0 and eth1
Start Wireshark on both router interfaces, eth0 and 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.

Captured packets on Router r1 interface eth0
Captured packets on Router r1 interface eth0

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.

Captured packets on Router r1 interface eth1
Captured packets on Router r1 interface eth1

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

Conclusion

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.

Scroll to Top