Exercise 1: First approach to the P4 development process - grupogita/ONOSP4-tutorial GitHub Wiki
This exercise provides a first approach to the P4 development process. In this exercise, you will configure a very basic topology in mininet, formed by two hosts and a software switch programmable with P4. The goal of this exercise is enabling the sucessful execution of the ping command between the two hosts. You will also adjust some parameters of the mininet topology.
You will have to modify the following files in this exercise:
- topo.py located on the path ONOSP4-tutorial/Exercise-1/mininet/topo.py
- main.p4 located on the path ONOSP4-tutorial/Exercise-1/p4src/main.p4
- flows file located on the path ONOSP4-tutorial/Exercise-1/p4src/flows
Topology and Mininet
The exercise requires the creation of two links between two devices (H1 - H2) and one switch (SW). The following diagram shows the resulting topology:
The first task to perform is to edit the file “topo.py” by addressing the tasks indicated with the ”TO-DO” comments within the code.
After performing the previous step, you can check the topology was created correctly using the following commands:
make app-build
make start
make mn-log
NOTES:
- The
make app-build
command, in addition to building the ONOS pipeline, executes themake p4-build
. Hence, it is not necessary to use the make p4-build command separatedly.- Although the main.p4 file has not yet been edited, it is necessary to build the p4 program because the command
make start
requires it to create the switch s1 to be used in the topolgy.- The file topo.py must have write access permissions to allow its execution within the
make start
command. Usechmod 755 topo.py
shell command before executing themake start
command. Otherwise, themake start
command will fail.
At the end of the last command, the CLI output is expected to look like the following code block. Line 15 indicates the topology was successfully created with mininet (on the console, you might find a lightning bolt emoticon between s1
and stratum_bmv2
):
1 docker logs -f mininet
2 *** Error setting resource limits. Mininet's performance may be affected
3 *** Creating network
4 *** Adding hosts:
5
6 *** Adding switches:
7 s1
8 *** Adding links:
9
10 *** Configuring hosts
11
12 *** Starting controller
13
14 *** Starting 1 switches
15 s1 stratum_bmv2 @ 50001
16
17 *** Starting CLI
Another way to check that the topology was successfully created is to use the command make mn-cli
to access the mininet CLI. Then, you can use the net
command to visualize the topology currently deployed on Mininet. You should see a CLI output similar to the following code block:
1 *** Attaching to Mininet CLI...
2 *** To detach press Ctrl-D (Mininet will keep running)
3 mininet> net
4 h1 h1-eth0: s1-eth1
5 h2 h2-eth0: s1-eth2
6 s1 lo: s1-eth1:h1-eth0
NOTE: At this point, the ping between h1 and h2 is not successful since the switch behavior has not been defined yet (i.e. The switch has not been programmed via the P4 Program).
P4 program
For the purpose of this exercise, the P4 program is simple and it simply indicates that all traffic coming in on port 1 will go out through port 2. The same way, when the traffic is coming in on port 2, it will go out through port 1.
The logic of the P4 program, and therefore the behavior of the switch, is based on the action
table. For the scenario that is going to be defined in this exercise, the switch will execute an action (i.e. forwarding a packet) depending on the input and destination parameters. In order to perform this action, the switch must have entries (i.e. flow entries) in this action
table. This will be done in the next section of this exercise.
It is necessary to be familiar with the structure of the file that defines the P4 program (main.p4) and how it works. The following figure shows graphically the operation and interaction of the parts which make up the program:
This main.p4 file is used to program the switch. In the template file, you will find the following code blocks which conform the basic mandatory structure of a P4 program:
- Header definitions: It contains the specification of the protocol fields that the switch will recognize and process. It also contains some structs that define internal variables used by the program. These structs will be better explained in more advanced exercises.
- Parser: This is the first block of code that processes traffic entering the switch. It has a structure similar to a C/JAVA switch-case structure and resembles a finite state machine. It has several
state
statements representing the sequence (i.e. transitions) evaluating different protocol headers. In the code, it is implemented in the ParserImpl() function. - Checksum Verification: This is a block which can be used to verify the checksum fields for different packets (e.g. IP, TCP, UDP). It is not used in this example. In the code it is implemented in the VerifyChecksumImpl() function.
- Ingress: This is the block performed for packets entering the switch. This block contains three main sections. The first section defines actions, which actually perform operations on the packet. In this action, header fields can be evaluated or modified, or information from the packet can be obtained. The second section defines tables, which are the match/action structures performing operations on the packets. A table contains two elements: a key, which refers to a field (usually, a protocol field) which will be used for matching, and a set of actions corresponding to those previously defined. The third section is the
apply
block where the apply method of the corresponding tables is invoked. This invocation is the actual matching against the corresponding table. In the code, it is implemented in the IngressPipeImpl() function - Egress processing: This is a block which is executed for the packets departing from the switch. It has function and structure similar to the Ingress block. In the code, it is implemented in the EgressPipeImpl() function.
- Checksum Calculation: This is a block which can be used to calculate the checksum fields for different packets (e.g. IP, TCP, UDP). It is not used in this example. In the code it is implemented in the ComputeChecksumImpl() function.
- Deparser: It is analogous to the Parser block. Instead of disassembling the packet, the Deparser builds the packet that will leave the switch. In the code, it is implemented in the DeparserImpl() function.
- Switch Model: It is the main block of the program where all the switch behavior is described by integrating the previously mentioned blocks. In the code, it is implemented with the V1SwitchModel() invocation and instantiation of the main function.
For further details on this structure, you can refer to the P4 Language specification.
Modifications to the main.p4 file
In order to complete this exercise you have to modify the file "main.p4". The instructions can be found in the file with the comment code ”TO-DO”. The objective is to emit an ipv4 packet by modifying the Deparser block of the P4 program.
Flows and successful PING
When the make start
command was executed after performing the changes indicated in the ”TO-DO” comments of the topo.py file, the modification of the P4 program had not been performed. After performing the corresponding modifications to the main.p4 file, run the following commands:
make stop
to stop all previously started dependenciesmake app-build
to compile the edited P4 programmake start
to start the topology and the edited P4 programmake app-reload
to install and launch the ONOS app
At this point, the P4 program defining the switch behavior to enable communication between the hosts through the P4 switch has been designed, the topology has been created and the ONOS application has been launched. However, the topology has not been made visible to the ONOS controller. Hence, it is necessary to execute the command make netcfg
which sends the JSON file which indicates to the ONOS controller the capabilities of the switch. After performing this step, the ONOS controller becomes aware of the topology, and therefore it can control the switch. Also, the topology can be seen both from the Graphical and Command Line interfaces.
Checking the topology from the ONOS Graphical User Interface (GUI)
To access the graphical user interface of the ONOS application, open a web browser window and enter the following URL http://<controller IP>:8181/onos/ui
where controller IP
refers to IP address of the machine where the controller is running. If you are running this tutorial directly on the same machine, you can use the 127.0.0.1
IP address. Verify in the GUI the topology and features in the P4 switch such as the capacity of each port and its speed.
NOTE: The default credentials to access the ONOS GUI are the following:
- user: onos
- password: rocks
Interacting with the ONOS controller through the REST API
One of the ways to interact with the ONOS controller is via its REST API. This interface is documented in detail and from the documentation page itself enables the interaction with the controller. In the browser of preference, enter the following address http://<controller IP>:8181/onos/v1/docs/
(keeping the credentials and the value for controller IP
used for the GUI) and follow the next steps:
- Go to "flows: Query and program flow rules" link.
- Select "flow/{deviceId}" link which will allow you to post a message to create a flow entry in a device by its ID.
- In the "device Id" field, enter the switch ID. If you are not sure about this value, you can go to the "devices" section in order to find it out.
- In the "appId" field, set the default indicator to 0.
- The documentation provides an example flow entry used as a reference to generate the CURL command to be used directly on the operating system shell or through a REST client such as Postman. Click on the yellow box on the right side to use the example flow.
- Click on the "Try it out" button and copy the generated segment titled "Curl".
Note: Check the Response Code value in order to verify if the invocation was sucessful. It should return a 2XX code. Otherwise, there was an error invoking the command. In this case, you have to check the Response Body and Response header fields to look for the error.
Note: In order to communicate with the REST API, you can use the curl(1) operating system command or a REST client such as Postman. For this exercise Postman will be used as an intermediary to manage and send the CURL commands in order to define the entries for the action table in the switch (i.e. the flow entries). It will communicate through the REST API with the controller. The information previously obtained through the interface available in the documentation page will be necessary to obtain the CURL characteristic of the switch. Follow the next steps:
- Start Postman
- Click on "import" and select raw text.
- Paste the CURL generated from the ONOS documentation and import it.
NOTE: The process to obtain the CURL from the ONOS documentation is performed once for each switch.
From now it is possible to manipulate the device (in this case the switch initially created with mininet) from Postman for the flow injection. The third task requires the modification of the file "flows" locate at the p4src folder. This file contains the structure of the REST commands that will create the entries for the action table. This file contains some ”TO-DO” indicating information that must be provided when modifying the code of these commands.
The Postman authentication method must be modified in order to be able to post commands to the switch. For this purpose, in the "Authentication" section, select "Basic Auth" as the authentication method with the credentials used to access the ONOS GUI.
After that, you can modify in the "body" section the desired REST command. Copy and paste the REST commands in the "flows" file and finally press the "send" button to send the REST command to the switch via the POST method.
NOTE: In the flows file there are two flow structures. Differentiate them and send them one by one. That is, repeat the process of modifying the "body".
Final verification
Now a successful ping can be performed from the mininet CLI using the following commands from the home CLI:
make mn-cli
h1 ping h2