P4C
The P4 Compiler
|
The p4c-ubpf compiler allows to translate P4 programs into the uBPF programs. We use the uBPF implementation provided by the P4rt-OVS switch. The uBPF VM is based on the open-source implementation provided by IOVisor.
The P4-to-uBPF compiler accepts only the P4_16 programs written for the ubpf_model.p4 architecture model.
The backend for uBPF is mostly based on P4-to-eBPF compiler. In fact, it implements the same concepts, but generates C code, which is compatible with the user space BPF implementation.
Please, refer to the overview of P4 written in eBPF Backend.
Why uBPF? The uBPF Virtual Machine can be used in any solution implementing the kernel bypass (e.g. DPDK apps).
The uBPF project re-implements the eBPF kernel-based Virtual Machine. While the BPF programs are intented to run in the kernel, the uBPF project enables running the BPF programs in user-space applications. It contains eBPF assembler, disassembler, interpreter, and JIT compiler for x86-64.
Moreover, contrary to the eBPF implementation, uBPF is not licensed under GPL. The uBPF implementation is licensed under Apache License, version 2.0.
The scope of the uBPF backend is wider than the scope of the eBPF backend. Except for simple packet filtering the P4-to-uBPF compiler supports also P4 registers and programmable actions including packet's modifications and tunneling. For further details refer to uBPF architecture model.
Note! Due to the reason that the standard_metadata
has been introduced to the uBPF model at 15th of May 2020 the old P4 programs will not work anymore. You should update your P4 program to the latest architecture model. Alternatively, you can also specify the old version of uBPF model: #define UBPF_MODEL_VERSION 20200304
before #include <ubpf_model.p4>
.
The current version of the P4-to-uBPF compiler translates P4_16 programs to programs written in the C language. This program is compatible with the uBPF VM and the clang
compiler can be used to generate uBPF bytecode.
We follow the convention of the P4-to-eBPF compiler so the parser translation is presented in the table Translating parsers from P4-to-eBPF. The translation of match-action pipelines is presented in the Translating match-action pipelines table from P4-to-eBPF.
However, we introduced some modifications, which are listed below:
mark_to_drop()
extern to the ubpf
model, so that packets to drop are marked in the P4-native way.The sample P4 programs are located in the examples/
directory. We have tested them with the P4rt-OVS switch - the Open vSwitch that can be extended with BPF programs at runtime. See the detailed tutorial on how to run and test those examples.
In order to generate the C code use the following command:
p4c-ubpf PROGRAM.p4 -o out.c
This command will generate out.c and the corresponding out.h file containing definitions of the packet structures and BPF maps.
Once the C program is generated it can be compiled using:
clang -O2 -target bpf -c out.c -o /tmp/out.o
The output file (out.o
) can be injected to the uBPF VM.
This Section contains description of the basic P4 programs, which were used to test the functionality of the P4-to-uBPF compiler. All tests have been run on the P4rt-OVS switch.
You can use Vagrantfile to set up a test environment.
Before any experiment the following commands need to be invoked:
Note! The P4-uBPF compiler works properly with clang-6.0
. We noticed some problems when using older versions of clang (e.g. 3.9).
This section presents how to run and test the P4-uBPF compiler.
This section presents a P4 program, which modifies the packet's fields.
Key: Source IPv4 address
Actions:
Sample usage:
The aim of this example is to test modification of wider packet's fields. Thus, we have used the IPv6 headers.
The match key is source IPv6 address. The P4 program implements three actions:
length % 8 != 0
.Sample usage:
This section presents P4 programs, which use registers. Register can be declared this way:
Register<value_type, key_type>(number_of_elements) register_t;
The parameters are as follows:
value_type
- is bit array type (i.e. bit<32>) or struct like type key_type
- is bit array type (i.e. bit<32>) or struct like type number_of_elements
- the maximum number of key-value pairsCurrently, the ubpf
architecture model does not allow to initialize registers with default values. Initialization has to be done by a control plane.
The rate limiter uses two registers. First which counts the number of packets and second which holds timestamps.
This rate limiter limits the number of packets per second. Responsible for that are two variables BUCKET_SIZE and WINDOW_SIZE placed in rate-limiter.p4 file. For instance now BUCKET_SIZE has value of 10 and WINDOW_SIZE has value of 100. It means that 10 packets are passed in 100 ms window. It also means 100 packets per second. If you send 1470 Bytes width packets the bit rate should not exceed 1.176 Mbit/s (1470B * 8 * (10/100ms)).
Due to registers limitation before starting your own tests initialize rate limiter registers with zeros:
To measure the bandwidth use the iperf
tool:
Start a iperf
UDP server
Then, run iperf
client:
The same rate limiter as above, but implemented using structs.
The packet counter counts every packet passed via the program. Before tests initialize packet counter register with zeros:
Then generate any network traffic. To check if the program counts packets use i.e.
This is very simple example of stateful firewall. Every TCP packet is analyzed to track the state of the TCP connection. If the traffic belongs to known connection it is passed. Otherwise, it is dropped.
Notice that the example program uses hash function which is constrained to hash only 64 bit values - that's why TCP connection is identified via IP source and destination address. This is the known limitation of the uBPF
backend used in P4rt-OVS (to be fixed in the future).
Due to registers limitation before starting your own tests initialize simple firewall registers with zeros:
To test simple firewall you can use as an example ptf/simple-firewall-test.py
test.
This section presents more complex examples of packet tunneling operations. There are two P4 programs used:
tunneling.p4
, which implements MPLS tunneling,vxlan.p4
, which implements more complex packet tunneling: VXLAN.To run example compile vxlan.p4
with p4c
and then clang-6.0
.
Sample usage:
To run example compile gtp.p4
with p4c
and then clang-6.0
.
To test encapsulation:
To test decapsulation:
Scapy can be used to easily test GTP protocol:
Tests use two VMs:
switch
- on this VM we run PTF testsgenerator
- expose two interfaces to P4rt-OVS switch installed on switch VMNote. As P4rt-OVS (the test uBPF target) is built on top of DPDK the tests require to be run in virtual environment with two VMs (generator
+ switch
).
Virtualbox
and Vagrant
on you machine: `sudo apt install -y virtualbox vagrant`
Install and configure environment for tests
Run PTF agent on generator machine
Compile P4 programs on switch machine
Run tests on switch machine
Important
After any system's reboot the below scripts have to be run again:
In case of tests errors please check if the p4rt-ovs
switch is up and running (ie. ps aux
).
-->
The P4 to uBPF compiler allows to define custom C extern functions and call them from P4 program as P4 action.
The design of this feature is identical to p4c-ebpf
. See the P4 to eBPF documentation to learn how to use this feature. Note that the C extern function written for p4c-ubpf
must be compatible with userspace BPF VM.
Tomasz OsiĆski <tomas.nosp@m.z.os.nosp@m.inski.nosp@m.2@or.nosp@m.ange..nosp@m.com>
Mateusz Kossakowski <mateu.nosp@m.sz.k.nosp@m.ossak.nosp@m.owsk.nosp@m.i@ora.nosp@m.nge..nosp@m.com>