# 網路模擬-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**