diff --git a/SIGCOMM_2015/source_routing/cleanup b/SIGCOMM_2015/source_routing/cleanup index 4035587..a155edf 100755 --- a/SIGCOMM_2015/source_routing/cleanup +++ b/SIGCOMM_2015/source_routing/cleanup @@ -1,2 +1 @@ -sudo killall simple_switch -redis-cli FLUSHDB \ No newline at end of file +sudo killall lt-simple_switch diff --git a/examples/README.md b/examples/README.md index dfe3a08..660d2bb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,6 +15,7 @@ included: - `counter`: how to use counters in P4 - `action_profile`: how to use action profiles in P4, using ECMP as support - `resubmit`: how to resubmit packets to the ingress pipelines +- `simple_nat`: a basic implementation of a full-cone NAT for TCP traffic All examples are orgranized the same way, with a `p4src` directory containing the P4 source code, and a `README` file describing the P4 program and explaining diff --git a/examples/simple_nat/README.md b/examples/simple_nat/README.md new file mode 100644 index 0000000..9abbfac --- /dev/null +++ b/examples/simple_nat/README.md @@ -0,0 +1,62 @@ +# Simple NAT + +## Description + +This program implements a very basic full-cone NAT for TCP traffic (over +IPv4). According to [Wikipedia] +(https://en.wikipedia.org/wiki/Network_address_translation#Methods_of_translation), +a full-cone NAT is defined as follows: + +Once an internal address (iAddr:iPort) is mapped to an external address +(eAddr:ePort), any packets from iAddr:iPort are sent through eAddr:ePort. Any +external host can send packets to iAddr:iPort by sending packets to eAddr:ePort. + +Note that this program was built on top of the simple_router P4 program, so you +will find some similarities. + +This program was added to illustrate two things: +- how to re-compute a TCP checksum after having modified the TCP header (which +is required for a TCP NAT) +- how to write a simple app that receives and emits "CPU packets" + +This program is slightly more advanced than most others in this repository, so +we recommend that you become familiar with the copy_to_cpu example before +studying this one. + +The program implements very basic funtionality only. For example it supports +only one internal interface, even though we may extend this in the future. The +Mininet topology used for the demo is the following: + +![Simple NAT topology](resources/topo.png) + +The program in a nutshell: +- non-TCP traffic is dropped. +- when a TCP packet is received on the external interface, for which there is no +mapping, the packet is dropped. +- when a TCP packet is received on the internal interface, for which there is no +mapping, the packet is sent to CPU and 2 new rules are added to the `nat` table +to allow translation and forwarding of TCP packets in both directions. The +packet is then re-injected in the dataplane (to avoid dropping the SYN packet). +- when a TCP packet is received on the either interface, for which there is a +mapping, the appropriate rewrites are executed for the IPv4 and TCP headers and +the packet is forwarded appropriately. + +The most important part of the program is the `nat` table. If you understand +what this table is doing, the rest of the program is mostly IPv4 forwarding. You +should also take a long look at [nat_app.py] (nat_app.py), which manages the +mappings and dynamically adds rules to the `nat` table. + +We use 11 as the CPU port. There is a special CPU veth pair (`cpu-veth-0` / +`cpu-veth-1`), which is use by the switch / the app to send and receive CPU +packets. The veth pair is created by `run_demo.sh` and can be destroyed with +`cleanup.sh`. + +### Running the demo + +To run the demo: +- start the 1-switch Mininet topo with `sudo ./run_demo.sh`. +- start the app with `sudo python nat_app.py`. +- create a TCP flow in Mininet from `h1` (internal host) to `h2` (external host) + - `h2 iperf -s&` + - `h1 iperf -c h2` +- you can cleanup the demo with `sudo ./cleanup.sh` diff --git a/examples/simple_nat/cleanup.sh b/examples/simple_nat/cleanup.sh new file mode 100755 index 0000000..095b66f --- /dev/null +++ b/examples/simple_nat/cleanup.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Copyright 2013-present Barefoot Networks, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [[ $EUID -ne 0 ]]; then + echo "This script should be run using sudo or as the root user" + exit 1 +fi + +killall lt-simple_switch &> /dev/null +mn -c &> /dev/null +intf="cpu-veth-0" +if ip link show $intf &> /dev/null; then + ip link delete $intf type veth +fi diff --git a/examples/simple_nat/commands.txt b/examples/simple_nat/commands.txt new file mode 100644 index 0000000..c4d73f0 --- /dev/null +++ b/examples/simple_nat/commands.txt @@ -0,0 +1,17 @@ +table_set_default if_info _drop +table_add if_info set_if_info 1 => 10.0.0.1 00:aa:bb:00:00:04 0 +table_add if_info set_if_info 2 => 192.168.0.1 00:aa:bb:00:00:05 1 +table_set_default nat _drop +table_add nat nat_miss_ext_to_int 1 1 1 0.0.0.0&&&0.0.0.0 0.0.0.0&&&0.0.0.0 0&&&0 0&&&0 => 99 +table_add nat nat_miss_int_to_ext 0 1 1 0.0.0.0&&&0.0.0.0 0.0.0.0&&&0.0.0.0 0&&&0 0&&&0 => 99 +table_set_default ipv4_lpm _drop +table_add ipv4_lpm set_nhop 10.0.0.10/32 => 10.0.0.10 1 +table_add ipv4_lpm set_nhop 192.168.0.10/32 => 192.168.0.10 2 +table_set_default forward _drop +table_add forward set_dmac 10.0.0.10 => 00:04:00:00:00:10 +table_add forward set_dmac 192.168.0.10 => 00:05:00:00:00:10 +table_set_default send_frame _drop +table_add send_frame do_rewrites 1 => 00:aa:bb:00:00:04 +table_add send_frame do_rewrites 2 => 00:aa:bb:00:00:05 +mirroring_add 250 11 +table_set_default send_to_cpu do_cpu_encap diff --git a/examples/simple_nat/nat_app.py b/examples/simple_nat/nat_app.py new file mode 100644 index 0000000..882fa92 --- /dev/null +++ b/examples/simple_nat/nat_app.py @@ -0,0 +1,77 @@ +# Copyright 2013-present Barefoot Networks, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from scapy.all import * +import subprocess +import os + +CLI_PATH = None + +EXTERN_IP = "192.168.0.1" + +current_nat_port = 1025 +nat_mappings = {} + +def send_to_CLI(cmd): + this_dir = os.path.dirname(os.path.realpath(__file__)) + p = Popen(os.path.join(this_dir, 'sswitch_CLI.sh'), stdout=PIPE, stdin=PIPE) + output = p.communicate(input=cmd)[0] + # print output + +# This is a very basic implementation of a full-cone NAT for TCP traffic +# We do not maintain a state machine for each connection, so we are not able to +# cleanup the port mappings, but this is sufficient for demonstration purposes +def process_cpu_pkt(p): + global current_nat_port + global EXTERN_IP + + p_str = str(p) + # 0-7 : preamble + # 8 : device + # 9 : reason + # 10 : iface + # 11- : data packet (TCP) + if p_str[:8] != '\x00' * 8 or p_str[8] != '\x00' or p_str[9] != '\xab': + return + ip_hdr = None + tcp_hdr = None + try: + p2 = Ether(p_str[11:]) + ip_hdr = p2['IP'] + tcp_hdr = p2['TCP'] + except: + return + print "Packet received" + print p2.summary() + if (ip_hdr.src, tcp_hdr.sport) not in nat_mappings: + ext_port = current_nat_port + current_nat_port += 1 + print "Allocating external port", ext_port + nat_mappings[(ip_hdr.src, tcp_hdr.sport)] = ext_port + # internal to external rule for this mapping + send_to_CLI("table_add nat nat_hit_int_to_ext 0 1 1 %s&&&255.255.255.255 0.0.0.0&&&0.0.0.0 %d&&&0xffff 0&&&0 => %s %d 1" %\ + (ip_hdr.src, tcp_hdr.sport, EXTERN_IP, ext_port)) + # external to internal rule for this mapping + send_to_CLI("table_add nat nat_hit_ext_to_int 1 1 1 0.0.0.0&&&0.0.0.0 %s&&&255.255.255.255 0&&&0 %d&&&0xffff => %s %d 1" %\ + (EXTERN_IP, ext_port, ip_hdr.src, tcp_hdr.sport)) + # a little bit hacky, this essentially ensures that the packet we re-inject + # in the CPU iface will not be processed again by this method + new_p = p_str[:9] + '\xac' + p_str[10:] + sendp(new_p, iface="cpu-veth-0", verbose=0) + +def main(): + sniff(iface="cpu-veth-0", prn=lambda x: process_cpu_pkt(x)) + +if __name__ == '__main__': + main() diff --git a/examples/simple_nat/p4src/simple_nat.p4 b/examples/simple_nat/p4src/simple_nat.p4 new file mode 100644 index 0000000..904c134 --- /dev/null +++ b/examples/simple_nat/p4src/simple_nat.p4 @@ -0,0 +1,364 @@ +/* +Copyright 2013-present Barefoot Networks, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +header_type ethernet_t { + fields { + dstAddr : 48; + srcAddr : 48; + etherType : 16; + } +} + +header_type ipv4_t { + fields { + version : 4; + ihl : 4; + diffserv : 8; + totalLen : 16; + identification : 16; + flags : 3; + fragOffset : 13; + ttl : 8; + protocol : 8; + hdrChecksum : 16; + srcAddr : 32; + dstAddr: 32; + } +} + +parser start { + set_metadata(meta.if_index, standard_metadata.ingress_port); + return select(current(0, 64)) { + 0 : parse_cpu_header; // dummy transition + default: parse_ethernet; + } +} + +header_type cpu_header_t { + fields { + preamble: 64; + device: 8; + reason: 8; + if_index: 8; + } +} + +header cpu_header_t cpu_header; + +parser parse_cpu_header { + extract(cpu_header); + set_metadata(meta.if_index, cpu_header.if_index); + return parse_ethernet; +} + +#define ETHERTYPE_IPV4 0x0800 + +header ethernet_t ethernet; + +parser parse_ethernet { + extract(ethernet); + return select(latest.etherType) { + ETHERTYPE_IPV4 : parse_ipv4; + default: ingress; + } +} + +header ipv4_t ipv4; + +field_list ipv4_checksum_list { + ipv4.version; + ipv4.ihl; + ipv4.diffserv; + ipv4.totalLen; + ipv4.identification; + ipv4.flags; + ipv4.fragOffset; + ipv4.ttl; + ipv4.protocol; + ipv4.srcAddr; + ipv4.dstAddr; +} + +field_list_calculation ipv4_checksum { + input { + ipv4_checksum_list; + } + algorithm : csum16; + output_width : 16; +} + +calculated_field ipv4.hdrChecksum { + verify ipv4_checksum; + update ipv4_checksum; +} + +#define IP_PROT_TCP 0x06 + +parser parse_ipv4 { + extract(ipv4); + set_metadata(meta.ipv4_sa, ipv4.srcAddr); + set_metadata(meta.ipv4_da, ipv4.dstAddr); + set_metadata(meta.tcpLength, ipv4.totalLen - 20); + return select(ipv4.protocol) { + IP_PROT_TCP : parse_tcp; + default : ingress; + } +} + +header_type tcp_t { + fields { + srcPort : 16; + dstPort : 16; + seqNo : 32; + ackNo : 32; + dataOffset : 4; + res : 4; + flags : 8; + window : 16; + checksum : 16; + urgentPtr : 16; + } +} + +header tcp_t tcp; + +parser parse_tcp { + extract(tcp); + set_metadata(meta.tcp_sp, tcp.srcPort); + set_metadata(meta.tcp_dp, tcp.dstPort); + return ingress; +} + +field_list tcp_checksum_list { + ipv4.srcAddr; + ipv4.dstAddr; + 8'0; + ipv4.protocol; + meta.tcpLength; + tcp.srcPort; + tcp.dstPort; + tcp.seqNo; + tcp.ackNo; + tcp.dataOffset; + tcp.res; + tcp.flags; + tcp.window; + tcp.urgentPtr; + payload; +} + +field_list_calculation tcp_checksum { + input { + tcp_checksum_list; + } + algorithm : csum16; + output_width : 16; +} + +calculated_field tcp.checksum { + verify tcp_checksum if(valid(tcp)); + update tcp_checksum if(valid(tcp)); +} + +action _drop() { + drop(); +} + +header_type intrinsic_metadata_t { + fields { + mcast_grp : 4; + egress_rid : 4; + mcast_hash : 16; + lf_field_list: 32; + } +} + +metadata intrinsic_metadata_t intrinsic_metadata; + +header_type meta_t { + fields { + do_forward : 1; + ipv4_sa : 32; + ipv4_da : 32; + tcp_sp : 16; + tcp_dp : 16; + nhop_ipv4 : 32; + if_ipv4_addr : 32; + if_mac_addr : 48; + is_ext_if : 1; + tcpLength : 16; + if_index : 8; + } +} + +metadata meta_t meta; + +action set_if_info(ipv4_addr, mac_addr, is_ext) { + modify_field(meta.if_ipv4_addr, ipv4_addr); + modify_field(meta.if_mac_addr, mac_addr); + modify_field(meta.is_ext_if, is_ext); +} + +table if_info { + reads { + meta.if_index : exact; + } + actions { + _drop; + set_if_info; + } +} + +action nat_miss_ext_to_int() { + modify_field(meta.do_forward, 0); + drop(); +} + +#define CPU_MIRROR_SESSION_ID 250 + +field_list copy_to_cpu_fields { + standard_metadata; +} + +action nat_miss_int_to_ext() { + clone_ingress_pkt_to_egress(CPU_MIRROR_SESSION_ID, copy_to_cpu_fields); +} + +action nat_hit_int_to_ext(srcAddr, srcPort) { + modify_field(meta.do_forward, 1); + modify_field(meta.ipv4_sa, srcAddr); + modify_field(meta.tcp_sp, srcPort); +} + +action nat_hit_ext_to_int(dstAddr, dstPort) { + modify_field(meta.do_forward, 1); + modify_field(meta.ipv4_da, dstAddr); + modify_field(meta.tcp_dp, dstPort); +} + +action nat_no_nat() { + modify_field(meta.do_forward, 1); +} + +table nat { + reads { + meta.is_ext_if : exact; + ipv4 : valid; + tcp : valid; + ipv4.srcAddr : ternary; + ipv4.dstAddr : ternary; + tcp.srcPort : ternary; + tcp.dstPort : ternary; + } + actions { + _drop; + nat_miss_int_to_ext; + nat_miss_ext_to_int; + nat_hit_int_to_ext; + nat_hit_ext_to_int; + nat_no_nat; // for debugging + } + size : 128; +} + +action set_nhop(nhop_ipv4, port) { + modify_field(meta.nhop_ipv4, nhop_ipv4); + modify_field(standard_metadata.egress_spec, port); + add_to_field(ipv4.ttl, -1); +} + +table ipv4_lpm { + reads { + meta.ipv4_da : lpm; + } + actions { + set_nhop; + _drop; + } + size: 1024; +} + +action set_dmac(dmac) { + modify_field(ethernet.dstAddr, dmac); +} + +table forward { + reads { + meta.nhop_ipv4 : exact; + } + actions { + set_dmac; + _drop; + } + size: 512; +} + +action do_rewrites(smac) { + // in case packet was injected by CPU + remove_header(cpu_header); + modify_field(ethernet.srcAddr, smac); + modify_field(ipv4.srcAddr, meta.ipv4_sa); + modify_field(ipv4.dstAddr, meta.ipv4_da); + modify_field(tcp.srcPort, meta.tcp_sp); + modify_field(tcp.dstPort, meta.tcp_dp); +} + +table send_frame { + reads { + standard_metadata.egress_port: exact; + } + actions { + do_rewrites; + _drop; + } + size: 256; +} + +action do_cpu_encap() { + add_header(cpu_header); + modify_field(cpu_header.preamble, 0); + modify_field(cpu_header.device, 0); + modify_field(cpu_header.reason, 0xab); // does not mean anything + modify_field(cpu_header.if_index, meta.if_index); +} + +table send_to_cpu { + actions { do_cpu_encap; } + size : 0; +} + +control ingress { + // retrieve information on the ingress interface + apply(if_info); + // determine what to do with the packet and which rewrites to apply + // depending on direction (int -> ext or ext -> int) and existing nat rules + apply(nat); + // forward packet + if (meta.do_forward == 1 and ipv4.ttl > 0) { + apply(ipv4_lpm); + apply(forward); + } +} + +control egress { + if (standard_metadata.instance_type == 0) { + // regular packet: execute rewrites + apply(send_frame); + } else { + // cpu packet: encap + apply(send_to_cpu); + } +} diff --git a/examples/simple_nat/resources/topo.graphml b/examples/simple_nat/resources/topo.graphml new file mode 100644 index 0000000..e937c3e --- /dev/null +++ b/examples/simple_nat/resources/topo.graphml @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + s1 + + + + + + + + + + + + + + + + + + h2 + + + + + + + + + + + + + + + + + + h1 + + + + + + + + + + + + + + + + + + nat_app.py + + + + + + + + + + + + + + + + + + cpu-veth-0 + + + + + + + + + + + + + + + + + + cpu-veth-1, port 11 + + + + + + + + + + + + + + + + + + port 1 +00:aa:bb:00:00:04 +10.0.0.1 + + + + + + + + + + + + + + + + + + port 2 +00:aa:bb:00:00:05 +192.168.0.1 + + + + + + + + + + + + + + + + + + 00:04:00:00:00:10 +10.0.0.10 + + + + + + + + + + + + + + + + + + 00:05:00:00:00:10 +192.168.0.10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Internal + + + + + + + + + + + + + + + + + + External + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/simple_nat/resources/topo.png b/examples/simple_nat/resources/topo.png new file mode 100644 index 0000000..443b374 Binary files /dev/null and b/examples/simple_nat/resources/topo.png differ diff --git a/examples/simple_nat/run_demo.sh b/examples/simple_nat/run_demo.sh new file mode 100755 index 0000000..d87a509 --- /dev/null +++ b/examples/simple_nat/run_demo.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# Copyright 2013-present Barefoot Networks, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [[ $EUID -ne 0 ]]; then + echo "This script should be run using sudo or as the root user" + exit 1 +fi + +THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +source $THIS_DIR/../env.sh + +P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py + +SWITCH_PATH=$BMV2_PATH/targets/simple_switch/simple_switch + +CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI + +# create CPU port +intf0="cpu-veth-0" +intf1="cpu-veth-1" +if ! ip link show $intf0 &> /dev/null; then + ip link add name $intf0 type veth peer name $intf1 + ip link set dev $intf0 up + ip link set dev $intf1 up + TOE_OPTIONS="rx tx sg tso ufo gso gro lro rxvlan txvlan rxhash" + for TOE_OPTION in $TOE_OPTIONS; do + /sbin/ethtool --offload $intf0 "$TOE_OPTION" off + /sbin/ethtool --offload $intf1 "$TOE_OPTION" off + done +fi +sysctl net.ipv6.conf.$intf0.disable_ipv6=1 +sysctl net.ipv6.conf.$intf1.disable_ipv6=1 + +$P4C_BM_SCRIPT p4src/simple_nat.p4 --json simple_nat.json +PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \ + --behavioral-exe $BMV2_PATH/targets/simple_switch/simple_switch \ + --json simple_nat.json \ + --cli $CLI_PATH \ + --thrift-port 22222 diff --git a/examples/simple_nat/sswitch_CLI.sh b/examples/simple_nat/sswitch_CLI.sh new file mode 100755 index 0000000..da3c710 --- /dev/null +++ b/examples/simple_nat/sswitch_CLI.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +source $THIS_DIR/../env.sh + +CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI + +$CLI_PATH simple_nat.json 22222 diff --git a/examples/simple_nat/topo.py b/examples/simple_nat/topo.py new file mode 100644 index 0000000..6586068 --- /dev/null +++ b/examples/simple_nat/topo.py @@ -0,0 +1,113 @@ +#!/usr/bin/python + +# Copyright 2013-present Barefoot Networks, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from mininet.net import Mininet +from mininet.topo import Topo +from mininet.log import setLogLevel, info +from mininet.cli import CLI +from mininet.link import TCLink, Intf + +from p4_mininet import P4Switch, P4Host + +import argparse +from time import sleep +import os +import subprocess + +parser = argparse.ArgumentParser(description='Mininet demo') +parser.add_argument('--behavioral-exe', help='Path to behavioral executable', + type=str, action="store", required=True) +parser.add_argument('--json', help='Path to JSON config file', + type=str, action="store", required=True) +parser.add_argument('--cli', help='Path to BM CLI', + type=str, action="store", required=True) +parser.add_argument('--thrift-port', help='Thrift server port for table updates', + type=int, action="store", required=True) + +args = parser.parse_args() + +class MyTopo(Topo): + def __init__(self, sw_path, json_path, thrift_port, **opts): + # Initialize topology and default options + Topo.__init__(self, **opts) + + switch = self.addSwitch('s1', + sw_path = sw_path, + json_path = json_path, + thrift_port = thrift_port, + pcap_dump = True) + + # internal host + h1 = self.addHost('h1', + ip = "10.0.0.10", + mac = "00:04:00:00:00:10") + self.addLink(h1, switch) + + # external host + h2 = self.addHost('h2', + ip = "192.168.0.10", + mac = "00:05:00:00:00:10") + self.addLink(h2, switch) + +def main(): + topo = MyTopo(args.behavioral_exe, + args.json, + args.thrift_port) + + net = Mininet(topo = topo, + host = P4Host, + switch = P4Switch, + controller = None) + + cpu_intf = Intf("cpu-veth-1", net.get('s1'), 11) + + net.start() + + sw_macs = ["00:aa:bb:00:00:04", "00:aa:bb:00:00:05"] + + sw_addrs = ["10.0.0.1", "192.168.0.1"] + + for n in xrange(2): + h = net.get('h%d' % (n + 1)) + h.setARP(sw_addrs[n], sw_macs[n]) + h.setDefaultRoute("dev eth0 via %s" % sw_addrs[n]) + + for n in xrange(2): + h = net.get('h%d' % (n + 1)) + h.describe() + + sleep(1) + + cmd = [args.cli, args.json, str(args.thrift_port)] + with open("commands.txt", "r") as f: + print " ".join(cmd) + try: + output = subprocess.check_output(cmd, stdin = f) + print output + except subprocess.CalledProcessError as e: + print e + print e.output + + sleep(1) + + print "Ready !" + + CLI( net ) + net.stop() + +if __name__ == '__main__': + setLogLevel( 'info' ) + main()