--- 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" ![](https://i.imgur.com/BEtcVQ0.png) ## 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 :::