diff --git a/ip-routing-pro/Readme.md b/ip-routing-pro/Readme.md new file mode 100644 index 0000000..efc6157 --- /dev/null +++ b/ip-routing-pro/Readme.md @@ -0,0 +1,70 @@ +### Introduction ### + +This demo shows how to implement a basic IP routers with static routing entries using P4, BMv2 and Mininet. + +The basic functionality of IP router is: + +- determine the output port for packet based on destination IP (Longest Prefix Match) +- decrement TTL value for IP protocol +- update destination MAC address based on next-hop IP address +- update source MAC address according to output port + +We have implemented the functionality of IP router as P4 program (router.p4). The program design is as follows: + +- We have used V1Model of P4_16 composed of Ingress and Egress control pipelines +- For Ingress control pipeline there is one table defined: + - **routing_table** - it determines the output port based on IPv4 LPM. When packet is matched the *ipv4_forward* action is invoked. It sets next-hop IP address in the routing_metadata, decrements IPv4 TTL and sets output port. +- For Egress control pipeline we have defined two tables: + - **switching_table** - it is responsible for setting destination MAC based on next-hop IP address, which is retrieved from routing metadata. + - **mac_rewriting_table** - it updates source MAC address according to the output port for the packet. + +The router.p4 program defines the data plane of IP routers. Note that the control plane in this demo is realized by populating static rules. + +The structure of the test network is shown below. Each network device has been configured with the **router.p4** program. + +

+ +

+ +

+ +

+ +The choice of this topology results from the following **paper[1]** + +``` +. +├── basic.p4 # for p4 switch +├── network.py # Try with "sudo python3 FatTree.py" boost mininet with CLI +├── r1-commands.txt # commands file for router 1 +├── r2-commands.txt # commands file for router 2 +├── Readme.md # YOU ARE HERE! +├── router.p4 # for p4 router +├── s1-commands.txt # commands file for switch &1.2.3.4 +├── s2-commands.txt +├── s3-commands.txt +└── s4-commands.txt +``` + +### Demo ### + +1. First of all you need to setup the environment on your Linux machine. +2. Enter the ip-routing/ directory. + +`cd ip-routing/` + +3. Run the Mininet topology. + +`sudo python3 network.py` + +4. In the Mininet console, check if ping between h1 and h2 works + +`h1 ping h2` +or `h1 ping h6` etc. + +5. Have a nice day! + + +## Reference + +**[1]** Anagnostakis K G, Greenwald M B, Ryger R S. On the sensitivity of network simulation to topology[C]//Proceedings. 10th IEEE International Symposium on Modeling, Analysis and Simulation of Computer and Telecommunications Systems. IEEE, 2002: 117-126. \ No newline at end of file diff --git a/ip-routing-pro/basic.p4 b/ip-routing-pro/basic.p4 new file mode 100644 index 0000000..e7b502b --- /dev/null +++ b/ip-routing-pro/basic.p4 @@ -0,0 +1,196 @@ +/* -*- P4_16 -*- */ +#include +#include + +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> diffserv; + 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 { + /* 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 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 + 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 { } +} + +/************************************************************************* +************* 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.diffserv, + 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; \ No newline at end of file diff --git a/ip-routing-pro/images/Networks.png b/ip-routing-pro/images/Networks.png new file mode 100644 index 0000000..f11f916 Binary files /dev/null and b/ip-routing-pro/images/Networks.png differ diff --git a/ip-routing-pro/images/NodeIpAndMACInfo.png b/ip-routing-pro/images/NodeIpAndMACInfo.png new file mode 100644 index 0000000..fa2a0eb Binary files /dev/null and b/ip-routing-pro/images/NodeIpAndMACInfo.png differ diff --git a/ip-routing-pro/network.py b/ip-routing-pro/network.py new file mode 100644 index 0000000..06fc675 --- /dev/null +++ b/ip-routing-pro/network.py @@ -0,0 +1,129 @@ +from p4utils.mininetlib.network_API import NetworkAPI + +net = NetworkAPI() + +net.setLogLevel('info') + +#Switches +net.addP4Switch('s1', cli_input='s1-commands.txt') +net.setThriftPort('s1',9081) +net.addP4Switch('s2', cli_input='s2-commands.txt') +net.setThriftPort('s2',9082) +net.addP4Switch('s3', cli_input='s3-commands.txt') +net.setThriftPort('s3',9083) +net.addP4Switch('s4', cli_input='s4-commands.txt') +net.setThriftPort('s4',9084) + +# Routers +# There is no p4router API in p4utils.mininetlib.network_API, +# use a specific p4 script that implements the router functionality. +net.addP4Switch('r1', cli_input='r1-commands.txt') +net.setThriftPort('s4',9091) +net.addP4Switch('r2', cli_input='r2-commands.txt') +net.setThriftPort('s4',9092) + +# P4 files +net.setP4Source('s1','basic.p4') +net.setP4Source('s2','basic.p4') +net.setP4Source('s3','basic.p4') +net.setP4Source('s4','basic.p4') + +net.setP4Source('r1','router.p4') +net.setP4Source('r2','router.p4') + +#Hosts +net.addHost('h1') +net.addHost('h2') +net.addHost('h3') +net.addHost('h4') +net.addHost('h5') +net.addHost('h6') + +#Links +net.addLink('h1','s1', port1=1, port2=2, addr1="00:00:0a:00:01:01", addr2="00:00:00:00:01:02") +net.addLink('h2','s1', port1=1, port2=3, addr1="00:00:0a:00:01:02", addr2="00:00:00:00:01:03") +net.addLink('h3','s2', port1=1, port2=2, addr1="00:00:0a:00:02:01", addr2="00:00:00:00:02:02") + +net.addLink('s1','r1', port1=1, port2=2, addr1="00:00:00:00:01:01", addr2="00:00:00:00:05:02") +net.addLink('s2','r1', port1=1, port2=3, addr1="00:00:00:00:02:01", addr2="00:00:00:00:05:03") +net.addLink('r2','r1', port1=1, port2=1, addr1="00:00:00:00:06:01", addr2="00:00:00:00:05:01") +net.addLink('s3','r2', port1=1, port2=2, addr1="00:00:00:00:03:01", addr2="00:00:00:00:06:02") +net.addLink('s4','r2', port1=1, port2=3, addr1="00:00:00:00:04:01", addr2="00:00:00:00:06:03") + +net.addLink('h4','s3', port1=1, port2=2, addr1="00:00:0a:00:03:01", addr2="00:00:00:00:03:02") +net.addLink('h5','s3', port1=1, port2=3, addr1="00:00:0a:00:03:02", addr2="00:00:00:00:03:03") +net.addLink('h6','s4', port1=1, port2=2, addr1="00:00:0a:00:04:01", addr2="00:00:00:00:04:02") + +# Links parameters +net.setBw('s1','h1', 20) # Mbps +net.setDelay('s1','h1', 10) # ms +net.setBw('s1','h2', 20) +net.setDelay('s1','h2', 3) +net.setBw('s2','h3', 20) +net.setDelay('s2','h3', 10) + +net.setBw('s1','r1', 10) +net.setDelay('s1','r1', 3) +net.setBw('s2','r1', 10) +net.setDelay('s2','r1', 3) + +net.setBw('r1','r2', 1000) +net.setDelay('r1','r2', 5) + +net.setBw('r2','s3', 10) +net.setDelay('r2','s3', 3) +net.setBw('r2','s4', 20) +net.setDelay('r2','s4', 10) + +net.setBw('s3','h4', 20) +net.setDelay('s3','h4', 3) +net.setBw('s3','h5', 20) +net.setDelay('s3','h5', 3) +net.setBw('s4','h6', 20) +net.setDelay('s4','h6', 3) + +# IPs for hosts +net.setIntfIp('h1','s1','10.0.1.10/24') +net.setIntfIp('h2','s1','10.0.1.20/24') +net.setIntfIp('h3','s2','10.0.2.10/24') +net.setIntfIp('h4','s3','10.0.3.10/24') +net.setIntfIp('h5','s3','10.0.3.20/24') +net.setIntfIp('h6','s4','10.0.4.10/24') +# IPs for switch +net.setIntfIp('s1','h1','10.0.1.2/24') # port 2 = s1-eth2 +net.setIntfIp('s1','h1','10.0.1.3/24') # port 3 = s1-eth3 +net.setIntfIp('s1','r1','10.0.1.1/24') # port 1 = s1-eth1 + +net.setIntfIp('s2','h3','10.0.2.2/24') +net.setIntfIp('s2','r1','10.0.2.1/24') + +net.setIntfIp('s3','h4','10.0.3.2/24') +net.setIntfIp('s3','h5','10.0.3.3/24') +net.setIntfIp('s3','r2','10.0.3.1/24') + +net.setIntfIp('s4','h6','10.0.4.2/24') +net.setIntfIp('s4','r2','10.0.4.1/24') +# IPs for routers +net.setIntfIp('r1','s1','10.0.1.5/24') +net.setIntfIp('r1','s2','10.0.2.5/24') +net.setIntfIp('r2','s3','10.0.3.5/24') +net.setIntfIp('r2','s4','10.0.4.5/24') +net.setIntfIp('r1','r2','192.168.1.1/24') +net.setIntfIp('r2','r1','192.168.1.2/24') + + +net.setDefaultRoute('h1', '10.0.1.5') +net.setDefaultRoute('h2', '10.0.1.5') +net.setDefaultRoute('h3', '10.0.2.5') +net.setDefaultRoute('h4', '10.0.3.5') +net.setDefaultRoute('h5', '10.0.3.5') +net.setDefaultRoute('h6', '10.0.4.5') + +#net.mixed() +net.enablePcapDumpAll() +#net.enableLogAll() + +net.enableCli() + +#net.addTaskFile('tasks.txt') +net.startNetwork() \ No newline at end of file diff --git a/ip-routing-pro/r1-commands.txt b/ip-routing-pro/r1-commands.txt new file mode 100644 index 0000000..de7c52e --- /dev/null +++ b/ip-routing-pro/r1-commands.txt @@ -0,0 +1,12 @@ +table_add routing_table ipv4_forward 10.0.1.0/24 => 10.0.1.1 2 +table_add routing_table ipv4_forward 10.0.2.0/24 => 10.0.2.1 3 +table_add routing_table ipv4_forward 10.0.3.0/24 => 192.168.1.2 1 +table_add routing_table ipv4_forward 10.0.4.0/24 => 192.168.1.2 1 + +table_add switching_table set_dmac 10.0.1.1 => 00:00:00:00:01:01 +table_add switching_table set_dmac 10.0.2.1 => 00:00:00:00:02:01 +table_add switching_table set_dmac 192.168.1.2 => 00:00:00:00:06:01 + +table_add mac_rewriting_table set_smac 1 => 00:00:00:00:05:01 +table_add mac_rewriting_table set_smac 2 => 00:00:00:00:05:02 +table_add mac_rewriting_table set_smac 3 => 00:00:00:00:05:03 \ No newline at end of file diff --git a/ip-routing-pro/r2-commands.txt b/ip-routing-pro/r2-commands.txt new file mode 100644 index 0000000..9058e09 --- /dev/null +++ b/ip-routing-pro/r2-commands.txt @@ -0,0 +1,12 @@ +table_add routing_table ipv4_forward 10.0.3.0/24 => 10.0.3.1 2 +table_add routing_table ipv4_forward 10.0.4.0/24 => 10.0.4.1 3 +table_add routing_table ipv4_forward 10.0.1.0/24 => 192.168.1.1 1 +table_add routing_table ipv4_forward 10.0.2.0/24 => 192.168.1.1 1 + +table_add switching_table set_dmac 10.0.3.1 => 00:00:00:00:03:01 +table_add switching_table set_dmac 10.0.4.1 => 00:00:00:00:04:01 +table_add switching_table set_dmac 192.168.1.1 => 00:00:00:00:05:01 + +table_add mac_rewriting_table set_smac 1 => 00:00:00:00:06:01 +table_add mac_rewriting_table set_smac 2 => 00:00:00:00:06:02 +table_add mac_rewriting_table set_smac 3 => 00:00:00:00:06:03 \ No newline at end of file diff --git a/ip-routing-pro/router.p4 b/ip-routing-pro/router.p4 new file mode 100644 index 0000000..6cedcdc --- /dev/null +++ b/ip-routing-pro/router.p4 @@ -0,0 +1,193 @@ +/* -*- P4_16 -*- */ +#include +#include + +const bit<16> TYPE_IPV4 = 0x0800; + +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> diffserv; + 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 headers { + ethernet_t ethernet; + ipv4_t ipv4; +} + +struct routing_metadata_t { + ip4Addr_t nhop_ipv4; +} + +struct metadata { + routing_metadata_t routing; +} + +parser RouterParser(packet_in packet, + out headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + + state start { + transition parse_ethernet; + } + + state parse_ethernet { + packet.extract(hdr.ethernet); + transition select(hdr.ethernet.etherType) { + TYPE_IPV4: parse_ipv4; + default: accept; + } + } + + state parse_ipv4 { + packet.extract(hdr.ipv4); + transition accept; + } + +} + + + +control ingress(inout headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + + action drop() { + mark_to_drop(standard_metadata); + } + + action ipv4_forward(ip4Addr_t nextHop, egressSpec_t port) { + meta.routing.nhop_ipv4 = nextHop; + standard_metadata.egress_spec = port; + hdr.ipv4.ttl = hdr.ipv4.ttl - 1; + } + + table routing_table { + key = { + hdr.ipv4.dstAddr: lpm; + } + actions = { + ipv4_forward; + drop; + NoAction; + } + default_action = NoAction(); + } + + apply { + routing_table.apply(); + } + +} + +control egress(inout headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + + action drop() { + mark_to_drop(standard_metadata); + } + + action set_dmac(macAddr_t dstAddr) { + hdr.ethernet.dstAddr = dstAddr; + } + + action set_smac(macAddr_t mac) { + hdr.ethernet.srcAddr = mac; + } + + table switching_table { + key = { + meta.routing.nhop_ipv4 : exact; + } + actions = { + set_dmac; + drop; + NoAction; + } + default_action = NoAction(); + } + + table mac_rewriting_table { + + key = { + standard_metadata.egress_port: exact; + } + + actions = { + set_smac; + drop; + NoAction; + } + default_action = NoAction(); + } + + apply { + switching_table.apply(); + mac_rewriting_table.apply(); + } + +} + +control deparser(packet_out packet, + in headers hdr) { + + apply { + packet.emit(hdr.ethernet); + packet.emit(hdr.ipv4); + } +} + +control MyComputeChecksum(inout headers hdr, inout metadata meta) { + apply { + update_checksum( + hdr.ipv4.isValid(), + { hdr.ipv4.version, + hdr.ipv4.ihl, + hdr.ipv4.diffserv, + 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); + } +} + +control MyVerifyChecksum(inout headers hdr, inout metadata meta) { + apply { } +} + + +V1Switch( +RouterParser(), +MyVerifyChecksum(), +ingress(), +egress(), +MyComputeChecksum(), +deparser() +) main; \ No newline at end of file diff --git a/ip-routing-pro/s1-commands.txt b/ip-routing-pro/s1-commands.txt new file mode 100644 index 0000000..2f3dd20 --- /dev/null +++ b/ip-routing-pro/s1-commands.txt @@ -0,0 +1,4 @@ +table_set_default ipv4_lpm drop +table_add ipv4_lpm ipv4_forward 10.0.1.10/32 => 00:00:0a:00:01:01 2 +table_add ipv4_lpm ipv4_forward 10.0.1.20/32 => 00:00:0a:00:01:02 3 +table_add ipv4_lpm ipv4_forward 10.0.0.0/16 => 00:00:00:00:05:02 1 \ No newline at end of file diff --git a/ip-routing-pro/s2-commands.txt b/ip-routing-pro/s2-commands.txt new file mode 100644 index 0000000..8abd924 --- /dev/null +++ b/ip-routing-pro/s2-commands.txt @@ -0,0 +1,3 @@ +table_set_default ipv4_lpm drop +table_add ipv4_lpm ipv4_forward 10.0.2.10/32 => 00:00:0a:00:02:01 2 +table_add ipv4_lpm ipv4_forward 10.0.0.0/16 => 00:00:00:00:05:03 1 \ No newline at end of file diff --git a/ip-routing-pro/s3-commands.txt b/ip-routing-pro/s3-commands.txt new file mode 100644 index 0000000..cc6bb19 --- /dev/null +++ b/ip-routing-pro/s3-commands.txt @@ -0,0 +1,4 @@ +table_set_default ipv4_lpm drop +table_add ipv4_lpm ipv4_forward 10.0.3.10/32 => 00:00:0a:00:03:01 2 +table_add ipv4_lpm ipv4_forward 10.0.3.20/32 => 00:00:0a:00:03:02 3 +table_add ipv4_lpm ipv4_forward 10.0.0.0/16 => 00:00:00:00:06:02 1 \ No newline at end of file diff --git a/ip-routing-pro/s4-commands.txt b/ip-routing-pro/s4-commands.txt new file mode 100644 index 0000000..df08031 --- /dev/null +++ b/ip-routing-pro/s4-commands.txt @@ -0,0 +1,3 @@ +table_set_default ipv4_lpm drop +table_add ipv4_lpm ipv4_forward 10.0.4.10/32 => 00:00:0a:00:04:01 2 +table_add ipv4_lpm ipv4_forward 10.0.0.0/16 => 00:00:00:00:06:03 1 \ No newline at end of file diff --git a/ip-routing/topo.py b/ip-routing/topo.py index 6fd0f5f..b0f770b 100644 --- a/ip-routing/topo.py +++ b/ip-routing/topo.py @@ -96,7 +96,7 @@ def main(): h3 = net.get('h3') h3.setDefaultRoute("dev eth0 via 10.0.30.1") - print "Ready !" + print ("Ready !") CLI(net) diff --git a/mn/p4_mininet.py b/mn/p4_mininet.py index f96a9f0..46c123a 100644 --- a/mn/p4_mininet.py +++ b/mn/p4_mininet.py @@ -26,14 +26,14 @@ def config(self, **params): return r def describe(self): - print "**********" - print self.name - print "default interface: %s\t%s\t%s" %( + print ("**********") + print (self.name) + print ("default interface: %s\t%s\t%s" %( self.defaultIntf().name, self.defaultIntf().IP(), self.defaultIntf().MAC() - ) - print "**********" + )) + print ("**********") class P4Switch(Switch): """P4 virtual switch"""