Try   HackMD

Programmable Network Switches

Lab2 - Using P4 and P4Runtime to Implement the Learning Switch Protocol

Overview

In this lab, you will write P4 code to program the dataplane and utilize the P4 Runtime Shell package in Python to perform the controller's tasks.

Environment

Setup

  1. Import the .ova file into VirtualBox and start the VM.

    • You can usep4/p4 for normal usage
    • Or vagrant/vagrant for root permission
  2. Download the sample code archive.

wget http://140.113.216.90:8000/p4.zip
  1. Unzip the sample code into the VM and place it in ~/tutorials. Overwrite if prompted. The folder should contain the following files:

    • exercises/
      • learning_switch/
        • disable_ipv6.sh
        • learning_switch.p4
        • Makefile
        • mycontroller.py
        • README.md
    • utils/
    • vm/
    • vm-ubuntu-20.04/
    • LICENSE
    • P4_tutorial.pdf
    • p4-cheat-sheet.pdf
    • README.md
    • run_controller.py
  2. Open a console and execute the following command:

pip3 install p4runtime-shell

Topology

In this lab, you will construct a hierarchical tree topology as illustrated below:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

The IP and MAC addresses of each host are listed as follows:

  • h1: 10.0.0.1, 08:00:00:00:01:11
  • h2: 10.0.0.2, 08:00:00:00:02:22
  • h3: 10.0.0.3, 08:00:00:00:03:33
  • h4: 10.0.0.4, 08:00:00:00:04:44

Note: Since all topology setups have been completed for you, you don't need to perform any actions in this step. If you are interested in knowing how to set MAC, IP, ARP, and port configurations, please refer to ~/tutorials/learning_switch/sig-topo.

Test

After setting up your VM, open two consoles and navigate to ~/tutorials/learning_switch/. Run the following commands:

# terminal 1
make run

# terminal 2
python3 run_controller.py

Once you see

# terminal 1
...
To view the P4Runtime requests sent to the switch, check the
corresponding txt file in /home/p4/tutorials/exercises/learning_switch/logs:
 for example run:  cat /home/p4/tutorials/exercises/learning_switch/logs/s1-p4runtime-requests.txt

mininet>

You can run

mininet> pingall

This command tests ping reachability between all hosts, equivalent to individual pings between hosts (e.g., h1 ping h2, h1 ping h3, …).

You will see the result:

mininet> pingall
*** Ping: testing ping reachability
h1 -> X X X 
h2 -> X X X 
h3 -> X X X 
h4 -> X X X 
*** Results: 100% dropped (0/12 received)

You can use Ctrl+C to terminate the ping, and use exit to stop the Mininet.

The other console should display some messages like below:

$ python3 run_controller.py 
[127.0.0.1:50051]: setup success!
[127.0.0.1:50052]: setup success!
[127.0.0.1:50053]: setup success!
[127.0.0.1:50051]: 08:00:00:00:01:11 08:00:00:00:02:22 0
[127.0.0.1:50051]: 08:00:00:00:01:11 08:00:00:00:03:33 0
[127.0.0.1:50051]: 08:00:00:00:01:11 08:00:00:00:04:44 0
[127.0.0.1:50051]: 08:00:00:00:02:22 08:00:00:00:01:11 0
[127.0.0.1:50051]: 08:00:00:00:02:22 08:00:00:00:03:33 0
[127.0.0.1:50051]: 08:00:00:00:02:22 08:00:00:00:04:44 0
[127.0.0.1:50052]: 08:00:00:00:03:33 08:00:00:00:01:11 0
[127.0.0.1:50052]: 08:00:00:00:03:33 08:00:00:00:02:22 0
[127.0.0.1:50052]: 08:00:00:00:03:33 08:00:00:00:04:44 0
[127.0.0.1:50052]: 08:00:00:00:04:44 08:00:00:00:01:11 0
[127.0.0.1:50052]: 08:00:00:00:04:44 08:00:00:00:02:22 0
[127.0.0.1:50052]: 08:00:00:00:04:44 08:00:00:00:03:33 0

The socket address 127.0.0.1:50051 corresponds to s1, 127.0.0.1:50052 is for s2, and 127.0.0.1:50053 is for s3.

Because the unmatched packet will be sent to the controller, and the controller currently only displays packet source, destination, and incoming port without any further functionality, the ping requests will not reach the destination side now. Additionally, the incoming port is reported as 0 because the switch is not providing this information to the controller. Your task is to modify the P4 code and the controller logic to enable the learning switch functionality.

The overall logic for the learning switch implementation involves the following steps:

  1. When receiving a packet:

    • Save the (source MAC address -> incoming port) mapping.
  2. If the destination MAC address is in the mapping:

    • Retrieve the corresponding port from the mapping.
    • Install a forwarding rule based on both the destination MAC address and the source MAC address.
  3. If the destination MAC address is not in the mapping:

    • Perform flooding to ensure the packet is forwarded out of all ports except the incoming port. (Using the packet out header)
  4. Forward the packet:

    • Use the obtained port to forward the packet out. (The ingress port, egress port, and the decision of whether to flood or not should be communicated using the packet out header)

Implementation Steps

  • In learning_switch.p4

    • TODO 1: After extracting the packet_out header from the switch packet, you should store the necessary fields into metadata.
    • TODO 2: In the send_to_cpu action, some information should be placed into the packet_in header to inform the controller. Please fill in these details.
    • TODO 3: The source MAC address and destination MAC address should be utilized in the table match.
    • TODO 4: If the packet_out header is valid, it indicates that the packet is from the switch. In this case, you should retrieve the egress port decided by the controller and set it to flooding if the flooding flag is set.
  • In mycontroller.py

    • TODO 1: Write a mapping of (source MAC -> incoming port). Check if the destination MAC is in the mapping. If it is, set the out_port to that value; otherwise, set it to FLOODING.
    • TODO 2: Select the table and action that will be used. Set the match field and action parameters. Then, insert the entry into the table.
    • TODO 3: Build a packet to be sent out by unicast.
    • TODO 4: Build a packet to be sent out by flooding.

Submission

  • {student_id}_lab2.zip
    • learning_switch.p4
    • mycontroller.py

Tips

  • Feel free to use the self.log method for debugging purposes. Its usage is the same as print() in Python, but it will add the switch address in front of the print message.
  • Insert table entry:
te = sh.TableEntry('MyIngress.some_table')(action="some_action")
te.match['hdr.ethernet.some_field'] = some_value
te.action['some_param'] = param
te.insert()
  • Build packet out:
packet_out = sh.PacketOut()
packet_out.payload = payload
packet_out.metadata['some_field'] = some_value
packet_out.send()
  • If you encounter issues with Mininet not opening, execute the command make clean.
  • The architecture used here is v1model. If you are looking for reference data, don't get it wrong!