P4utils: send to cpu

[topology]

 

If a rule “mirroring_add 100 3” has been added in the switch, the switch will clone the packet and sent it to the controller. The cloned packet only contains the ethernet header and ip header. The remaining part are truncated. The controller can gather information from switches. The applications can be throughput calculation, loss detection, or deep packet inspection.

 

[Note] Before you run the following example, please do the following steps first.

git clone https://github.com/secdev/scapy.git

cd scapy

python ./setup.py install

 

[p4app.json]

{

  "program": "send_to_cpu.p4",

  "switch": "simple_switch",

  "compiler": "p4c",

  "options": "--target bmv2 --arch v1model --std p4-16",

  "switch_cli": "simple_switch_CLI",

  "cli": true,

  "pcap_dump": true,

  "enable_log": true,

  "cpu_port": true,

  "topo_module": {

    "file_path": "",

    "module_name": "p4utils.mininetlib.apptopo",

    "object_name": "AppTopo"

  },

  "controller_module": null,

  "topodb_module": {

    "file_path": "",

    "module_name": "p4utils.utils.topology",

    "object_name": "Topology"

  },

  "mininet_module": {

    "file_path": "",

    "module_name": "p4utils.mininetlib.p4net",

    "object_name": "P4Mininet"

  },

  "topology": {

    "links": [["h1","s1"], ["s3","h2"], ["s1","s2"], ["s2","s3"]],

    "hosts": {

      "h1": {

      },

      "h2": {

      }

    },

    "switches": {

      "s1": {

        "cli_input": "s1-commands.txt",

        "program": "send_to_cpu.p4",

        "cpu_port": true

      },

      "s2": {

        "cli_input": "s2-commands.txt",

        "program": "send_to_cpu.p4",

        "cpu_port": true

      },

      "s3": {

        "cli_input": "s3-commands.txt",

        "program": "send_to_cpu.p4",

        "cpu_port": true

      }          

    }

  }

 

}

 

[send_to_cpu.p4]

/* -*- P4_16 -*- */

#include <core.p4>

#include <v1model.p4>

 

const bit<16> TYPE_IPV4 = 0x800;

 

/*************************************************************************

*********************** H E A D E R S  ***********************************

*************************************************************************/

 

typedef bit<9>  egressSpec_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>    tos;

    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;

}

 

struct metadata {   

}

 

struct headers {

    ethernet_t   ethernet;

    ipv4_t       ipv4;

}

 

/*************************************************************************

*********************** 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 {

 

        packet.extract(hdr.ethernet);

        transition select(hdr.ethernet.etherType){

 

            TYPE_IPV4: ipv4;

            default: accept;

        }

 

    }

 

    state ipv4 {

 

        packet.extract(hdr.ipv4);

        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) {

 

        //set the src mac address as the previous dst, this is not correct right?

        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;

 

       //set the destination mac address that we got from the match in the table

        hdr.ethernet.dstAddr = dstAddr;

 

        //set the output port that we also get from the table

        standard_metadata.egress_spec = port;

 

        //decrease ttl by 1

        hdr.ipv4.ttl = hdr.ipv4.ttl -1;

 

    }

 

    table ipv4_lpm {

        key = {

            hdr.ipv4.dstAddr: lpm;

        }

        actions = {

            ipv4_forward;

            drop;

            NoAction;

        }

        size = 1024;

        default_action = NoAction();

    }

 

    apply {

 

        //only if IPV4 the rule is applied. Therefore other packets will not be forwarded.

        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 {

 

     if (standard_metadata.instance_type == 0 ){

         clone3(CloneType.E2E,100, meta);

     }

 

     //handle the cloned packet

     if (standard_metadata.instance_type != 0){

         truncate((bit<32>)34); //ether+ip header

     }

    }

}

 

/*************************************************************************

*************   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.tos,

              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 {

 

        //parsed headers have to be added again into the packet.

        packet.emit(hdr.ethernet);       

        packet.emit(hdr.ipv4);

 

    }

}

 

/*************************************************************************

***********************  S W I T C H  *******************************

*************************************************************************/

 

//switch architecture

V1Switch(

MyParser(),

MyVerifyChecksum(),

MyIngress(),

MyEgress(),

MyComputeChecksum(),

MyDeparser()

) main;

 

[s1-commands.txt] s1 will clone the packet and send it to the cpu.

table_set_default ipv4_lpm drop

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:0a:00:01:01 1

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:02:01:00 2

 

//creates a mirroring ID 100 to output port 3

mirroring_add 100 3

 

[s2-commands.txt]

table_set_default ipv4_lpm drop

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:01:00 1

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:01:00 2

 

[s3-commands.txt] s3 will clone the packet and send it to the cpu.

table_set_default ipv4_lpm drop

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:02:01:00 2

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.3.2/32 => 00:00:0a:00:03:02 1

 

//creates a mirroring ID 100 to output port 3

mirroring_add 100 3

 

[controller.py]

import nnpy

import struct

from p4utils.utils.topology import Topology

from p4utils.utils.sswitch_API import SimpleSwitchAPI

from scapy.all import Ether, sniff, Packet, BitField

 

class myController(object):

 

    def __init__(self):

        self.topo = Topology(db="topology.db")

        self.controllers = {}

        self.connect_to_switches()

 

    def connect_to_switches(self):

        for p4switch in self.topo.get_p4switches():

            thrift_port = self.topo.get_thrift_port(p4switch)

            #print "p4switch:", p4switch, "thrift_port:", thrift_port

            self.controllers[p4switch] = SimpleSwitchAPI(thrift_port)   

 

    def recv_msg_cpu(self, pkt):

        print "interface:", pkt.sniffed_on

        print "summary:", pkt.summary()

     

    def run_cpu_port_loop(self):

        cpu_interfaces = [str(self.topo.get_cpu_port_intf(sw_name).replace("eth0", "eth1")) for sw_name in self.controllers]

        sniff(iface=cpu_interfaces, prn=self.recv_msg_cpu)

       

if __name__ == "__main__":

    controller = myController()

    controller.run_cpu_port_loop()

 

[execution]

 

Open another terminal to run the controller

 

h1 ping h2. We can see from the following figure that controller can receive packets from s1 and s3.

 

Dr. Chih-Heng Ke

Department of Computer Science and Information Engineering, National Quemoy University, Kinmen, Taiwan

Email: smallko@gmail.com