p4app is a tool that can build, run, debug, and test P4 programs. The philosophy behind p4app is "easy things should be easy" - p4app is designed to make small, simple P4 programs easy to write and easy to share with others.
-
Install docker if you don't already have it.
-
If you want, put the
p4app
script somewhere in your path. For example:cp p4app /usr/local/bin
That's it! You're done.
p4app runs p4app packages. p4app packages are just directories with a .p4app
extension - for example, if you wrote a router in P4, you might place it in
router.p4app
. Inside the directory, you'd place your P4 program, any
supporting files, and a main.py
file that tells p4app how to run it.
This repository comes with an example p4app called wire.p4app. Here's how you can run it:
p4app run examples/wire.p4app
If you run this command, you'll find yourself at a Mininet command prompt. p4app
will automatically download a Docker image containing the P4 compiler and tools,
compile wire.p4, and set up a container with a simulated network you
can use to experiment. In addition to Mininet itself, you can use tshark
,
scapy
, and the net-tools and nmap suites right out of the box.
That's pretty much it! There's one more useful command, though. p4app caches the P4 compiler and tools locally, so you don't have to redownload them every time, but from time to time you may want to update to the latest versions. When that time comes, run:
p4app update
A p4app package has a directory structure that looks like this:
my_program.p4app
|
|- main.py
|
|- my_program.p4
|
|- ...other files...
The main.py
file is a Python script that tells p4app how to build and run a
P4 program; it's comparable to a Makefile. Here's how examples/wire.p4app/main.py looks:
from p4app import P4Mininet
from mininet.topo import SingleSwitchTopo
topo = SingleSwitchTopo(2)
net = P4Mininet(program='wire.p4', topo=topo)
net.start()
net.pingAll()
from mininet.cli import CLI
CLI(net)
This p4app script starts by importing P4Mininet, which is a p4app library that
extends Mininet with special functionality like compiling P4 programs and
configuring BMV2 switches. The script imports the SingleSwitchTopo
topology from Mininet. In this case the topo
has two hosts connected to a
single switch. The Mininet network is instantiated by calling P4Mininet
,
providing it with the program (wire.p4
), as well as the topology it created
(topo
). Once the network net
is started, it can be used like a standard
Mininet network. Here the script tests connectivity by running Mininet's
pingAll()
. Finally, the script launches the mininet CLI by calling CLI(net)
.
This brings up a CLI that you can use to debug your network.
Configuring tables in your P4 program is easy with p4app. P4app provides a
wrapper around P4Runtime to configure tables in your P4-16 program. You can
call insertTableEntry()
on a switch object to add a table entry to that
switch. From examples/ring.p4app:
sw.insertTableEntry(table_name='MyIngress.ipv4_lpm',
match_fields={'hdr.ipv4.dstAddr': ["10.0.0.%d" % i, 32]},
action_name='MyIngress.ipv4_forward',
action_params={'dstAddr': '00:00:00:00:00:%02x' % i,
'port': 1})
You can also set a table's default action with default_action=True
:
sw.insertTableEntry(table_name='MyIngress.ipv4_lpm',
default_action=True,
action_name='MyIngress.ipv4_forward',
action_params={'dstAddr': '00:00:00:00:00:00',
'port': 2})
To inspect all the table entries for a switch, you can use:
sw.printTableEntries()
You can add multicast groups using addMulticastGroup()
. From
examples/multicast.p4app:
sw.addMulticastGroup(mgid=mgid, ports=range(1, n+1))
You can also update and delete multicast groups:
sw.updateMulticastGroup(mgid=mgid, ports=[2])
sw.deleteMulticastGroup(mgid=mgid, ports=[])
If your P4 program defines a counter, you can read it from the main.py
script
while the switch is running using readCounter()
. From
examples/counter.p4app:
packet_count, byte_count = s1.readCounter('ingressPortCounter', port)
If you are running a P4-14 program, there is no P4Runtime support, so you won't
be able to use the methods described above. Instead, you can use
simple_switch_CLI
to configure the runtime of a P4-14 program. p4app provides
an interface to simple_switch_CLI
through a command()
method on each
switch. For example, to add a table entry to switch s1
:
s1.command('table_add ipv4_lpm set_nhop 10.0.0.10/32 => 10.0.0.10 1')
To run commands interactively on a currently running p4app, you can use
p4app exec command arg1 arg2 ...
This will run a command in a currently running p4app instance. If there are multiple instances running, the command will be executed on the most recently started p4app.
To run a command on a Mininet host, you can use the Mininet m
utility script,
which is included in p4app. For example, run ping
on Mininet host h1
:
p4app exec m h1 ping 10.0.0.2
Or, you can simply open a bash shell on one of the mininet hosts:
p4app exec m h1 bash
You can also run tcpdump
on the Mininet host to see packets in realtime with
Wireshark:
p4app exec m h1 tcpdump -Uw - | wireshark -ki -
If you're hacking on the P4 toolchain or p4app itself, you may want to use a
modified Docker image instead of the standard p4lang one. That's easy to do;
just set the P4APP_IMAGE
environment variable to the Docker image you'd like
to use. For example:
P4APP_IMAGE=me/my_p4app_image:latest p4app run my_program.p4app
When you invoke p4app, you can pass extra arguments after the path to your
p4app directory. These arguments will be passed to the p4app main.py
script.
For example, you can specify the number of switches for the ring.p4app
example:
~/p4app/p4app run examples/ring.p4app 4
The script parses these arguments using Python's sys
:
import sys
if len(sys.argv) > 1:
N = int(sys.argv[1])
By default, p4app will mount the directory /tmp/p4app-logs
on the host to
/tmp/p4app-logs
on the docker container (guest). The output from bmv2, as well
as any output from your programs, will be saved to this directory. Instead of
using the default directory (/tmp/p4app-logs
), you can specify another
directory with the $P4APP_LOGDIR
environment variable. For example, if you
run:
P4APP_LOGDIR=./out p4app run myapp.p4app
all the log files will be stored to ./out
.