---
# System prepended metadata

title: 網路模擬-copy_to_cpu
tags: [P4, Mininet]

---

# 網路模擬-copy_to_cpu

###### tags: `P4`、`Mininet`
-------

Question
===
- In this environment, we need to prepare 2 hosts、1 switch. you can check [nsg-ethz](https://github.com/nsg-ethz/p4-learning/tree/master/examples/copy_to_cpu) to build this environment.

Environment
===

![](https://i.imgur.com/GsBX1VM.png)

Codes
===

> receive.py ↓
```
#!/usr/bin/env python
import sys
import struct
import os

from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr, bind_layers
from scapy.all import Packet, IPOption, Ether
from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField
from scapy.all import IP, UDP, Raw, ls
from scapy.layers.inet import _IPOption_HDR

class CpuHeader(Packet):
    name = 'CpuPacket'
    fields_desc = [BitField("device_id",0,16), BitField('reason',0,16), BitField('counter', 0, 80)]

bind_layers(CpuHeader, Ether)

def handle_pkt(pkt):
    print "Controller got a packet"

    p = CpuHeader(str(pkt))
    if p.reason == 200:
        p.show()

    sys.stdout.flush()

def main():
    if len(sys.argv) < 2:
        iface = 's1-cpu-eth1'
    else:
        iface = sys.argv[1]

    print "sniffing on %s" % iface
    sys.stdout.flush()
    sniff(iface = iface,
          prn = lambda x: handle_pkt(x))

if __name__ == '__main__':
    main()
```

> p4app.json ↓
```
{
  "program": "copy_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"], ["s1", "h2"]],
    "hosts": {
      "h1": {
      },
      "h2": {
      }
    },
    "switches": {
      "s1": {
        "cli_input": "s1-commands.txt",
        "program": "copy_to_cpu.p4",
        "cpu_port": true
      }
    }
  }

}
```

> basic.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;
}

header cpu_t {
   bit<16> device_id;
   bit<16> reason;
   bit<80> counter;
}

struct metadata {
    /* empty */
}

struct headers {
    ethernet_t   ethernet;
    ipv4_t       ipv4;
    cpu_t        cpu;
}

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


    register<bit<80>>(1) cpu_counter;

    apply {


     //non clonned packets have an instance_type of 0, so then we clone it.
     // using the mirror ID = 100. That in combination with the control plane, will
     //select to which port the packet has to be cloned to.
     if (standard_metadata.instance_type == 0 && hdr.ipv4.tos == 1){
         clone(CloneType.E2E,100);
         hdr.ipv4.tos = 100;
     }

     //sets the tos field of the cloned packet equal to instance_type
     if (standard_metadata.instance_type != 0){
        hdr.ipv4.tos = 0;
        hdr.cpu.setValid();
        hdr.cpu.device_id = 1;
        hdr.cpu.reason = 200;
        cpu_counter.read(hdr.cpu.counter, (bit<32>)0);
        hdr.cpu.counter = hdr.cpu.counter +1;
        cpu_counter.write((bit<32>)0, hdr.cpu.counter);
        truncate((bit<32>)14);
     }
    }
}

/*************************************************************************
*************   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.cpu);
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);

    }
}

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

//switch architecture
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
```

> cmd.txt ↓
```
table_add phy_forward forward 1 => 2

table_add phy_forward forward 2 => 1

mirroring_add 100 3
```

Test
===
> in this test, we simulation 2 different of receive.py.
> 

> 2 different of pkt.show ↓
![](https://i.imgur.com/jWmHXnH.jpg)

> python receive.py ↓
![](https://i.imgur.com/D35Ebfu.jpg)

> h1 ping h2 ↓
![](https://i.imgur.com/9snIOKz.jpg)

> pkt.show result ↓
![](https://i.imgur.com/1BDOV1D.jpg)

> 2 different of pkt.summary ↓
![](https://i.imgur.com/44HjLZD.jpg)

> python receive.py ↓
![](https://i.imgur.com/D35Ebfu.jpg)

> h1 ping h2 ↓
![](https://i.imgur.com/9snIOKz.jpg)

> pkt.summary result ↓
![](https://i.imgur.com/ZE1dNEE.jpg)


fin.


Reference
===
 1.copy-to-cpu,source:https://github.com/nsg-ethz/p4-learning/tree/master/examples/copy_to_cpu
 
 2.Internet Control Message Protocol (ICMP) Parameters, source:https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
 
---
###### 2020-05-25
###### written by **yi-hao tu**