Changelog:
- 17 Sep 2024: update skeleton code in git re: calling myutil.py decode_packet_in_metadata function with wrong number of arguments, and add note to about manual change for this below
- 26 Sep 2024: update skeleton code in git re: decode_packet_in_metadata being a buggy version
- 26 Sep 2024: more detailed desription re: retrieving information from packets copied to the controller; add hint for verification step
- 27 Sep 2024: add note about
extra
messages during network setup - 1 Oct 2024: correct
tutorials/cs4457_f24
totutorials/exercises/cs4457_f24
- 2 Oct 2024: ARM64 VM instructions link
- 2 Oct 2024: note
print_counters.py
update - 5 Oct 2024: split out advice about using wireshark to separate hints section and refer to it from all the steps, not just the last one. Also note that if checking
print_counters.py
, there will be packets from autoconfiguraiton, etc. counted there too in step 1 instructions. - 7 Oct 2024: note that step 1 is intended to be done main() (not something that won’t consistently run)
1 Your Task
1.1 Part 1
Download the supplied VM image for Virtualbox (x86-64) at https://virginia.box.com/s/vl6jdfo27jooxbb07i3setll1l7tac14
(or:
follow these instructions for an ARM64 VM for Apple Silicon Macs.
install p4 according to the instructions at https://github.com/jafingerhut/p4-guide/blob/master/bin/README-install-troubleshooting.md [which should work on arm64 VM running on UTM on OS X, but I have not tested this personally], and then
move the
stock
P4 tutorials out of the way usingmv tutorials tutorials.orig
then copy our modified version of the p4 tutorials using
git clone -b cs4457-f24-base https://github.com/charlesreiss/p4tutorials.git ./tutorials
)
Run the VM and login (username/password p4/p4 for the Virtualbox image)
Go to the
tutorials/exercises/cs4457_f24
directory.Run
git pull
to make sure you have the latest version of the skeleton code.Following the instructions below, setup a simulated network with four simulated hosts (
h1
,h2
,h3
, andh4
) one simulated switch (s1
) running a data plane specified inmydataplane.p4
and a control plane implemented inmycontroller.py
.To do this, as described below, you’ll run
make
to start the network emulatormininet
. This will give you a prompt allowing you to run commands on each of the hosts. In another terminal, runpython3 mycontroller.py
to run the controller for the switchs1
.Verify that the initial implementation:
- does not allow hosts to be reachable from each other until the controller is run to initialize the switch (using commands like
h1 ping h2
orpingall
oriperfudp 1M h1 h2
in mininet); - allows all hosts to be reachable from each other when the switch is initialize
- broadcasts all frames sent by any simulated host to all other simulated hosts. This means:
- running the command
python3 print_counters.py
in a separate terminal will show a count of the number of packets and bytes sent to each of the switch’s output ports. You should see that ports 1 through 4 all have the same number of packets/bytes output, no matter what commands you run. - the packet traces in pcaps/s1-ethX_out.pcap will show what the switch outputted to each port. Examined with wireshark or with a command like
tcpdump -er pcaps/s1-ethX_out.pcap
, they should show that each port received the same data.
- running the command
- does not allow hosts to be reachable from each other until the controller is run to initialize the switch (using commands like
There is no submission for part 1, but if your VM setup is not working, then you will not be ready to do part 2.
1.2 Part 2
The initially supplied switch implementation sends all packets to all ports, rather than directing packets to a particular port based on their destination MAC address.
Modify it so that it does a simple form of
MAC learning
:- The controller should learn the association between MAC addresses and switch ports.
- Based on this association, the controller should setup tables in the dataplane to route packets to each MAc address to the appropriate port instead of having them broadcast to all ports.
(A better version of this functionality would implement ARP (address resolution protocol), but that is not expected for this assignment.)
As part of your implementation, you will need to arrange for some packets to be sent to the controller. You should arrange for packets to only be sent to the controller if the dataplane cannot route them to the correct port.
We recommend following the instructions below under
Part 2 recommendation
, but this is not required.Verify that in your implementation:
packets are directed to the correct port rather than being broadcast (without the MAC addresses being hard-coded in the switch in advance)
once the controller learns about a host, few packets from the host are sent to the controller (most packets are handled exclusively by the data plane
fast path
of the switch)
Describe how you verified your implementation in a file called
results.txt
orresults.pdf
. Include any relevant output.Submit your modified
mycontroller.py
,mydataplane.p4
andresults.txt
orresults.pdf
.(Note that this assignment will be manually graded.)
2 Part 2 recommendation
2.1 Step 1: Setting a fixed table entry
Modify
mycontroller.py
to make the following call in main() towrite_or_overwrite_table_entry
to make it so packets destined to 08:00:00:00:01:01 (h1
’s MAC address intopology.json
) are sent to port 1:write_or_overwrite_table_entry( p4info_helper=p4info_helper, switch=s1, table_name='MyIngress.mac_dst_lpm', match_fields={ # first 48 bits match this MAC address 'hdr.ethernet.dstAddr': ('08:00:00:00:01:01', 48), }, action_name='MyIngress.forward_to_port', action_params={ 'port': 1, } )
Verify that this change causes packets destined for h1
not to be duplicated on other ports but should still leave h1 reachable.
I would suggest examining the pcaps
files as described below under the heading Examining pcaps
to do this. Alternately, you can send pings and see how it changes the values from print_counters.py
, but note that its counts includes autoconfiguration packets, not just your pings and the like.
In later steps rather than hard-coding the MAC address of h1
and its corresponding port, you will infer that information from packets the switch receives.
2.2 Step 2: make packets be sent to the controller
Modify
mydataplane.p4
to uncomment the callcopy_to_controller()
beforemac_dst_lpm.apply()
.[Edited 17 Sep 2024]: Make sure the
process_packet
function contains the fixed code (which it should if you rangit pull
since 17 Sep 2024):metadata = decode_packet_in_metadata( p4info_helper, switch, packet.metadata )
and not:
metadata = decode_packet_in_metadata(packet.metadata)
[Edited 26 Sep 2024]: And make sure myutil.py is the latest version from git (which it should be if you ran
git pull
since 26 Sep 2024, or you can download from here)After doing this (and restarting the network simulation),
mycontroller.py
should start outputting information about every packet sent when you run commands in mininet. This output is produced by theprocess_packet
function that you will modify later.(You might see some extra messages when mininet is getting setup from IPv6 autoconfiguration. These messages can be sent before mininet finishes configuring the MAC addresses in the hosts in the simulated topology, so they may use MAC addresses that aren’t part of the configured topology.)
If you run a command like
h1 ping h2
, you should see ICMP echo request and ICMP echo reply messages.
2.3 Step 3: Set mac_dst_lpm
entries dynamically
Modify
process_packet
inmycontroller.py
to add entries similar to the ones we added manually above forh1
dynamically. Rather than hard-coding any MAC addresses and port numbers, you should look at each transmitted packet that comes from a MAC address not yet handled and add entries to the tables to handle forwarding for that MAC address.You can use
frame.src
andframe.dst
to retrieve the MAC addresses sent in the incoming frames.You can use
metadata['inPort'][0]
to retrieve the input port number of the packet.Verify that your solution makes it so that, after each host sends some traffic (such as because you ran
pingall
), each host does only receives packets destined for its own MAC address (or packets sent to a broadcast/multicast MAC address).(Like with step 1, I would suggest examining the pcaps files to do this.)
2.4 Step 4: Limit which packets are sent to the controller
Add a new table
mac_src_lpm
ormac_port_src_lpm
, which is likemac_dst_lpm
, but:- matches based on the source mac address and (optionally) input port instead of the destination mac address
- has a default action of copy_to_controller and another possible action of
NoAction
Replace the call to
copy_to_controller()
with an applicatoin of this table. Since the default action in the table is copy_to_controller, this will initially have the same effectModify
process_packet
to:- add a table entry to new table to override the default
copy_to_controller
setting toNoAction
, so the switch does not receive future packets for that source MAC address and (optionally) port, and - (optional) if a machine changes from one port to another, to overwrite or delete any table mac_port_src_lpm entry added for the prior port. This will ensure that if the machine changes back to the original port, then the controller will handle that operation.
- add a table entry to new table to override the default
2.5 Step 5: Verification
- One way to verify that the correct behavior is happening is to run a bunch of
hX ping hY
commands (wherehX
andhY
would be h1 through h4) and examine the packet traces in thepcaps
directory as described below under the headingExamining pcaps
. Note that each of these packet traces represent a single direction on a link.
3 Hints
3.1 Examining pcaps
As mentioned above the simulation produces a bunch of packet traces in the
pcaps
subdirectory of thetutorials/exercises/cs4457_f24
directory. Each one represents the packets sent in one direction from the switch.You can open these with wireshark:
type
wireshark
orwireshark &
at the command line to open wireshark;use File > Open to find the .pcap files in wireshark;
if you want to view traffic going in both directions to one of the ports, you can open the
in
file with File > Open, then open theout
file with File > Merge. You should then examine both of the traces;
You can also examine these from the command line with a command like:
tcpdump -er pcaps/FILE.pcap
3.2 Understanding mydataplane.p4:
mydataplane.p4
: This contains an implementation of the data plane of a very simple switch:Initially, frames are parsed in MyParser(), which extracts headers for use by later steps.
In this stage, we also setup a special header (called
hdr.packet_in
in the code) that is used to send packets1 to the control plane for special processing. Corresponding code in MyEgress() will disable this header before sending out packets to destinations other than the control plane.Then packets enter ingress processing MyIngress(). This stage does table lookups based on the destination MAC addresses of the packet. The tables can be configured by the control plane to choose one of four actions:
- forwarding the packet to a particular output port;
- broadcasting the packet to all (non-controller) output ports;
- doing nothing
There’s also an action which is initially not used called
copy_to_controller()
which sends a copy of the current packet to the controller. (In addition, the packet also undergoes normal processing.)If a packet is sent on any output port (including to the controller), then MyEgress() runs. Currently this has logic which will:
- avoid sending the control-plane information header to non-control-plane ports
- increments counters to track how many bytes and packets are sent to each destination port
Before packets are sent out, MyDeparser() runs, which converts headers back into bytes.
3.3 Understanding mycontroller.py
mycontroller.py
which contains an implementation of the control plane.The code in the
main()
function:- connects to the switch
- loads the P4 program into the switch (from a compiled file which will be produced by
make
) - configures the switch to support functionality to send packets to multiple destinations
- runs a loop where it receives packets sent from the switch and sends them to the process_packet function
Initially, the loop that receives packets from the switch won’t do anything because the cs4457_f24.p4 does not run the
copy_to_controller
action.The
process_packet
function decodes somemetadata
fields that are setup bycs4457_f24.p4
. These are defined in theheader packet_in_header_t
incs4457_f24.p4
and set inMyIngress
(by modifyinghdr.packet_in
).To prevent the
metadata
fields from being output as part of packets sent to the simulated hosts, code in MyEgress marks this metadata as invalid except when the switch is outputting to the controler.The controller’s code in
process_packet
extracts the fields from the metadata into local variables for your convenience later. When the fields are sent to the controller, each metadata field is sent separately, but without only an index instead of a name, and without information about the size or format of the field. This means that if you modify the metadata fields incs4457_f24.p4
without corresponding modifications tomycontroller.py
, mycontroller will read the wrong data.
3.4 Running the simulated network
topology.json
specifies the layout of the simulated network and how the hosts are configured.In this case, there will be one switch called
s1
connected to four hostsh1
,h2
,h3
,h4
.The hosts are assigned MAC addresses and informed of the MAC addresses of all other hosts.
Running
make
will compile the .p4 file and start a simulated network based ontopology.json
, and give you a prompt in themininet
tool.In the mininet tool you can:
Run a command
FOO
on a particular simulated host, likeh1
, using a commandh1 FOO
.In the command you run the names of other hosts will be replaced by their IP addresses.
For example
h1 ping h2
will run the commandping 10.0.1.2
(since10.0.1.2
is the configured IP address of h2 in topology.json). Theping
command sends echo requests (using the IP control protocol ICMP) to a particular host and prints out messages when it gets echo replies.The simulated hosts are implemented by running a normal program with its network configuration changed, so you can run arbitrary commands.
Get a terminal within a simulated host
h1
usingxterm h1
Test whether each host is reachable from each other with
pingall
Send data from host
h1
to hosth2
using TCP using a command likeiperf h1 h2
(named after the a well known networking testing tool callediperf
)Send data from host
h1
to hosth2
using UDP at 1Mbps using a commnad likeiperfudp 1M h1 h2
.
(and more, see also the command
help
in mininet)We need to run our controller program to setup the emulated switch. Until this is done, none of the hosts will be able to contact each other.
You can run
python3 mycontroller.py
in another terminal to do this.Note that exiting
mininet
or terminating the python3 program will stop the network.We provide a utility program called
print_counters.py
which will contact the emulated switch and retrieve counts of how many packets and bytes have been sent on each port.You can run it in a separate terminal using
python3 print_counters.py
. (Note that you will need a version from git on/after 2 October because it previously expected filenames inbuild/
that didn’t matchmydataplane.p4
.)The utility program uses the same code as
mycontroller.py
to connect to this switch, but does send any commands that would register it as the controller of the switch or any commands that would reconfigure the switch.
really, layer-2 frames, but we will use the term
packet
since that is consistent with the P4 documentation.↩︎