> For the complete documentation index, see [llms.txt](https://anida-huang.gitbook.io/notes-network-simulation-and-analysis/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://anida-huang.gitbook.io/notes-network-simulation-and-analysis/qi-mo/20210524-static-routing.md).

# 20210531 Programming Protocol-independent Packet Processors ( P4 ) - 4

## 課堂資料

> 範例程式

### Copy to CPU

{% embed url="<https://www.dropbox.com/sh/9qzkarvkwehgn9q/AADeYzt_V0dGA4ZHKYzsj0Isa/p4-utils-example/copy-to-cpu?dl=0&subfolder_nav_tracking=1>" %}

{% embed url="<https://pymotw.com/2/socket/tcp.html>" %}

### Send to CPU

{% embed url="<https://www.dropbox.com/sh/9qzkarvkwehgn9q/AAAdJcjZi8xZikCBoGE_Xgqxa/p4-utils-example/send_to_cpu?dl=0&subfolder_nav_tracking=1>" %}

### Send to CPU2

{% embed url="<https://www.dropbox.com/sh/9qzkarvkwehgn9q/AAC8k-RcjtZ4DnDx3AKtYkz3a/p4-utils-example/send_to_cpu2?dl=0&subfolder_nav_tracking=1>" %}

### 課堂作業附件

<div align="left"><img src="https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbZx1xAecDiHmavviTm%2F-Mb_3daPj71un7bBM_uM%2Fimage.png?alt=media&amp;token=3614f915-6738-4c4d-8c1d-e99f796e2ea8" alt=""></div>

{% embed url="<https://stackoverflow.com/questions/603852/how-do-you-udp-multicast-in-python>" %}

{% embed url="<https://www.dropbox.com/sh/9qzkarvkwehgn9q/AAA3Y9caVnWEYvmJU4F-IbqBa/p4-utils-example/test-multicast?dl=0&subfolder_nav_tracking=1>" %}

## 課堂練習

{% tabs %}
{% tab title="Terminal 01" %}

```
cd Downloads
```

```
mkdir copy-to-cpu send-to-cpu send-to-cpu2
```

```
mv -f copy-to-cpu.zip copy-to-cpu/
```

```
mv -f send_to_cpu.zip send-to-cpu/
```

```
mv -f send_to_cpu2.zip send-to-cpu2/
```

```
cd copy-to-cpu
```

```
unzip copy-to-cpu.zip
```

```
cd ..
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-Mbm1snqLcpAoO1-QC9U%2Fimage.png?alt=media\&token=1fe5b4e0-ddb5-4200-a245-91bfc5d72faf)

```
cd send-to-cpu
```

```
unzip send_to_cpu.zip
```

```
cd ..
```

```
cd send-to-cpu2
```

```
unzip send_to_cpu2.zip
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-Mbm2Bz-Ejy5XRiXS3pe%2Fimage.png?alt=media\&token=84f39e3b-579b-41d3-b4df-8426f12c2c24)
{% endtab %}
{% endtabs %}

```
cd ..
```

### Copy to CPU

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mb-IYTuyztXorfgeThk%2F-Mb0-53-1mO352YHR0Ha%2Fimage.png?alt=media\&token=6f080390-65f0-4bce-9b48-2333362a3ecc)

{% tabs %}
{% tab title="Terminal 01" %}

```
cd copy-to-cpu
```

```
gedit p4app.json basic.p4 receive.py cmd.txt &
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-Mbm2UuOKtL5Ga11OVlV%2Fimage.png?alt=media\&token=5aa16ae8-775e-4024-898c-c1e9e6114302)

```
sed -i '/^$/d' p4app.json
```

```
p4run
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-Mbm54iGLDiT5cfy6Rx2%2Fimage.png?alt=media\&token=62eec57b-3d69-4c13-8943-beb9e9316b3c)

```
s1 ifconfig
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-Mbm5KPDLh1tf1Zs_EA-%2Fimage.png?alt=media\&token=63a26a79-37ad-4053-b110-aa777ab2ce86)

> **監聽後進行`h1 ping h2`**

```
h1 ping h2 -c 5
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-Mbm6a5-nTAoA_zh9Nrg%2Fimage.png?alt=media\&token=c6064737-ba14-4fe9-b2b8-a16f8054f7c5)

```
xterm h2 h1
```

{% endtab %}

{% tab title="gedit" %}
{% hint style="info" %}
**p4app.json**

```
{

  "program": "basic.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,

  "topo_module": {

    "file_path": "",

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

    "object_name": "AppTopoStrategies"

  },

  "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": {
    "assignment_strategy": "l2", 

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

    "hosts": {

      "h1": {

      },

      "h2": {

      }

    },

    "switches": {

      "s1": {

        "cli_input": "cmd.txt",
        "program": "basic.p4",
        "cpu_port": true 

      }

    }

  }

}
```

**basic.p4**

```
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

/*************************************************************************
*********************** H E A D E R S  ***********************************
*************************************************************************/
 
struct metadata {
    /* empty */
}

 

struct headers {
}

 

/*************************************************************************
*********************** 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 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 forward(bit<9> port) {
        standard_metadata.egress_spec = port;
    }

    table phy_forward {
        key = {
            standard_metadata.ingress_port: exact;
        }

        actions = {
            forward;
            drop;
        }
        size = 1024;
        default_action = drop();
    }

    apply {
        phy_forward.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 ){
         clone(CloneType.E2E,100);
        }
    }
}

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

/*************************************************************************
***********************  D E P A R S E R  *******************************
*************************************************************************/

control MyDeparser(packet_out packet, in headers hdr) {
    apply {
    }
}

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

V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()

) main;
```

**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"
    print pkt.summary()

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()
```

**cmd.txt**

```
table_add phy_forward forward 1 => 2
table_add phy_forward forward 2 => 1
mirroring_add 100 3
```

{% endhint %}
{% endtab %}

{% tab title="Terminal 02" %}

```
cd Downloads/copy-to-cpu
```

> **進行監聽**

```
python receive.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-Mbm6M00gzRW2g2hfvq_%2Fimage.png?alt=media\&token=6add7fc5-4d0a-49a6-b1d9-2678cf56f6ad)

```
python receive.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-MbmCho9veLX8QtEftOy%2Fimage.png?alt=media\&token=4cd36605-26de-4b58-944e-979270fd94d0)
{% endtab %}

{% tab title="h2" %}

```
python server.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-MbmCaFDVXq2JUPbKJef%2Fimage.png?alt=media\&token=cbecaf39-540d-4e3c-a8f5-844a64967b9e)
{% endtab %}

{% tab title="h1" %}

```
python client.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm-CVSlxcUl0D5Is83%2F-MbmCXAF7dG6Hn2p6SOz%2Fimage.png?alt=media\&token=e6f7c067-1089-4db2-a73f-362c13a38774)
{% endtab %}

{% tab title="Terminal 03" %}

```
cd Downloads/copy-to-cpu
```

```
gedit client.py server.py
```

```
rm -rf socket.pyc
```

{% endtab %}

{% tab title="gedit 02" %}
{% hint style="info" %}
**server.py**

```
import socket
import sys

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Bind the socket to the port
server_address = ('10.0.0.2', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
sock.bind(server_address)

# Listen for incoming connections
sock.listen(1)

connection, client_address = sock.accept()
print >>sys.stderr, 'connection from', client_address
data = connection.recv(1024)
print >>sys.stderr, 'received "%s"' % data
connection.close()
```

**client.py**

```
import socket
import sys

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect the socket to the port where the server is listening
server_address = ('10.0.0.2', 10000)
print >>sys.stderr, 'connecting to %s port %s' % server_address
sock.connect(server_address)

# Send data
message = 'hello world'
print >>sys.stderr, 'sending "%s"' % message
sock.sendall(message)
sock.close()
```

{% endhint %}
{% endtab %}
{% endtabs %}

```
cd ..
```

### Send to CPU

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mb-IYTuyztXorfgeThk%2F-Mb08JpL3fwtuK2uU5FU%2Fimage.png?alt=media\&token=e93b6345-2714-482a-bf9e-f827709d95d4)

{% tabs %}
{% tab title="Terminal 01" %}

```
cd send-to-cpu
```

```
sed -i '/^$/d' p4app.json
```

```
gedit p4app.json send_to_cpu.p4 s1-commands.txt s2-commands.txt s3-commands.txt controller.py &
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmFzt0kFbZSkIwVrhc%2Fimage.png?alt=media\&token=16ba7a31-f02f-4deb-be93-7a50df40f01e)

```
p4run
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmH_0KWfUn3Fx91TmQ%2Fimage.png?alt=media\&token=18ec2b83-c076-4f0e-9a57-a8cc74823110)

```
h1 ping h2 -c 5
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmHs2jVYB332nH-HEJ%2Fimage.png?alt=media\&token=88c2fdc7-fdae-4eec-bb6f-d85899cd7152)
{% endtab %}

{% tab title="gedit" %}
{% hint style="info" %}
**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**

```
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

//creates a mirroring ID 100 to output port 3
//mirroring_add 100 3
```

**s3-commands.txt**

```
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()
```

{% endhint %}
{% endtab %}

{% tab title="Terminal 02" %}

```
cd send-to-cpu
```

```
python controller.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmI13EWR_jRBHP_yN8%2Fimage.png?alt=media\&token=e28393e1-57c3-4f23-b609-9d78d6432872)
{% endtab %}
{% endtabs %}

```
cd ..
```

### Send to CPU2

{% tabs %}
{% tab title="Terminal 01" %}

```
cd send-to-cpu2
```

```
sed -i '/^$/d' p4app.json
```

```
gedit p4app.json send_to_cpu.p4 s1-commands.txt s2-commands.txt s3-commands.txt controller.py &
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmIteLdQX0TQzx3pL-%2Fimage.png?alt=media\&token=54f355e0-bb91-4d3c-917a-54925b0307bb)

```
p4run
```

```
xterm h1 h2
```

{% endtab %}

{% tab title="gedit" %}
{% hint style="info" %}
**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;

    }

    action to_cpu(egressSpec_t port) {
        standard_metadata.egress_spec = port;
    }

    table ipv4_lpm {
        key = {
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            ipv4_forward;
	    to_cpu;	
            drop;
        }
        size = 1024;
    }

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

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

```
table_set_default ipv4_lpm to_cpu 3
```

**s2-commands.txt**

```
```

**s3-commands.txt**

```
table_set_default ipv4_lpm to_cpu 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
from scapy.all import *

rules=[]

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 "-------------------------------------------------------------------"
	global rules
	print "interface:", pkt.sniffed_on
	print "summary:", pkt.summary()
        if IP in pkt:
          ip_src=pkt[IP].src
          ip_dst=pkt[IP].dst
          print "ip_src:", ip_src, " ip_dst:", ip_dst
	  if (ip_src, ip_dst) not in rules:
            rules.append((ip_src, ip_dst))
            print "rules:", rules
          else:
	    return 
       
        switches = {sw_name:{} for sw_name in self.topo.get_p4switches().keys()}
        #print "switches:", switches
        for sw_name, controller in self.controllers.items():
         for host in self.topo.get_hosts_connected_to(sw_name):
           host_ip_addr = self.topo.get_host_ip(host)
	   if ip_src == host_ip_addr:
             sw_src = sw_name
             
           if ip_dst == host_ip_addr:
	     sw_dst = sw_name  
             sw_port = self.topo.node_to_node_port_num(sw_name, host)
             host_ip = self.topo.get_host_ip(host) + "/32"
             host_mac = self.topo.get_host_mac(host)
             #print host, "(", host_ip, host_mac, ")", "-->", sw_name, "with port:", sw_port
 
             #add rule
             print "table_add at {}:".format(sw_name)
             self.controllers[sw_name].table_add("ipv4_lpm", "ipv4_forward", [str(host_ip)], [str(host_mac), str(sw_port)])

        print "sw_src:", sw_src, "sw_dst:", sw_dst   
        paths = self.topo.get_shortest_paths_between_nodes(sw_src, sw_dst)
        sw_1=sw_src
        for next_hop in paths[0][1:]:
         host_ip = ip_dst + "/32"
         sw_port = self.topo.node_to_node_port_num(sw_1, next_hop)
         dst_sw_mac = self.topo.node_to_node_mac(next_hop, sw_1)
         #add rule
         print "table_add at {}:".format(sw_1)
         self.controllers[sw_1].table_add("ipv4_lpm", "ipv4_forward", [str(host_ip)],
                                                    [str(dst_sw_mac), str(sw_port)])
         sw_1=next_hop

	print "send original packet back from ", pkt.sniffed_on
        sendp(pkt, iface=pkt.sniffed_on, verbose=False)
            

 
      
    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()
```

{% endhint %}
{% endtab %}

{% tab title="Terminal 02" %}

```
cd send-to-cpu2
```

```
simple_switch_CLI --thrift-port 9090
```

```
table_dump ipv4_lpm
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmLlfIvX5FvQMcj_14%2Fimage.png?alt=media\&token=b447c51d-0a03-4ce5-b833-a9a111d703be)

```
simple_switch_CLI --thrift-port 9091
```

```
table_dump ipv4_lpm
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmLutkMSk2Ym5VJE3P%2Fimage.png?alt=media\&token=648e1ae9-e5cb-4f28-8981-362babbd5e57)

```
simple_switch_CLI --thrift-port 9092
```

```
table_dump ipv4_lpm
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmM4uoFvDG8NKz0V2B%2Fimage.png?alt=media\&token=aea1b74a-5de9-4081-9b90-453f33c48ebc)

```
python controller.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmMrHY27aIFl1DLcCE%2Fimage.png?alt=media\&token=71a7daf3-6c87-4789-9743-0a536db29767)
{% endtab %}

{% tab title="h1" %}

```
ping 10.0.3.2 -c 5
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmCqMWjSct2oiWj6ao%2F-MbmMws_S03q6WSVI9Ks%2Fimage.png?alt=media\&token=9b26d41a-8122-4021-85eb-66a03edece99)
{% endtab %}

{% tab title="h2" %}

```
```

{% endtab %}
{% endtabs %}

### Broadcast / Multicast

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mb0MxqYY76gcefXD-9O%2F-Mb0WySu6kxulW2xUprh%2Fimage.png?alt=media\&token=22e02c82-71af-49d4-b6a3-5bf6ed09ca76)

{% tabs %}
{% tab title="Terminal 01" %}

```
cd p4-test/test-broadcast
```

```
p4run
```

```
xterm h1 h2 h3
```

{% endtab %}

{% tab title="h2" %}

> **接收`h1`傳送**

```
python receive.py
```

> **進行傳送，由`h1`、`h3`接收**

```
python send.py
```

> **接收`h3`傳送**

```
python receive.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmUlT-fCGibuniCVlM%2F-MbmXdWtN2jqW3ZZ9LHX%2Fimage.png?alt=media\&token=cd1c6ce8-1723-4af4-83d4-16c4a836b1ef)
{% endtab %}

{% tab title="h3" %}

> **接收`h1`傳送**

```
python receive.py
```

> **接收`h2`傳送**

```
python receive.py
```

> **進行傳送，由`h1`、`h2`接收**

```
python send.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmUlT-fCGibuniCVlM%2F-MbmXYVeOrJg6Or-fTDN%2Fimage.png?alt=media\&token=3fdfcc2e-0d4f-4844-ac1f-aa97a0291a78)
{% endtab %}

{% tab title="h1" %}

> **進行傳送，由`h2`、`h3`接收**

```
python send.py
```

> **接收`h2`傳送**

```
python receive.py
```

> **接收`h3`傳送**

```
python receive.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-MbmUlT-fCGibuniCVlM%2F-MbmXSbH1Lk7va-QcSb_%2Fimage.png?alt=media\&token=264735a1-3f4a-4d6a-a810-091fddfe56dd)
{% endtab %}
{% endtabs %}

#### 延伸

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mb-IYTuyztXorfgeThk%2F-Mb0MKN7yPqy_uNxRXnw%2Fimage.png?alt=media\&token=15303fac-381a-4e5e-9721-eca036d1aa10)

* **`Broadcast`**：當接收封包時，廣播至所有節點
* **`Multicast`**：針對有興趣的節點傳送封包

## 課堂作業

### Multicast

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mb0MxqYY76gcefXD-9O%2F-Mb0X-KlJRQ4peVYhXAI%2Fimage.png?alt=media\&token=b343f677-5d22-4bec-843a-a4cdba51a606)

{% tabs %}
{% tab title="Terminal 01" %}

```
cd p4-test
```

```
cp -r test-broadcast/ multicast
```

```
cd multicast
```

```
gedit basic.p4 p4app.json cmd.txt
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm_dF3hIV1mpaGzhsB%2F-MbmbTG1HGjkdmj2tlns%2Fimage.png?alt=media\&token=1cf248df-4125-4862-ad72-a0737df85b30)

```
gedit send.py receive.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm_dF3hIV1mpaGzhsB%2F-Mbmbvz5Pzq2ogROekKi%2Fimage.png?alt=media\&token=1dfaacfa-951f-49d7-9977-e6ad1803b3b5)

```
p4run
```

```
xterm h1 h2 h3 h4
```

{% endtab %}

{% tab title="gedit" %}
{% hint style="info" %}
**p4app.json**

```
{
  "program": "basic.p4",
  "switch": "simple_switch",
  "compiler": "p4c",
  "options": "--target bmv2 --arch v1model --std p4-16",
  "switch_cli": "simple_switch_CLI",
  "cli": true,
  "pcap_dump": false,
  "enable_log": true,
  "topo_module": {
    "file_path": "",
    "module_name": "p4utils.mininetlib.apptopo",
    "object_name": "AppTopoStrategies"
  },
  "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"], ["h2", "s1"], ["h3", "s1"], ["h4", "s1"]],
    "hosts": {
      "h1": {
      },
      "h2": {
      },
      "h3": {
      },
      "h4": {
      }
    },
    "switches": {
      "s1": {
        "cli_input": "cmd.txt",
        "program": "basic.p4"
      }
    }
  }
}
```

**basic.p4**

```
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

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

header ethernet_t {
    bit<48> dstAddr;
    bit<48> 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;
    bit<32> srcAddr;
    bit<32> dstAddr;
}
 
struct metadata {
    /* empty */
}

 

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 parse_ethernet {
        packet.extract(hdr.ethernet);
        transition select(hdr.ethernet.etherType) {
            16w0x800: parse_ipv4;
            default: accept;
        }
    }
    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition accept;
    }
    state start {
        transition parse_ethernet;
    }
}

 

/*************************************************************************
************   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 forward(bit<9> port) {
        standard_metadata.egress_spec = port;
    }

    table mac_forward {
        key = {
            hdr.ethernet.dstAddr: exact;
        }

        actions = {
            forward;
            drop;
        }
        size = 1024;
        default_action = drop();
    }

    action broadcast(bit<16> mcast_grp_id) {
        standard_metadata.mcast_grp = mcast_grp_id;
    }

    table ip_broadcast {
        key = {
            hdr.ipv4.dstAddr: exact;
            standard_metadata.ingress_port: exact;
        }

        actions = {
            broadcast;
            NoAction;
        }
        size = 1024;
        default_action = NoAction();
    }

    apply {
        if (!ip_broadcast.apply().hit){
          mac_forward.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 {
    }
}

/*************************************************************************
***********************  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);
    }
}

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

V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()

) main;

```

**cmd.txt**

```
table_add mac_forward forward 00:00:0a:00:01:01 => 1
table_add mac_forward forward 00:00:0a:00:01:02 => 2
table_add mac_forward forward 00:00:0a:00:01:03 => 3
table_add ip_broadcast broadcast 224.0.0.10 1 => 1 
mc_mgrp_create 1
mc_node_create 0 2 4
mc_node_associate 1 0
```

**send.py**

```
#!/usr/bin/env python

from socket import *

s = socket(AF_INET, SOCK_DGRAM)
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
s.sendto('hello everyone', ('224.0.0.10', 1234))
```

**receive.py**

```
import socket
import struct

MCAST_GRP = '224.0.0.10'
MCAST_PORT = 1234
IS_ALL_GROUPS = True

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
    # on this port, receives ALL multicast groups
    sock.bind(('', MCAST_PORT))
else:
    # on this port, listen ONLY to MCAST_GRP
    sock.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)

sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

while True:
  # For Python 3, change next line to "print(sock.recv(10240))"
  print sock.recv(10240)
```

{% endhint %}
{% endtab %}

{% tab title="h4" %}

```
wireshark
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm_dF3hIV1mpaGzhsB%2F-MbmePz52zalgaYEVX_Z%2Fimage.png?alt=media\&token=60f67f29-69af-417c-9c68-e90b3b05a609)

```
python receive.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm_dF3hIV1mpaGzhsB%2F-MbmfgXs9gPsxipruWyV%2Fimage.png?alt=media\&token=bf8a6925-8702-468d-8957-b28e2c0c0cdf)
{% endtab %}

{% tab title="h3" %}

```
wireshark
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm_dF3hIV1mpaGzhsB%2F-MbmeXFrIjRzkz2BwH-C%2Fimage.png?alt=media\&token=cfe824cc-1e29-41e3-8cb9-14daf09c674f)

```
python receive.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm_dF3hIV1mpaGzhsB%2F-MbmflANeyxvFI9dn42L%2Fimage.png?alt=media\&token=fcc5775f-d650-4fb2-9745-ef01d80d83bc)
{% endtab %}

{% tab title="h2" %}

```
wireshark
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm_dF3hIV1mpaGzhsB%2F-MbmebVQE8zBGWe2Ie9f%2Fimage.png?alt=media\&token=3e9168d8-b6a3-46e3-b2b6-111cee26be75)

```
python receive.py
```

![](https://1624492921-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MU7EdDCmxVnXY6VZf4s%2F-Mbm_dF3hIV1mpaGzhsB%2F-MbmfOyGsyOYUYBnXzja%2Fimage.png?alt=media\&token=eb4513d9-3b77-4349-a82a-33a2e072eb6a)
{% endtab %}

{% tab title="h1" %}

> **進行傳送，由`h2`、`h3`、`h4`進行`wireshark`監測**

```
python send.py
```

> **再次傳送，由`h2`、`h3`、`h4`進行監測**

```
python send.py
```

{% endtab %}
{% endtabs %}
