# 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="/files/-Mb_3daPj71un7bBM_uM" 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 ..
```

![](/files/-Mbm1snqLcpAoO1-QC9U)

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

```
unzip send_to_cpu.zip
```

```
cd ..
```

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

```
unzip send_to_cpu2.zip
```

![](/files/-Mbm2Bz-Ejy5XRiXS3pe)
{% endtab %}
{% endtabs %}

```
cd ..
```

### Copy to CPU

![](/files/-Mb0-53-1mO352YHR0Ha)

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

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

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

![](/files/-Mbm2UuOKtL5Ga11OVlV)

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

```
p4run
```

![](/files/-Mbm54iGLDiT5cfy6Rx2)

```
s1 ifconfig
```

![](/files/-Mbm5KPDLh1tf1Zs_EA-)

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

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

![](/files/-Mbm6a5-nTAoA_zh9Nrg)

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

![](/files/-Mbm6M00gzRW2g2hfvq_)

```
python receive.py
```

![](/files/-MbmCho9veLX8QtEftOy)
{% endtab %}

{% tab title="h2" %}

```
python server.py
```

![](/files/-MbmCaFDVXq2JUPbKJef)
{% endtab %}

{% tab title="h1" %}

```
python client.py
```

![](/files/-MbmCXAF7dG6Hn2p6SOz)
{% 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

![](/files/-Mb08JpL3fwtuK2uU5FU)

{% 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 &
```

![](/files/-MbmFzt0kFbZSkIwVrhc)

```
p4run
```

![](/files/-MbmH_0KWfUn3Fx91TmQ)

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

![](/files/-MbmHs2jVYB332nH-HEJ)
{% 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
```

![](/files/-MbmI13EWR_jRBHP_yN8)
{% 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 &
```

![](/files/-MbmIteLdQX0TQzx3pL-)

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

![](/files/-MbmLlfIvX5FvQMcj_14)

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

```
table_dump ipv4_lpm
```

![](/files/-MbmLutkMSk2Ym5VJE3P)

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

```
table_dump ipv4_lpm
```

![](/files/-MbmM4uoFvDG8NKz0V2B)

```
python controller.py
```

![](/files/-MbmMrHY27aIFl1DLcCE)
{% endtab %}

{% tab title="h1" %}

```
ping 10.0.3.2 -c 5
```

![](/files/-MbmMws_S03q6WSVI9Ks)
{% endtab %}

{% tab title="h2" %}

```
```

{% endtab %}
{% endtabs %}

### Broadcast / Multicast

![](/files/-Mb0WySu6kxulW2xUprh)

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

![](/files/-MbmXdWtN2jqW3ZZ9LHX)
{% endtab %}

{% tab title="h3" %}

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

```
python receive.py
```

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

```
python receive.py
```

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

```
python send.py
```

![](/files/-MbmXYVeOrJg6Or-fTDN)
{% endtab %}

{% tab title="h1" %}

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

```
python send.py
```

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

```
python receive.py
```

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

```
python receive.py
```

![](/files/-MbmXSbH1Lk7va-QcSb_)
{% endtab %}
{% endtabs %}

#### 延伸

![](/files/-Mb0MKN7yPqy_uNxRXnw)

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

## 課堂作業

### Multicast

![](/files/-Mb0X-KlJRQ4peVYhXAI)

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

```
cd p4-test
```

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

```
cd multicast
```

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

![](/files/-MbmbTG1HGjkdmj2tlns)

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

![](/files/-Mbmbvz5Pzq2ogROekKi)

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

![](/files/-MbmePz52zalgaYEVX_Z)

```
python receive.py
```

![](/files/-MbmfgXs9gPsxipruWyV)
{% endtab %}

{% tab title="h3" %}

```
wireshark
```

![](/files/-MbmeXFrIjRzkz2BwH-C)

```
python receive.py
```

![](/files/-MbmflANeyxvFI9dn42L)
{% endtab %}

{% tab title="h2" %}

```
wireshark
```

![](/files/-MbmebVQE8zBGWe2Ie9f)

```
python receive.py
```

![](/files/-MbmfOyGsyOYUYBnXzja)
{% endtab %}

{% tab title="h1" %}

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

```
python send.py
```

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

```
python send.py
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://anida-huang.gitbook.io/notes-network-simulation-and-analysis/qi-mo/20210524-static-routing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
