# 網路模擬與分析(5/31): P4
###### tags: `Mininet`、`P4`
---
**--------------目前為純 code 及 result,詳解部分之後更新--------------**
---
## Copy-to-CPU (physical forwarding)

`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;
```
`cmd.txt`:
```
table_add phy_forward forward 1 => 2
table_add phy_forward forward 2 => 1
mirroring_add 100 3
```
`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()
```
`p4run`

---
### TCP Socket
`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()
```
`p4run`:

---
### Copy-to-CPU (Shortest Path by Controller)


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

---
### Broadcast

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




```
table_add ipv4_lpm set_nhop 10.0.1.1/32 => 00:00:0a:00:01:01 1
table_add ipv4_lpm set_nhop 10.0.2.1/32 => 00:00:0a:00:02:01 2
table_add ipv4_lpm set_nhop 10.0.3.1/32 => 00:00:0a:00:03:01 3
mirroring_add 250 3
```

---
### Send-to-CPU (HomeWork-3)

`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"], ["s6","h3"], ["s1","s2"], ["s2","s3"], ["s3","s6"], ["s2","s4"], ["s4","s5"], ["s5","s6"]],
"hosts": {
"h1": {
},
"h2": {
},
"h3": {
}
},
"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
},
"s4": {
"cli_input": "s4-commands.txt",
"program": "send_to_cpu.p4",
"cpu_port": true
},
"s5": {
"cli_input": "s5-commands.txt",
"program": "send_to_cpu.p4",
"cpu_port": true
},
"s6": {
"cli_input": "s6-commands.txt",
"program": "send_to_cpu.p4",
"cpu_port": true
}
}
}
}
```
`s1-commands.txt`:
```
table_set_default ipv4_lpm to_cpu 3
```
`s3-commands.txt`:
```
table_set_default ipv4_lpm to_cpu 4
```
`s6-commands.txt`
```
table_set_default ipv4_lpm to_cpu 4
```

**進行測試時,h1 ping h2, h2 ping h3, 以及 h3 ping h2 是 ok 的,但是當測試 h1 ping h3 或 h3 ping h1 時,會發生錯誤。**
猜測是最短路徑有3個 CPU Port,因此就會發生報錯了。
因此我就拓樸改成這樣去測試:


結果發現 h1 ping h3 還是不行,因此我認為在`controller.py`那邊程式碼可能有點錯誤,之後我在解決這問題。
---
## Broadcast & Multicast

`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"]],
"hosts": {
"h1": {
},
"h2": {
},
"h3": {
}
},
"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 255.255.255.255 1 => 1
table_add ip_broadcast broadcast 255.255.255.255 2 => 2
table_add ip_broadcast broadcast 255.255.255.255 3 => 3
mc_mgrp_create 1
mc_node_create 0 2 3
mc_node_associate 1 0
mc_mgrp_create 2
mc_node_create 1 1 3
mc_node_associate 2 1
mc_mgrp_create 3
mc_node_create 2 1 2
mc_node_associate 3 2
```
`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', ('255.255.255.255', 1234))
```
`receive.py`
```
#!/usr/bin/env python
import sys
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.bind(('', 1234))
sys.stderr.write("received '%s' from %s\n" % s.recvfrom(1024))
```

---
### Bridge & Multicast (HomeWork-4)


`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": false,
"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 multicast(bit<16> mcast_grp_id) {
standard_metadata.mcast_grp = mcast_grp_id;
}
table ip_multicast {
key = {
hdr.ipv4.dstAddr: exact;
standard_metadata.ingress_port: exact;
}
actions = {
multicast;
NoAction;
}
size = 1024;
default_action = NoAction();
}
apply {
if (!ip_multicast.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 mac_forward forward 00:00:0a:00:01:04 => 4
table_add ip_broadcast broadcast 244.0.0.10 1 => 1
table_add ip_broadcast broadcast 244.0.0.20 2 => 2
table_add ip_broadcast broadcast 244.0.0.30 3 => 3
table_add ip_broadcast broadcast 244.0.0.40 4 => 4
mc_mgrp_create 1
mc_node_create 0 2 4
mc_node_associate 1 0
mc_mgrp_create 2
mc_node_create 1 1 3 4
mc_node_associate 2 1
mc_mgrp_create 3
mc_node_create 2 1 4
mc_node_associate 3 2
mc_mgrp_create 4
mc_node_create 2
mc_node_associate 4 3
```
`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', ('255.255.255.255', 1234))
```
`receive.py`:
```
#!/usr/bin/env python
import sys
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.bind(('', 1234))
sys.stderr.write("received '%s' from %s\n" % s.recvfrom(1024))
```

---
## Reference
1. https://www.dropbox.com/sh/9qzkarvkwehgn9q/AADeYzt_V0dGA4ZHKYzsj0Isa/p4-utils-example/copy-to-cpu?dl=0&subfolder_nav_tracking=1
2. https://pymotw.com/2/socket/tcp.html
3. https://www.dropbox.com/sh/9qzkarvkwehgn9q/AAAdJcjZi8xZikCBoGE_Xgqxa/p4-utils-example/send_to_cpu?dl=0&subfolder_nav_tracking=1
4. https://www.dropbox.com/sh/9qzkarvkwehgn9q/AAC8k-RcjtZ4DnDx3AKtYkz3a/p4-utils-example/send_to_cpu2?dl=0&subfolder_nav_tracking=1
5. https://stackoverflow.com/questions/603852/how-do-you-udp-multicast-in-python
6. https://www.dropbox.com/sh/9qzkarvkwehgn9q/AAA3Y9caVnWEYvmJU4F-IbqBa/p4-utils-example/test-multicast?dl=0&subfolder_nav_tracking=1