The goal of this exercise is to implement and provide a control plane to the ECMP routing exercise from 2 weeks ago. Unlike in the previous exercises where you specified the entries for the forwarding tables manually, we will now implement a controller that generates and installs forwarding rules automatically, based on the network topology.
In traditional networks the control plane is the brain of any networking device. It is in charge of deciding where packets have to be sent. Devices's control planes exchange topology information with other devices and compute which is the best way of sending traffic based on some routing protocol (RIP, OSPF, BGP).
To simplify things a lot in this exercise we will not implement a distributed and dynamic control plane like the ones mentioned above, but something simple, centralized and static.
Before you start this exercise, update p4-utils
, some new features and bugs were fixed during the last week:
cd ~/p4-tools/p4-utils
git pull
For this exercise we provide you with the following files:
p4app.json
: describes the topology we want to create with the help of mininet and p4-utils package. It is the same topology we used for the ECMP exercise.p4src/ecmp.p4
: we will use the solution of the 05-ECMP exercise as starting point.send.py
: a small python script to generate multiple packets with different tcp port.routing-controller.py
: routing controller skeleton. The controller uses global topology information and the simple switchruntime_API
to populate the routing tables.topology_generator.py
: python script that automatically generatesp4app
configuration files. It allows you to generate 3 types of topologies: linear, circular, and random (with a node number and degree). Run it with-h
option to see the command line parameters.
For this exercise we will use the l3
assignment strategy. Unlike the mixed
strategy where hosts connected to the same
switch formed a subnetwork and each switch formed a different domain. In the l3
assignment, we consider switches to only work
at the layer 3, meaning that each interface must belong to a different subnetwork. If you use the namings hY
and sX
(e.g h1, h2, s1, s2...),
the IP assignment will go as follows:
- Host IPs:
10.x.y.2
, wherex
is the id of the gateway switch, andy
is the host id. - Switch ports directly connected to a host:
10.x.y.1
, wherex
is the id of the gateway switch, andy
is the host id. - Switch to Switch interfaces:
20.sw1.sw2.<1,2>
. Wheresw1
is the id of the first switch (following the order in thep4app
link definition),sw2
is the id of the second switch. The last byte is 1 for sw1's interface and 2 for sw2's interface.
Note that it is the first time we assign IP addresses to a switch. However, it is very important to note that actually p4-utils
will not assign those IPs
to the switches, but it will save them so they can be virtually
used for some switch functionality (we will see what this means later).
You can find all the documentation about p4app.json
in the p4-utils
documentation.
The data plane program of this exercise is identical to the solution of last week's ECMP exercise (you are free to use your own solution instead of the one we provide you if you prefer, however the following description will be based on the ECMP solution we provide).
Take a moment to go through the P4 code again to remind yourself how the program works before starting to implement the controller.
The main task of the controller (we provide a skeleton in routing-controller.py
) is to translate the network topology
(stored in topology.db
) to match-action table entries. For example for the topology that we used in last week's ECMP exercise,
it should run the following commands to fill the ipv4_lpm
and ecmp_group_to_nhop
tables in switch s1
(note that the IPs used below follow the
l3
assignment strategy and not the mixed
one so IPs will not match to the ones used in the previous exercise):
table_set_default ipv4_lpm drop
table_set_default ecmp_group_to_nhop drop
table_add ipv4_lpm set_nhop 10.1.1.2/32 => 00:00:0a:01:01:02 1
table_add ipv4_lpm ecmp_group 10.6.2.2/32 => 1 4
table_add ecmp_group_to_nhop set_nhop 1 0 => 00:00:00:02:01:00 2
table_add ecmp_group_to_nhop set_nhop 1 1 => 00:00:00:03:01:00 3
table_add ecmp_group_to_nhop set_nhop 1 2 => 00:00:00:04:01:00 4
table_add ecmp_group_to_nhop set_nhop 1 3 => 00:00:00:05:01:00 5
You have to write your controller application in the routing-controller.py
file that we already provided you. You will see that we already implemented some
small functions that use the Topology
and SimpleSwitchAPI
objects from p4utils. Among others, the provided functions do:
connect_to_switches()
: function that establishes a connection with the simple switchthrift
server using theSimpleSwitchAPI
object and saves those objects in theself.controllers
dictionary. This dictionary has the form of:{'sw_name' : SimpleSwitchAPI()}
.reset_states()
: iterates over theself.controllers
object and runs thereset_state
function which will empty the state (registers, tables, etc) for every switch.set_table_defaults()
: for each p4 switch it sets the default action foripv4_lpm
andecmp_group_to_nhop
tables.
In this exercise your task is to implement the route
function which is in charge of
populating the table entries such that you can route traffic using the shortest path in the network.
Furthermore, if multiple equal cost paths are found you have to assign them to an ECMP group.
At a high level, the route
function should do the following:
- Iterate over all pairs of switches in the topology
- Compute all the shortest paths between each of these pairs of switches
- Install the table entries needed depending on the following 3 scenarios:
- If source switch and destination switch are the same. Install an entry for each directly connected host: You need host ip (use
/32
), mac address, and in which port index it is connected to the switch. - If there is a single path between src switch and destination switch and the destination switch has direct hosts connected: this time use the next hop to get the output port and the destination mac address.
- If there are multiple paths between src switch and destination switch and the destination switch has direct hosts connected: create a ecmp group (as in the example above) for all multiple next hops needed to reach the destination switch. If for the same source switch the same multiple hops have to be used for another destination use the already defined ecmp group.
- If source switch and destination switch are the same. Install an entry for each directly connected host: You need host ip (use
To get information about the shortest paths, ip addresses, mac addresses, port indexes and how nodes are connected between each other you will have to strongly utilize the topology object from p4-utils
.
To implement the routing function you will have to strongly utilize the topology object from p4-utils
.
You can find documentation about all the functions you have to use to solve this exercise in the p4-utils documentation page (all the functions documented should be enough to solve the exercise). However, if you want to, you can also find the topology object source code here and use other functions.
Once you completed your implementation of the route
function of the controller, you can test the program the same way as the ECMP exercise last week:
-
Start the topology (this will also compile and load the program).
sudo p4run
-
Run the controller.
python routing-controller.py
-
Check that you can ping:
> mininet pingall
-
check that ECMP works: monitor the 4 links from
s1
that will be used duringecmp
(froms1-eth2
tos1-eth5
). Doing this you will be able to check which path is each flow taking.sudo tcpdump -enn -i s1-ethX
-
Ping between two hosts:
You should see traffic in only 1 or 2 interfaces (due to the return path). Since all the ping packets have the same 5-tuple.
-
Do iperf between two hosts:
You should also see traffic in 1 or 2 interfaces (due to the return path). Since all the packets belonging to the same flow have the same 5-tuple, and thus the hash always returns the same index.
-
Get a terminal in
h1
. Use thesend.py
.python send.py 10.6.2.2 1000
This will send
tcp syn
packets with random ports. Now you should see packets going to all the interfaces, since each packet will have a different hash.
Now you have a controller that should be able to populate automatically the routing tables of any topology. To test that your solution does work with other topologies you can use the
topology_generator.py
script we provided you and generate random topologies:
python topology_generator.py --output_name <name.json> --topo random -n <number of switches to use> -d <average switch degree>
This will create a random topology with n
switches that have on average d
interfaces (depending on n
, d
might not be possible). Also each switch will have one host directly connected to it (so n
hosts).
Run the random topology:
sudo p4run --config <name.json>
Run the controller, and check that your can send traffic to all the nodes. Furthermore, check that ECMP works.
We have added a small guideline in the documentation section. Use it as a reference when things do not work as expected.