Skip to content

Tutorial Assignments

HARSH GONDALIYA edited this page Jul 31, 2019 · 18 revisions

P4->NetFPGA Tutorial Assignments

In this Lab session you will have the opportunity to complete the following three projects:

  1. Switch Calculator - using the switch as a calculator and key-value store

  2. TCP Monitor - using the switch to monitor the number of bytes transferred in each TCP connection

  3. In-band Network Telemetry (INT) - implement basic support for INT to facilitate network measurements


Set Up

The hardware testing component of these tutorial assignments require a machine equipped with both a SUME board and a dual port 10G NIC connected as shown below.

10G NIC           SUME
------          ---------
     |          |
eth1 --<======>-- nf0
     |          |
eth2 --<======>-- nf1
     |          |
------         -- nf2
                |
               -- nf3
                |
                ---------

Assignment 1: Switch Calculator

This is a simple exercise which demonstrates many of the basic features of the P4->NetFPGA workflow. In this assignment, you will write a P4 program that configures the NetFPGA SUME switch to act as a simple calculator and key-value store.

The operations that will be supported are:

  • ADD - add two operands and return the result
  • SUBTRACT - subtract two operands and return the result
  • ADD_REG - add an operand to the current value stored in a register on the switch and return the result
  • SET_REG - set the value of a register on the switch
  • LOOKUP - lookup the given key in a table on the switch and return the result

In order to ask the switch to perform one of these operations, a client will send a packet with the following header on top of the Ethernet layer:

header Calc_h {
    bit<32> op1;
    bit<8> opCode;
    bit<32> op2;
    bit<32> result;
}

Where op1 and op2 are the first and second operand respectively, opCode indicates which of the 5 supported operations to perform, and the result field is set by the switch after performing the required computation. The switch should also swap the source and destination MAC addresses on the received packet before sending the final packet back to the client.

In summary, the switch will perform the following tasks:

  • Receive and parse packet from client
  • Swap the source and destination MAC addresses
  • Examine the opCode field to determine the appropriate operation to perform
  • Set the result field if necessary
  • Construct the final packet and send it back to the client

The following image shows how the operands and registers should be used to perform the various functions:

switch_calc_ops.png

What to do:

To complete this assignment you will need to do the following:

  1. Modify $SUME_FOLDER/tools/settings.sh to ensure that the P4_PROJECT_NAME environment variable is set to switch_calc. Run $ source settings.sh

  2. Complete switch_calc.p4 - a skeleton P4 program has been provided for you in $P4_PROJECT_DIR/src/switch_calc.p4. TopParser and TopDeparser are already complete. TopPipe (i.e. the match-action pipeline) has all of the necessary tables and actions defined. Your job is to fill in the control flow to implement the switch_calc program. Note that the commands.txt file in the same directory fills the entries in the lookup_table.

  3. Review gen_testdata.py - this is the python script that generates the test data (i.e. applied/expected packets and metadata) to be used in the simulations that verify functionality. The file is located in $P4_PROJECT_DIR/testdata/.

  4. Run the P4-SDNet compiler to generate the resulting HDL and an initial simulation framework:

    $ cd $P4_PROJECT_DIR && make

  5. Run the SDNet simulation:

    $ cd $P4_PROJECT_DIR/nf_sume_sdnet_ip/SimpleSumeSwitch

    $ ./vivado_sim.bash.

    Note: you may also run vivado_sim_waveform.bash if you would like to fire up the Vivado GUI and see the HDL waveforms (this is a very useful debugging tool).

    If this simulation passes great! If it does not you will need to modify either your P4 program or your gen_testdata.py script.

  6. Generate the scripts that can be used in the NetFPGA SUME simulations to configure the table entries.

    $ cd $P4_PROJECT_DIR && make config_writes

  7. Wrap SDNet output in wrapper module and install as a SUME library core:

    $ cd $P4_PROJECT_DIR && make uninstall_sdnet && make install_sdnet

  8. Set up the SUME simulation. The $NF_DESIGN_DIR/test/sim_switch_default directory contains a run.py script which is responsible for running a SUME simulation, check it out. You will see that it reads the test packets generated by the gen_testdata.py script and applies the packets to SUME interfaces. All we need to do here is copy the config_writes.py script generated in step 6 into this directory by running make.

    $ cd $NF_DESIGN_DIR/test/sim_switch_default && make

  9. Run the SUME simulation. The following command launches the SUME simulation and calls the run.py script in the $NF_DESIGN_DIR/test/sim_switch_default/ directory:

    $ cd $SUME_FOLDER

    $ ./tools/scripts/nf_test.py sim --major switch --minor default

    Note: you may also run the above command with the --gui option to fire up the Vivado GUI and see the HDL waveforms. Again, a very useful debugging tool.

    Also run the sim_switch_ctrlWrites simulation, which demonstrates how to read/write registers in the SUME simulations. Remember to run make in this test directory as well.

    If all simulations pass and you don't plan to perform hardware testing then you are done with this assignment. Otherwise, move onto the next step.

  10. Compile the bitstream.

    $ cd $NF_DESIGN_DIR && make

  11. Check that the design meets the timing requirements. See this FAQ for information about how to do this.

  12. Copy the bitstream file and config_writes.sh script into the $NF_DESIGN_DIR/bitfiles directory.

    $ cd $NF_DESIGN_DIR/bitfiles

    $ mv simple_sume_switch.bit ${P4_PROJECT_NAME}.bit

    $ cp $P4_PROJECT_DIR/testdata/config_writes.sh ./

  13. Program FPGA. Make sure that the P4_PROJECT_NAME environment variable is set correctly in the $SUME_FOLDER/tools/settings.sh file. Source this settings.sh file.

    $ cd $NF_DESIGN_DIR/bitfiles/

    $ sudo bash

    # bash program_switch.sh

    Note: Make sure that all the configuration writes succeed. If this is the first time you are programming the FPGA since the machine was last powered off then you might get ERROR messages stating No such device.

    To resolve this, follow the below-mentioned steps:

    a. Reboot the Machine

    b. Execute the following commands (taken from Step 9 on Getting Started Page in this repo):

    [root@nf-test109 ~]# cd $DRIVER_FOLDER

    [root@nf-test109 sume_riffa_v1_0_0]# make all

    [root@nf-test109 sume_riffa_v1_0_0]# make install

    [root@nf-test109 sume_riffa_v1_0_0]# modprobe sume_riffa

    [root@nf-test109 sume_riffa_v1_0_0]# lsmod | grep sume_riffa

    c. Now, you will be able to see nf0, nf1, nf2 and nf3 ports in your ifconfig output. Re-execute the aforementioned commands in Step 13. This time the configuration writes & the loading bitstream process should succeed.

  14. Test the design on real hardware! Go to the $P4_PROJECT_DIR/sw/CLI directory and run the P4_SWITCH_CLI.py script. This initiates an interactive command line interface that you can use to interact with your switch (i.e. read/write registers, add/remove table entries, etc.). Type help to see the list of available commands.

    Go to the $P4_PROJECT_DIR/sw/hw_test_tool directory and run $ sudo bash then run the switch_calc_tester.py script. This will initiate a command line tool that you can use to submit packets to the switch and view its response. Type help run_test to see how to use the command.

    Try adding two numbers testing> run_test 2 + 3 and seeing what you get. Also try doing things like setting one of the const register entries from the command line then submitting an ADD_REG packet. Or submit a SET_REG packet and read the value from the command line. Or add a new entry into the lookup_table and submit a LOOKUP packet to get the result.

  15. If the switch appears to be working congratulations! You've finished the assignment!

Hints:

  • Writing the P4 program:

    • You can check if your P4 program will compile by entering into the $P4_PROJECT_DIR/src directory and running $ make
    • You will need to use one of the register atom externs available in the P4->NetFPGA library. In this case, we have already instantiated the const_reg_rw atom for you, but you will need to use it in the control flow. These registers can only be accessed one time in the P4 code.
  • Debugging tools:

    • See here for debugging tips.

Assignment 2: TCP Monitor

This tutorial is designed to give users experience writing stateful P4 programs by utilizing some of the register atoms available in the P4->NetFPGA extern library.

In this assignment, you will write a P4 program to configure the NetFPGA SUME to perform some basic TCP connection monitoring. Recall that a TCP connection is established with the 3-way handshake (SYN, SYN-ACK, ACK) and in general, completes after a FIN packet is sent in both directions. For the purposes of this assignment, we will define a flow as all of the packets with a particular 5-tuple ID (srcIP, dstIP, protocol, source_port, destination_port); a flow starts with a packet that has the SYN bit set and ends with a packet that has the FIN bit set. Note that using this definition, each TCP connection consists of two flows, one in each direction.

The TCP Monitor P4 program will compute the size of each flow (in bytes) and after the flow has completed, it will update a histogram indicating the distribution of flow sizes that have passed through the switch. The control-plane can read this histogram off of the switch and display the current distribution in real time.

The general idea is as follows:

  • There are two register arrays:

    • byte_cnt - stores the current size in bytes of all "active" flows. Where an "active" flow is one in which the SYN packet has been seen, but the FIN packet has not been seen yet.
    • dist - stores the histogram of flow sizes that have been seen to pass through the switch.
  • The 5-tuple is extracted from each arriving packet and hashed using a simple longitudinal redundancy check (LRC) to compute the index with which to access the byte_cnt register.

  • The initial SYN packet will reset the byte_cnt register entry to 0

  • Each subsequent packet of the flow will increment its corresponding entry in the byte_cnt register with the size of its TCP payload.

  • The FIN packet will extract the final size of the flow from the byte_cnt register and use it to increment one of the entries of the dist register.

  • Note: It is true that the 5-tuple of different flows may hash to the same entry of the byte_cnt register, which will skew the resulting histogram towards larger flows. We will ignore that case for the purposes of this assignment to keep things simple.

What to do:

  1. Modify $SUME_FOLDER/tools/settings.sh to ensure that the P4_PROJECT_NAME environment variable is set to tcp_monitor. Run $ source settings.sh

  2. Complete tcp_monitor.p4 - a skeleton P4 program has been provided for you in $P4_PROJECT_DIR/src/tcp_monitor.p4. TopParser and TopDeparser are already complete. TopPipe (i.e. the match-action pipeline) has all of the necessary tables and actions defined. Your job is to fill in the control flow to implement the tcp_monitor program.

  3. Review gen_testdata.py - we have completed the gen_testdata.py script for you, but ask that you review how it uses python's scapy module to generate the flows.

  4. Follow steps 4 - 13 as listed in the "Switch Calculator" tutorial above.

  5. Hardware testing! $ cd $P4_PROJECT_DIR/sw/hw_test_tool and open up two terminal windows:

    • In one terminal, run the view_dist.py script which will periodically read the flow size distribution from the switch and display it as a histogram.

    • In the other terminal, run $ sudo bash then run the tcp_monitor_tester.py script, which initiates a command line interface that allows you to send TCP flows through the switch. Type help to see options.

    Make sure that the histogram is being updated as you would expect. Note that if you try to send too many concurrent flows through the switch, some will hash to the same byte_cnt register entry which will skew the results.

  6. If the switch appears to be working properly then congratulations! You've finished the assignment!


Assignment 3: In-band Network Telemetry (INT)

In this assignment you will write a P4 program to implement basic INT support for the NetFPGA SUME platform. INT is quickly becoming one of the most popular applications for programmable data-planes, and it's all about gaining more visibility into your network. There are different ways of adding support for INT, but in this tutorial we will roughly base our implementation on this INT specification.

The basic idea is as follows:

  • An INT source end host will generate a packet with an INT header (over Ethernet) that contains an instruction bitmask.

  • Each bit of that instruction bitmask corresponds to a different type of metadata in the switch (e.g. switch ID, ingress port, egress port, ingress timestamp, etc.).

  • If a particular bit is set in the instruction bitmask that means the switch should insert the corresponding metadata into the packet. Each piece of metadata is represented as a header with a single bottom-of-stack (bos) bit followed by 31 bits of data. The last piece of INT metadata must have the bos bit set to 1. The diagram below shows how new INT metadata is inserted into the packet.

|  Ethernet  |  INT  |  INT_data  |  INT_data  |  payload  |
|            |       |    bos:0   |    bos:1   |           |
                     ^ 
                     |
            New data inserted here
  • The format of the INT header is shown below. We will ignore the replication and copy fields to simplify the implementation.
// INT header
header int_h {
    bit<2> ver;                   // version #
    bit<2> rep;                   // replication requested
    bit<1> c;                     // is copy 
    bit<1> e;                     // max hop count exceeded
    bit<5> rsvd1;                 // reserved 1 
    bit<5> ins_cnt;               // # of 1's in instruction bitmask
    bit<8> max_hop_cnt;           // max # hops allowed to add metadata
    bit<8> total_hop_cnt;         // # hops that have added metadata 
    bit<5> instruction_bitmask;   // which metadata to add to packet
    bit<27> rsvd2;                // reserved 2
}
  • Our INT implementation will support gathering 5 types of metadata from the switch:

    • SwitchID
    • Ingress Port
    • Egress Queue Occupancy
    • Ingress Timestamp
    • Egress Port
  • Upon receiving the final packet, the INT sink end host will extract the resulting metadata.

What to do:

  1. Modify $SUME_FOLDER/tools/settings.sh to ensure that the P4_PROJECT_NAME environment variable is set to int. Run $ source settings.sh

  2. Complete int.p4 - a skeleton P4 program has been provided for you in $P4_PROJECT_DIR/src/int.p4.

  3. Review gen_testdata.py

  4. Follow steps 4 - 13 as listed in the "Switch Calculator" tutorial above.

  5. Hardware testing! $ cd $P4_PROJECT_DIR/sw/hw_test_tool and open up two terminal windows:

    • In one terminal run $ sudo bash then run # ./rcv_int, which receives, parses, and prints received INT packets.

    • In the other terminal run $ sudo bash then run # ./int_tester.py, which allows you to send INT packets with a particular instruction bitmask into the switch. For example, testing> run_test 0b11001 will submit an INT packet that requests to switch to insert the switchID, ingress port, and egress port into the packet. Type testing> help run_test to see documentation about the command.

If the switch appears to be working congratulations! You've completed the assignment.