Experimenting with Mininet and IPv6 routes
When students discover IPv6, they usually start playing with static routes to understand how routing tables are built. At UCL, we’ve used a variety of techniques to let the students understand routing tables. A first approach is to simply use the blackboard and let the students analyse routing tables and explain how packets will be forwarded in a given network. This works well, but students often ask for additional exercises to practice before the exam. Another approach is to use netkit. netkit was designed by researchers at Roma3 University as an experimental learning tool. It relies on User Mode Linux to run a Linux kernels as processes on a virtual machine. Several student labs were provided by the netkit authors. We have used it in the past, but the project does not seem to make progress anymore. A third approach is to use Mininet. Mininet is an emulation framework developed at Stanford University that leverages the namespaces features of recent Linux kernel. With those features, a single Linux kernel can support a variety of routers and hosts interconnected by virtual links. Mininet has been used by various universities as an educational tool, but unfortunately it was designed with IPv4 in mind while Computer Networking : Principles, Protocols and Practice has been focussed on IPv6.
Olivier Tilmans has recently developed one missing piece to enable students to use Mininet to experiment with IPv6. His IPMininet python module provides the classes that are required to automatically configure IPv6 networks with various forms of routing. It is available from PyPi from https://pypi.python.org/ipmininet.
The syntax of IPMininet is relatively simple and can be learned by looking at a few examples.
Let us start our exploration of IPv6 routing with simple network topology that contains three hosts and three routers and uses static routes.
a ---- r1 ---- r2 ---- r3 ---- c
+
b
Although IPMininet can assign prefixes and addresses automatically, we use manually assigned addresses in this example.
We use five /64
IPv6 prefixes in this network topology:
2001:89ab:12::/64
on the link betweenr1
andr2
2001:89ab:23::/64
on the link betweenr2
andr3
2001:7ab:1::/64
on the link between hosta
andr1
2001:7ab:2::/64
on the link between hostb
andr2
2001:7ab:3::/64
on the link between hostc
andr3
Given the network topology, we install default routes on r1
and
r3
via r2
. Router r1
has static routes to reach 2001:7ab:1::/64
via r1
and 2001:7ab:3::/64
via r3
.
With these IP prefixes and the network topology, we can now use IPMininet to create the topology and assign the addresses.
We start by creating the objects that correspond to the static routes on the three routers.
r1_routes = [StaticRoute("::/0", "2001:89ab:12::2")]
r3_routes = [StaticRoute("::/0", "2001:89ab:23::1")]
r2_routes = [StaticRoute("2001:7ab:1::/64", "2001:89ab:12::1"),
StaticRoute("2001:7ab:3::/64", "2001:89ab:23::2")]
We can now create the three routers.
r1 = self.addRouter_v6('r1', r1_routes)
r2 = self.addRouter_v6('r2', r2_routes)
r3 = self.addRouter_v6('r3', r3_routes)
And the links between the pairs of routers. In these lines, params1
(resp. params2
) corresponds to the configuration of the interface on the first (resp. second) node.
self.addLink(r1, r2, params1={"ip": "2001:89ab:12::1/64"},
params2={"ip": "2001:89ab:12::2/64"})
self.addLink(r2, r3, params1={"ip": "2001:89ab:23::1/64"},
params2={"ip": "2001:89ab:23::2/64"})
The configuration of the links between the routers and the hosts follows a similar pattern.
self.addLink(r1, self.addHost('a'),
params1={"ip": "2001:7ab:1::1/64"},
params2={"ip": "2001:7ab:1::a/64"})
self.addLink(r2, self.addHost('b'),
params1={"ip": "2001:7ab:2::1/64"},
params2={"ip": "2001:7ab:2::b/64"})
self.addLink(r3, self.addHost('c'),
params1={"ip": "2001:7ab:3::1/64"},
params2={"ip": "2001:7ab:3::c/64"})
The entire script is available from https://github.com/obonaventure/RoutingExamples/blob/master/static/simple.py
To help students to start using IPMininet, Mathieu Jadin has created a Vagrant box that launches a Ubuntu virtual machine will all the required software. You can access the VagrantFile and the associated puppet manifests from https://github.com/obonaventure/RoutingExamples
Here is a simple example of the utilisation of this Vagrant box.
We start the network topology shown above with the sudo python ipmininet_example.py
command. It launches the Mininet interactive shell that provides several useful commands:
mininet> help
Documented commands (type help <topic>):
========================================
EOF gterm iperf links pingall ports route time
dpctl help iperfudp net pingallfull px sh x
dump intfs ips nodes pingpair py source xterm
exit ip link noecho pingpairfull quit switch
You may also send a command to a node using:
<node> command {args}
For example:
mininet> h1 ifconfig
The interpreter automatically substitutes IP addresses
for node names when a node is the first arg, so commands
like
mininet> h2 ping h3
should work.
Some character-oriented interactive commands require
noecho:
mininet> noecho h2 vi foo.py
However, starting up an xterm/gterm is generally better:
mininet> xterm h2
mininet>
Some of the standard mininet commands assume the utilisation of IPv4 and do not have a direct IPv6 equivalent. Here are some useful commands.
The nodes
command lists the routers and hosts that have been created in the mininet topology.
mininet> nodes
available nodes are:
a b c r1 r2 r3
The links
command lists the links that have been instantiated and
shows that mapping between the named interfaces on each node.
mininet> links
r1-eth1<->a-eth0 (OK OK)
r1-eth0<->r2-eth0 (OK OK)
r2-eth2<->b-eth0 (OK OK)
r2-eth1<->r3-eth0 (OK OK)
r3-eth1<->c-eth0 (OK OK)
It is possible to execute any of the standard Linux commands to configure the TCP/IP stack on any of the hosts by prefixing the command with the corresponding host. Remember to always specify
inet6
as the address family to retrieve the IPv6 information.
mininet> a ip -f inet6 link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: a-eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether d6:a0:57:60:09:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
mininet> a ip -f inet6 address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: a-eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 2001:7ab:1::a/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::d4a0:57ff:fe60:90a/64 scope link
valid_lft forever preferred_lft forever
mininet> a ip -f inet6 route
2001:7ab:1::/64 dev a-eth0 proto kernel metric 256 pref medium
fe80::/64 dev a-eth0 proto kernel metric 256 pref medium
default via 2001:7ab:1::1 dev a-eth0 metric 1024 pref medium
mininet> a ip -f inet6 route
2001:7ab:1::/64 dev a-eth0 proto kernel metric 256 pref medium
fe80::/64 dev a-eth0 proto kernel metric 256 pref medium
default via 2001:7ab:1::1 dev a-eth0 metric 1024 pref medium
mininet> r2 ip -f inet6 route
2001:7ab:1::/64 via 2001:89ab:12::1 dev r2-eth0 proto zebra metric 20 pref medium
2001:7ab:2::/64 dev r2-eth2 proto kernel metric 256 pref medium
2001:7ab:3::/64 via 2001:89ab:23::2 dev r2-eth1 proto zebra metric 20 pref medium
2001:89ab:12::/64 dev r2-eth0 proto kernel metric 256 pref medium
2001:89ab:23::/64 dev r2-eth1 proto kernel metric 256 pref medium
fe80::/64 dev r2-eth0 proto kernel metric 256 pref medium
fe80::/64 dev r2-eth2 proto kernel metric 256 pref medium
fe80::/64 dev r2-eth1 proto kernel metric 256 pref medium
Another useful command is xterm node
that allows to launch a terminal on the specified node
. This gives you a interactive shell
on any node. You can use it to capture packets with tcpdump
. As an example, let us use traceroute6
to trace the path followed by packets from host a
towards the IPv6 address of host v
, i.e. ` 2001:7ab:3::c. The output of this command shows that the path passes through routers
r1,
r2 and
r3`.
mininet> a traceroute6 -q 1 2001:7ab:3::c
traceroute to 2001:7ab:3::c (2001:7ab:3::c) from 2001:7ab:1::a, 30 hops max, 16 byte packets
1 2001:7ab:1::1 (2001:7ab:1::1) 0.105 ms
2 2001:89ab:12::2 (2001:89ab:12::2) 1.131 ms
3 2001:89ab:23::2 (2001:89ab:23::2) 0.845 ms
4 2001:7ab:3::c (2001:7ab:3::c) 0.254 ms
When debugging a network, it can be interesting to capture packets on specific links to check that they follow the expected path. In this simple network, the best place to capture packets is on router r2
. This router has two interfaces: r2-eth0
connected to r1
and r2-eth1
connected to r3
. If you use tcpdump
without any
filter, you will capture the packets generated by xterm
. To capture packets, you need to specify precise filters that will match the packets of interest. For traceroute6
, you need to match the IPv6 packets that contain UDP segments and some ICMPv6 packets. The script below provides a simple filter that you can reuse. It takes one argument: the name of the interface on which tcpdump
needs to run.
#!/bin/bash
tcpdump -v -i $1 -n '(ip6 && udp) || (icmp6 && (ip6[40] == 1 || ip6[40]==3))'
Here are the packets captured by tcpdump
on the two interfaces of router r2
.
#sh dump-traceroute.sh r2-eth0
tcpdump: listening on r2-eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:26:20.738814 IP6 (flowlabel 0xee1ca, hlim 1, next-header UDP (17) payload length: 24) 2001:7ab:1::a.60819 > 2001:7ab:3::c.33434: [bad udp cksum 0x4f9b -> 0xf554!] UDP, length 16
13:26:20.738835 IP6 (flowlabel 0x467c2, hlim 64, next-header ICMPv6 (58) payload length: 72) 2001:89ab:12::2 > 2001:7ab:1::a: [icmp6 sum ok] ICMP6, time exceeded in-transit for 2001:7ab:3::c
13:26:20.739280 IP6 (flowlabel 0xee1ca, hlim 2, next-header UDP (17) payload length: 24) 2001:7ab:1::a.60819 > 2001:7ab:3::c.33434: [bad udp cksum 0x4f9b -> 0x2152!] UDP, length 16
13:26:20.739310 IP6 (flowlabel 0x6dc40, hlim 63, next-header ICMPv6 (58) payload length: 72) 2001:89ab:23::2 > 2001:7ab:1::a: [icmp6 sum ok] ICMP6, time exceeded in-transit for 2001:7ab:3::c
13:26:20.740206 IP6 (flowlabel 0xee1ca, hlim 3, next-header UDP (17) payload length: 24) 2001:7ab:1::a.60819 > 2001:7ab:3::c.33434: [bad udp cksum 0x4f9b -> 0x934d!] UDP, length 16
13:26:20.740297 IP6 (flowlabel 0xe744c, hlim 62, next-header ICMPv6 (58) payload length: 72) 2001:7ab:3::c > 2001:7ab:1::a: [icmp6 sum ok] ICMP6, destination unreachable, unreachable port, 2001:7ab:3::c udp port 33434
# sh dump-traceroute.sh r2-eth1
tcpdump: listening on r2-eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
13:26:20.739288 IP6 (flowlabel 0xee1ca, hlim 1, next-header UDP (17) payload length: 24) 2001:7ab:1::a.60819 > 2001:7ab:3::c.33434: [bad udp cksum 0x4f9b -> 0x2152!] UDP, length 16
13:26:20.739307 IP6 (flowlabel 0x6dc40, hlim 64, next-header ICMPv6 (58) payload length: 72) 2001:89ab:23::2 > 2001:7ab:1::a: [icmp6 sum ok] ICMP6, time exceeded in-transit for 2001:7ab:3::c
13:26:20.740221 IP6 (flowlabel 0xee1ca, hlim 2, next-header UDP (17) payload length: 24) 2001:7ab:1::a.60819 > 2001:7ab:3::c.33434: [bad udp cksum 0x4f9b -> 0x934d!] UDP, length 16
13:26:20.740290 IP6 (flowlabel 0xe744c, hlim 63, next-header ICMPv6 (58) payload length: 72) 2001:7ab:3::c > 2001:7ab:1::a: [icmp6 sum ok] ICMP6, destination unreachable, unreachable port, 2001:7ab:3::c udp port 33434
We clearly observe that traceroute6
sends UDP segments with a decreasing HopLimit. On the other hand, the intermediate routers reply with ICMPv6 time exceeded messages while the destination returns an ICMPv6 port unreachable message back to the source.
This is a first example with static IPv6 routes which could bootstrap students who are learning IPv6 routing. Other examples will follow. Suggestions are welcome as issues or better pull requests on https://github.com/obonaventure/RoutingExamples