---
tags: mininet-wifi-tutorials
---
# P4
:::info
**In this short demo you will:**
- comprehend the difference between P4 and OpenFlow
- learn the basics of P4
- run your first P4 scenario
- have some clues on how to build your own _network protocol_
:::
## What is P4?
* is not OpenFlow 2.0!
* both P4 and OpenFlow focus on opening up the forwarding plane
* addresses the need to program the data plane
* OpenFlow assumes the switches have a fixed, well-known behavior, while P4 allows us to program PISA (Protocol Independent Switch Architecture) chips
* let's us control switches "top-down"

## ONOS
ONOS stands for **O**pen **N**etwork **O**perating **S**ystem.
- If you are familiar with **server operating systems**, you will find that ONOS provides some analogous types of functionality, including APIs and abstractions, resource allocation, and permissions, as well as user-facing software such as a CLI, a GUI, and system applications.
- If you are familiar with traditional "**inside the box**" switch operating systems, you will find that ONOS manages your entire network rather than a single device, which can dramatically simplify management, configuration, and deployment of new software, hardware and services.
- If you are familiar with **SDN controllers**, you should feel right at home because the ONOS platform and applications act as an extensible, modular, distributed SDN controller.
**More information**:
https://wiki.onosproject.org/display/ONOS/ONOS
### Running ONOS (demo)
:::warning
**Requirements:**
- P4 (`sudo util/install.sh -P` installs P4)
- ONOS
- Mininet-WiFi
- Scapy
:::
#### Starting with P4 - Static rules
```
~/mininet-wifi$ sudo python examples/p4/p4.py
```
#### Starting with ONOS - Dynamic rules
In one terminal:
```
~/onos$ bazel run onos-local
```
In another terminal:
```
~/onos$ . ~/.bash_profile
~/onos$ onos localhost
onos> app activate fwd drivers.bmv2 drivers.mellanox pipelines.fabric proxyarp lldpprovider hostprovider segmentrouting
```
```
onos> apps -a -s
```
Now open in your favorite web browser:
```
http://localhost:8181/onos/ui/index.html
```
:::info
*** Default _username_ and _password_ are onos/rocks ***
:::
In a third terminal:
```
~/mininet-wifi$ sudo python examples/p4/p4.py -r
```
### Handover scenario
<**handover.<span>py**>
```python=
#!/usr/bin/python
import os
import sys
from mininet.log import setLogLevel, info
from mn_wifi.cli import CLI
from mn_wifi.net import Mininet_wifi
from mn_wifi.bmv2 import P4Switch
from mininet.term import makeTerm
from mininet.node import RemoteController
def topology(remote_controller):
'Create a network.'
net = Mininet_wifi()
info('*** Adding stations/hosts\n')
h1 = net.addHost('h1', ip='10.0.0.1', mac="00:00:00:00:00:01")
sta1 = net.addStation('sta1', ip='10.0.0.2', mac="00:00:00:00:00:02")
info('*** Adding APs\n')
ap1 = net.addAccessPoint('ap1', failMode='standalone', mac="00:00:00:00:00:11",
ssid='ap1', position='85,70,0')
ap2 = net.addAccessPoint('ap2', failMode='standalone', mac="00:00:00:00:00:12",
ssid='ap2', position='155,70,0')
info('*** Adding P4Switch\n')
if remote_controller:
s1 = net.addSwitch('s1', netcfg=True, thriftport=50001, cls=P4Switch)
else:
path = os.path.dirname(os.path.abspath(__file__))
json_file = path + '/handover.json'
config1 = path + '/commands_s1.txt'
s1 = net.addSwitch('s1', netcfg=True, json=json_file, thriftport=50001,
switch_config=config1, cls=P4Switch)
info('*** Adding Controller\n')
if remote_controller:
net.addController('c0', controller=RemoteController)
info("*** Configuring propagation model\n")
net.setPropagationModel(model="logDistance", exp=4)
info('*** Configuring WiFi Nodes\n')
net.configureWifiNodes()
info('*** Creating links\n')
net.addLink(s1, ap1)
net.addLink(s1, ap2)
net.addLink(s1, h1)
info('*** Plotting Graph\n')
net.plotGraph(max_x=200, max_y=200)
info('*** Configuring Mobility\n')
net.startMobility(time=0)
net.mobility(sta1, 'start', time=1, position='10,50,0')
net.mobility(sta1, 'stop', time=30, position='175,50,0')
net.stopMobility(time=30)
info('*** Starting network\n')
net.start()
if not remote_controller:
net.staticArp()
ap1.cmd('ovs-ofctl add-flow ap1 in_port=1,actions:output=2')
ap1.cmd('ovs-ofctl add-flow ap1 in_port=2,actions:output=1')
ap2.cmd('ovs-ofctl add-flow ap2 in_port=1,actions:output=2')
ap2.cmd('ovs-ofctl add-flow ap2 in_port=2,actions:output=1')
#ap1.cmd('iw dev ap1-wlan1 interface add mon1 type monitor')
#ap2.cmd('iw dev ap2-wlan1 interface add mon2 type monitor')
#ap1.cmd('ip link set mon1 up')
#ap2.cmd('ip link set mon2 up')
makeTerm(sta1, cmd="bash -c 'ping 10.0.0.1;'")
#makeTerm(ap1, cmd="bash -c 'cd %s && python sniffer.py mon1;'" % path)
#makeTerm(ap2, cmd="bash -c 'cd %s && python sniffer.py mon2;'" % path)
info('*** Running CLI\n')
CLI(net)
os.system('pkill -9 -f \'xterm\'')
info('*** Stopping network\n')
net.stop()
if __name__ == '__main__':
setLogLevel('info')
remote_controller = True if '-r' in sys.argv else False
topology(remote_controller)
```
<**handover.p4**>
```clike
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>
const bit<16> TYPE_IPV4 = 0x800;
const bit<8> TCP_TYPE = 0x06;
/*************************************************************************
*********************** H E A D E R S ***********************************
*************************************************************************/
typedef bit<9> egressSpec_t;
typedef bit<9> ingressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;
header ethernet_t {
macAddr_t dstAddr;
macAddr_t srcAddr;
bit<16> etherType;
}
header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
ip4Addr_t srcAddr;
ip4Addr_t dstAddr;
}
header p4wifi_t {
bit<56> newdata;
macAddr_t bssid;
macAddr_t mac;
}
header tcp_t {
bit<16> srcPort;
bit<16> dstPort;
bit<32> seqNo;
bit<32> ackNo;
bit<4> dataOffset;
bit<3> res;
bit<3> flags_1;
bit<3> flags_2;
bit<3> flags_3;
bit<16> window;
bit<16> checksum;
bit<16> urgentPtr;
}
struct metadata {
/* empty */
}
struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
p4wifi_t p4wifi;
tcp_t tcp;
}
/*************************************************************************
*********************** P A R S E R ***********************************
*************************************************************************/
parser MyParser(packet_in packet,
out headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
state start {
transition parse_ethernet;
}
state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
TYPE_IPV4: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
packet.extract(hdr.ipv4);
transition select(hdr.ipv4.protocol) {
TCP_TYPE : parse_tcp;
default : accept;
}
}
state parse_tcp {
packet.extract(hdr.tcp);
transition parse_wifi;
}
state parse_wifi {
packet.extract(hdr.p4wifi);
transition accept;
}
}
/*************************************************************************
************ C H E C K S U M V E R I F I C A T I O N *************
*************************************************************************/
control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
apply { }
}
/*************************************************************************
************** I N G R E S S P R O C E S S I N G *******************
*************************************************************************/
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
action drop() {
mark_to_drop(standard_metadata);
}
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
standard_metadata.egress_spec = port;
hdr.ethernet.dstAddr = dstAddr;
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
}
table ipv4_lpm {
key = {
hdr.ipv4.dstAddr: exact;
}
actions = {
ipv4_forward;
drop;
NoAction;
}
size = 1024;
default_action = NoAction();
}
apply {
if (hdr.ipv4.isValid()) {
ipv4_lpm.apply();
}
}
}
/*************************************************************************
**************** E G R E S S P R O C E S S I N G *******************
*************************************************************************/
control MyEgress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
apply { }
}
/*************************************************************************
************* C H E C K S U M C O M P U T A T I O N **************
*************************************************************************/
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
apply {
update_checksum(
hdr.ipv4.isValid(),
{ hdr.ipv4.version,
hdr.ipv4.ihl,
hdr.ipv4.diffserv,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr },
hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16);
}
}
/*************************************************************************
*********************** D E P A R S E R *******************************
*************************************************************************/
control MyDeparser(packet_out packet, in headers hdr) {
apply {
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
packet.emit(hdr.tcp);
packet.emit(hdr.p4wifi);
}
}
/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
```
<**commands_s1.txt**>
```bash
table_set_default ipv4_lpm drop
table_add MyIngress.ipv4_lpm ipv4_forward 10.0.0.1 => 00:00:00:00:00:01 3
table_add MyIngress.ipv4_lpm ipv4_forward 10.0.0.2 => 00:00:00:00:00:02 1
```
Compiling the P4 code:
```
p4c handover.p4
```
Running the script:
```
sudo python handover-p4.py
```
Getting flows:
```
simple_switch_CLI --thrift-port 50001 <<< "table_dump MyIngress.ipv4_lpm"
```
Manual rules:
```
simple_switch_CLI --thrift-port 50001 <<< "table_delete MyIngress.ipv4_lpm 1"
simple_switch_CLI --thrift-port 50001 <<< "table_add MyIngress.ipv4_lpm ipv4_forward 10.0.0.2 => 00:00:00:00:00:02 2"
```
or simply:
```
simple_switch_CLI --thrift-port 50001 <<< "table_modify MyIngress.ipv4_lpm ipv4_forward 1 00:00:00:00:00:02 2"
```
### Using the mobility application
```
~/onos$ . ~/.bash_profile
~/onos$ onos localhost
onos> app activate fwd drivers.bmv2 drivers.mellanox pipelines.fabric proxyarp lldpprovider hostprovider segmentrouting mobility
```
Running the script:
```
~$ sudo python handover.py -r
```
### Existing applications
You can find a list of applications at: https://github.com/opennetworkinglab/onos/tree/master/apps
### Creating your own application
If you wish to create an application from scratch, or wish to create a small program with minimal code and functionality you may want to refer to: https://wiki.onosproject.org/display/ONOS/Creating+your+own+apps
<**sniffer.<span>py**>
```python=
#!/usr/bin/env python
import sys
from scapy.all import *
nodeID = str(sys.argv[1][-1:])
def handle_pkt(pkt):
if pkt.addr2[-1:] == nodeID:
print(pkt.addr1)
def main():
iface = sys.argv[1]
print("sniffing on %s" % iface)
sys.stdout.flush()
sniff(iface=iface, lfilter=lambda x: x.haslayer(Dot11AssoResp),
prn=lambda x: handle_pkt(x))
if __name__ == '__main__':
main()
```
### Building my own _network protocol_
:::info
**See:** https://github.com/ramonfontes/tutorials/tree/master/exercises/handover
:::