# 網路模擬與分析(5/31): P4 ###### tags: `Mininet`、`P4` --- **--------------目前為純 code 及 result,詳解部分之後更新--------------** --- ## Copy-to-CPU (physical forwarding) ![](https://i.imgur.com/rbZGHPr.jpg) `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` ![](https://i.imgur.com/VKdEzhL.png) --- ### 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`: ![](https://i.imgur.com/clANFzv.png) --- ### Copy-to-CPU (Shortest Path by Controller) ![](https://i.imgur.com/QQyI6pQ.png) ![](https://i.imgur.com/cMXM8OP.jpg) `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() ``` ![](https://i.imgur.com/yQcKjLw.png) --- ### Broadcast ![](https://i.imgur.com/CyQ3Cu1.png) `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() ``` ![](https://i.imgur.com/RJsvFlC.png) ![](https://i.imgur.com/f6Fr1jx.png) ![](https://i.imgur.com/68IMBlv.png) ![](https://i.imgur.com/T8sMjpZ.png) ``` 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 ``` ![](https://i.imgur.com/TRTfKap.png) --- ### Send-to-CPU (HomeWork-3) ![](https://i.imgur.com/dlgh8ZG.jpg) `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 ``` ![](https://i.imgur.com/zOkvcxf.png) **進行測試時,h1 ping h2, h2 ping h3, 以及 h3 ping h2 是 ok 的,但是當測試 h1 ping h3 或 h3 ping h1 時,會發生錯誤。** 猜測是最短路徑有3個 CPU Port,因此就會發生報錯了。 因此我就拓樸改成這樣去測試: ![](https://i.imgur.com/LrpIB0j.jpg) ![](https://i.imgur.com/fDZVyd3.png) 結果發現 h1 ping h3 還是不行,因此我認為在`controller.py`那邊程式碼可能有點錯誤,之後我在解決這問題。 --- ## Broadcast & Multicast ![](https://i.imgur.com/LxJTTAZ.jpg) `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)) ``` ![](https://i.imgur.com/Rt8rL6i.png) --- ### Bridge & Multicast (HomeWork-4) ![](https://i.imgur.com/lVVxE3J.png) ![](https://i.imgur.com/Upk1mn9.jpg) `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)) ``` ![](https://i.imgur.com/cj4q51s.png) --- ## 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