Work in progress
The content of this page is still work in progress
P4 is a domain-specific language designed to allow programming of protocol-independent packet processors. Behavioral Model v2 (BMv2) is the reference P4 software switch. Initial support for this type of device has been included into ONOS 1.6 (Goldeneye) with 2 goals: i) provide the research community with a platform to experiment with P4-based applications and ii) define a common groundwork to support programmable data planes in the next versions of ONOS.
This document will guide you through the necessary steps to program a network of BMv2 (virtual) devices using ONOS. This document assumes you are already familiar with ONOS, P4 and BMv2. In other words, we assume you already know how to build and run ONOS, write a P4 program, build and run BMv2.
Features at a glance
By using ONOS 1.6, you'll be able to program a network of BMv2 devices with all the benefits of a logically centralized SDN platform. The following features are currently supported:
- Device discovery: connection / disconnection events
JSON configuration swap
Packet-ins and packet-outs
Match-action table population (via flow rules, flow objectives, or intents)
Port statistics collections
Flow statistics collection
The figure below sketches the high-level architecture of the BMv2 integration in ONOS (click to zoom).
On the northbound, ONOS provides a new Java API called "BMv2 Device Context Service" that can be used by applications to specify at runtime the JSON configuration of a given BMv2 device. Match-action tables can be populated using existing northbound APIs such as flow rule, flow objective or intents, with native support for non-standard P4 match and actions (via extension selectors and treatments).
On the southbound, ONOS speaks with BMv2 using Thrift. This project has been based on a customized version of the BMv2 “simple_switch” target that, differently from the original one, supports primitives to send packet-in events to the controller and to receive and transmit packet-outs. The source code of onos-bmv2 is available here.
BMv2 device context
In order to enforce a given JSON configuration on a device, applications need to provide a “BMv2 Device Context”. Device contexts are used to bind together in a Java class a BMv2 JSON configuration and an “Interpreter” implementation. Interpreters are used by ONOS to “understand” a given P4 program. They provide a mapping between ONOS objects and program-specific P4 objects (e.g. headers, actions, table names, etc.), allowing existing services and apps (e.g. host tracking, LLDP discovery, ARP proxy, reactive forwarding, etc.) to work with virtually any P4 program.
The Interpreter interface defines 3 types of mapping:
ONOS table ID ↔ P4 table name
Criterion's type ↔ P4 header instance’s field name
- Flow rule's treatment instance → BMv2 action instance
While for criteria and tables it is possible to specify a 1-to-1 relationship through a map, for Instructions the same is not possible or at least it's not convenient. The reason is that Instructions in ONOS are modeled after OpenFlow actions (which are protocol-dependent), while flow rule treatments (to be applied as a consequence of a match) are usually defined as a list of multiple instructions. In P4 instead, actions are defined as a compound of low-level protocol-independent primitives (not expressible using ONOS Instructions), and, most important, P4 allows to specify only one action per table entry. Extracting the "meaning" of a given treatment instance and mapping it to a P4 action is not straightforward and it's usually program-specific. That’s why we expect a P4 programmer using ONOS to write its own interpretation logic (i.e. Java code) that can map a given treatment instance to a BMv2 action instance.
When devices connect for the first time to ONOS a “default” context is automatically applied, triggering a device configuration swap and exposing to the system a default interpreter. Such a context is used to provide a minimum set of data plane capabilities for basic ONOS services and apps to work. The default context is based on a default.json BMv2 configuration, (compiled from default.p4) and a default interpreter implementation.
FAQ regarding interpreters
- Do I necessarily need to write an interpreter for my P4 program?
No, interpreters are optional, meaning that a context can be created with an “empty” interpreter. In this case, you can’t expect other ONOS services to work with that given context. When not using an Interpreter or when creating flow rules based on non-standard match or actions, developers can use BMv2 extension treatment and selectors.
- Do I need to provide a mapping for all the headers and actions defined in my P4 program?
No, you can provide a mapping for only some of them. The general advice is to provide a mapping for those criteria and treatments used by the other ONOS services and applications you need in order to run your applications. Most of the times, you can re-use the default interpreter (provided that you write your own P4 program as an extension to default.p4)
Non-standard match and actions
BMv2 extension selectors and treatments can be used to express flow rules with match fields or actions for which either a mapping is not provided by the interpreter, or for which such a mapping is not possible using standard ONOS types. The following code example shows how to create a flow rule that uses these extensions. Header fields and actions can be referenced using the same name used in the P4 program, while the BMv2 configuration is needed by the extension builder to properly format the values according to the specific P4 program (e.g. generate the appropriate byte representation of a given match field).
ApplicationId myAppId = ...; DeviceId myDeviceId = ...; Bmv2DeviceContext myContext = ...; Bmv2Configuration myConfiguration = myContext.configuration(); Ip4Prefix dstPrefix = Ip4Prefix.valueOf("18.104.22.168/24"); ExtensionSelector extSelector = Bmv2ExtensionSelector.builder() .forConfiguration(myConfiguration) .matchExact("standard_metadata", "ingress_port", 10) .matchLpm("ipv4", "dstAddr", dstPrefix.address().toOctets(), dstPrefix.prefixLength()) .build(); ExtensionTreatment extTreatment = Bmv2ExtensionTreatment.builder() .forConfiguration(myConfiguration) .setActionName("next_hop") .addParameter("nhop_id", 4) .build(); FlowRule rule = DefaultFlowRule.builder() .forDevice(myDeviceId) .fromApp(myAppId) .forTable(0) .withSelector(DefaultTrafficSelector.builder() .extension(extSelector, myDeviceId) .build()) .withTreatment(DefaultTrafficTreatment.builder() .extension(extTreatment, myDeviceId) .build()) .build();
ONOS+P4 development environment (onos-p4-dev)
To make it easy for you to get started, we prepared a repository with all you need to build and run a Mininet network of BMv2 devices that connect to ONOS. It includes:
- Scripts to build onos-bmv2 (ONOS fork of BMv2) and p4c-bmv2 (P4 compiler for BMv2)
- Example P4 programs to get started
- Mininet custom file to use onos-bmv2
- Various utility commands to quickly debug a running BMv2 network
Follow the instructions at this link to get started:
Exporting environment variables in the shell profile
To get the most from the tools and instructions discussed in the following sections, it is highly recommended that you add this line to your shell configuration profile (.bash_aliases, .profile, etc.):
Environment variables (WIP)
This walkthrough demonstrates the necessary steps and commands to run a network of BMv2 devices in Mininet, connected to ONOS.
Build and run ONOS. This how-to screencast is a good starting point to build and run ONOS locally on your development machine, for any other information please refer to the ONOS Developer Guide.
Important! Build using Maven
We are transitioning our build system from Maven to BUCK. Most of ONOS 1.6 modules can be build using BUCK expect for the bmv2 modules which are built by Maven. Hence, be sure to build ONOS using the command:
$ mvn clean install
Activate the BMv2 drivers. This will activate the whole BMv2 southbound subsystem, including the BMv2 providers, controller, and distributed services. In the ONOS command line type:
onos> app activate org.onosproject.drivers.bmv2
Check that both the BMv2 providers and drivers have been loaded successfully. On the ONOS command line, type:
onos> app -s -a
You should see an output similar to this (depending on your startup apps defined in
* 8 org.onosproject.bmv2 1.6.1.SNAPSHOT BMv2 Providers * 18 org.onosproject.drivers 1.6.1.SNAPSHOT Default Device Drivers * 19 org.onosproject.drivers.bmv2 1.6.1.SNAPSHOT BMv2 Drivers * 26 org.onosproject.openflow-base 1.6.1.SNAPSHOT OpenFlow Provider * 27 org.onosproject.hostprovider 1.6.1.SNAPSHOT Host Location Provider * 28 org.onosproject.lldpprovider 1.6.1.SNAPSHOT LLDP Link Provider * 29 org.onosproject.openflow 1.6.1.SNAPSHOT OpenFlow Meta App * 41 org.onosproject.fwd 1.6.1.SNAPSHOT Reactive Forwarding App * 80 org.onosproject.proxyarp 1.6.1.SNAPSHOT Proxy ARP/NDP App
Start Mininet using the custom file
onos-p4-dev. On your Mininet VM (the same where you have cloned
onos-p4-dev) shell, type:
$ sudo -E mn --custom $BMV2_PY --switch onosbmv2 --controller remote,ip=192.168.57.1,port=40123
This will run a simple Mininet topology with 2 hosts connected to a BMv2 switch, to use a different topology please refer to the Mininet guide. The
-Eargument in sudo ensures that all environment variables are exported to the root user, including
$BMV2_PYis used to point to the location of the Mininet custom file
bmv2.py. All these variables are exported automatically by the
onos-p4-devshell configuration script. In our case, ONOS is running on a machine reachable from the Mininet VM at the IP address
192.168.57.1. Be sure to use the correct IP address of your ONOS instance.
40123is the default listening port of the BMv2 controller in ONOS. If successful, the output of the previous command should be similar to this:
*** Creating network *** Adding controller *** Adding hosts: h1 h2 *** Adding switches: s1 *** Adding links: (h1, s1) (h2, s1) *** Configuring hosts h1 h2 *** Starting controller c0 *** Starting 1 switches s1 Starting BMv2 target: /home/mininet/p4/onos-bmv2/targets/simple_switch/simple_switch --device-id 1 -i 1@s1-eth1 -i 2@s1-eth2 --thrift-port 38400 --log-console -Lwarn /home/mininet/p4/p4src/build/empty.json -- --controller-ip 192.168.57.1 --controller-port 40123 *** Starting CLI: mininet>
Check that the BMv2 switch is running. On the Mininet VM shell, type:
$ p4log 1 Calling target program-options parser Adding interface s1-eth1 as port 1 Adding interface s1-eth2 as port 2
This command shows the log of the BMv2 instance with device ID 1 (look for
--device-idin the Mininet startup output).
Running BMv2 for the first time
Be aware that when running BMv2 for the first time after building it, it may take a while (up to 30 seconds) before the software switch process is executed and the log file written.
Another way to check if the switch is running is by using the native BMv2 runtime CLI. In this case, you can use the
p4clicommand to print some switch information:
$ echo "switch_info" | p4cli 1 Obtaining JSON from switch... Done Control utility for runtime P4 table manipulation RuntimeCmd: device_id : 1 thrift_port : 38400 notifications_socket : ipc:///tmp/bmv2-1-notifications.ipc elogger_socket : None debugger_socket : None
Check that the BMv2 switch has successfully connected to ONOS. On the ONOS command line, check the output of the following command.
onos> devices id=bmv2:192.168.57.100:45674#1, available=true, role=NONE, type=SWITCH, mfr=p4.org, hw=bmv2, sw=1.0.0, serial=n/a, driver=bmv2-thrift, bmv2JsonConfigMd5=aefbfbd1543efbfbdefbfbdefbfbd121defbfbdefbfbd3468efbfbd76, bmv2ProcessInstanceId=-1811218096, protocol=bmv2-thrift
From the output, we can see that our BMv2 switch is connected (
available=true), along with the MD5 sum of the JSON configuration currently deployed (
bmv2JsonConfigMd5)and a unique ID of the BMv2 process instance (
bmv2ProcessInstanceId). The latter is assigned automatically at switch boot and is used by ONOS to distinguish between different executions of similar BMv2 instances (i.e. with the same device ID and MD5 sum) and to detect a potential state change of a device (e.g. a reboot after a crash of the BMv2 process) in order to promptly re-establish network state (e.g. re-install flow rules).
The MD5 sum you see here is the one of the default.json configuration that is deployed on each BMv2 switch when it first connects to ONOS.
You can also use the ONOS web GUI to explore your network. Point your browser to
http://127.0.0.1:8181/onos/ui/login.htmland authenticate yourself using username
karaf. You should be able to see something similar to this (click to zoom)
Check that the 2 hosts can ping each other. On the Mininet VM shell, use the
pingallcommand and chek the output:
mininet> pingall *** Ping: testing ping reachability h1 -> h2 h2 -> h1 *** Results: 0% dropped (2/2 received)
If the hosts can't ping this is probably due to the fact that the "Reactive Forwarding" application is not running. Refer to the previous step to check the list of active apps, if not running you can do so by typing the following on the ONOS command line:
app activate org.onosproject.fwd
Make sure that also the following applications are running:
BMv2 Device Context Service API (WIP)
- Prototype API for device configuration management, to be promoted to core APIs in the future
- How to set a context for a given device?
- Configuration swap enforcement
- Why do I need to register the interpreter's class loader?
- In ONOS 1.6.0:
- When running a cluster with multiple instances of ONOS, a stack overflow Java exception will be thrown by
Bmv2TableEntryService. This is due to a known bug of the Kryo serializer. This issue has been already fixed and is available in the latest
- When running a cluster with multiple instances of ONOS, a stack overflow Java exception will be thrown by
Future work (WIP)
- Implement match key builder for all criterion types
- Implement match key builder for all match types (ternary, exact, lpm, valid and range)