diff --git a/README.md b/README.md index fa04115..b62cab9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # tutorials -Holds different P4 language tutorials +P4 language tutorials diff --git a/SIGCOMM_2015/flowlet_switching/commands.txt b/SIGCOMM_2015/flowlet_switching/commands.txt new file mode 100644 index 0000000..7c0e3ae --- /dev/null +++ b/SIGCOMM_2015/flowlet_switching/commands.txt @@ -0,0 +1,11 @@ +table_set_default ecmp_group _drop +table_set_default ecmp_nhop _drop +table_set_default forward _drop +table_set_default send_frame _drop +table_add ecmp_group set_ecmp_select 10.0.0.1/32 => 0 2 +table_add ecmp_nhop set_nhop 0 => 10.0.1.1 1 +table_add ecmp_nhop set_nhop 1 => 10.0.2.1 2 +table_add forward set_dmac 10.0.1.1 => 00:04:00:00:00:00 +table_add forward set_dmac 10.0.2.1 => 00:04:00:00:00:01 +table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00 +table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:01 diff --git a/SIGCOMM_2015/flowlet_switching/p4src/includes/headers.p4 b/SIGCOMM_2015/flowlet_switching/p4src/includes/headers.p4 new file mode 100644 index 0000000..b9b0725 --- /dev/null +++ b/SIGCOMM_2015/flowlet_switching/p4src/includes/headers.p4 @@ -0,0 +1,56 @@ +/* +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; + } +} + +header_type tcp_t { + fields { + srcPort : 16; + dstPort : 16; + seqNo : 32; + ackNo : 32; + dataOffset : 4; + res : 3; + ecn : 3; + ctrl : 6; + window : 16; + checksum : 16; + urgentPtr : 16; + } +} diff --git a/SIGCOMM_2015/flowlet_switching/p4src/includes/intrinsic.p4 b/SIGCOMM_2015/flowlet_switching/p4src/includes/intrinsic.p4 new file mode 100644 index 0000000..1785cd0 --- /dev/null +++ b/SIGCOMM_2015/flowlet_switching/p4src/includes/intrinsic.p4 @@ -0,0 +1,26 @@ +/* +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 intrinsic_metadata_t { + fields { + ingress_global_timestamp : 48; + lf_field_list : 32; + mcast_grp : 16; + egress_rid : 16; + } +} + +metadata intrinsic_metadata_t intrinsic_metadata; diff --git a/SIGCOMM_2015/flowlet_switching/p4src/includes/parser.p4 b/SIGCOMM_2015/flowlet_switching/p4src/includes/parser.p4 new file mode 100644 index 0000000..6d92708 --- /dev/null +++ b/SIGCOMM_2015/flowlet_switching/p4src/includes/parser.p4 @@ -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. +*/ + +parser start { + 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_PROTOCOLS_TCP 6 + +parser parse_ipv4 { + extract(ipv4); + return select(latest.protocol) { + IP_PROTOCOLS_TCP : parse_tcp; + default: ingress; + } +} + +header tcp_t tcp; + +parser parse_tcp { + extract(tcp); + return ingress; +} diff --git a/SIGCOMM_2015/flowlet_switching/p4src/simple_router.p4 b/SIGCOMM_2015/flowlet_switching/p4src/simple_router.p4 new file mode 100644 index 0000000..2df930c --- /dev/null +++ b/SIGCOMM_2015/flowlet_switching/p4src/simple_router.p4 @@ -0,0 +1,132 @@ +/* +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. +*/ + +#include "includes/headers.p4" +#include "includes/parser.p4" +#include "includes/intrinsic.p4" + +header_type ingress_metadata_t { + fields { + ecmp_offset : 14; // offset into the ecmp table + + nhop_ipv4 : 32; + + // TODO: add your flowlet metadata here + } +} + +metadata ingress_metadata_t ingress_metadata; + +action _drop() { + drop(); +} + +action set_nhop(nhop_ipv4, port) { + modify_field(ingress_metadata.nhop_ipv4, nhop_ipv4); + modify_field(standard_metadata.egress_spec, port); + add_to_field(ipv4.ttl, -1); +} + +#define ECMP_BIT_WIDTH 10 +#define ECMP_GROUP_TABLE_SIZE 1024 +#define ECMP_NHOP_TABLE_SIZE 16384 + +// TODO: add flowlet id to hash fields + +field_list l3_hash_fields { + ipv4.srcAddr; + ipv4.dstAddr; + ipv4.protocol; + tcp.srcPort; + tcp.dstPort; +} + +field_list_calculation ecmp_hash { + input { + l3_hash_fields; + } + algorithm : crc16; + output_width : ECMP_BIT_WIDTH; +} + + +action set_ecmp_select(ecmp_base, ecmp_count) { + modify_field_with_hash_based_offset(ingress_metadata.ecmp_offset, ecmp_base, + ecmp_hash, ecmp_count); +} + +table ecmp_group { + reads { + ipv4.dstAddr : lpm; + } + actions { + _drop; + set_ecmp_select; + } + size : ECMP_GROUP_TABLE_SIZE; +} + +table ecmp_nhop { + reads { + ingress_metadata.ecmp_offset : exact; + } + actions { + _drop; + set_nhop; + } + size : ECMP_NHOP_TABLE_SIZE; +} + +action set_dmac(dmac) { + modify_field(ethernet.dstAddr, dmac); +} + +table forward { + reads { + ingress_metadata.nhop_ipv4 : exact; + } + actions { + set_dmac; + _drop; + } + size: 512; +} + +action rewrite_mac(smac) { + modify_field(ethernet.srcAddr, smac); +} + +table send_frame { + reads { + standard_metadata.egress_port: exact; + } + actions { + rewrite_mac; + _drop; + } + size: 256; +} + +control ingress { + // TODO: flowlet switching + apply(ecmp_group); + apply(ecmp_nhop); + apply(forward); +} + +control egress { + apply(send_frame); +} diff --git a/SIGCOMM_2015/flowlet_switching/run_demo.sh b/SIGCOMM_2015/flowlet_switching/run_demo.sh new file mode 100755 index 0000000..25bf7f6 --- /dev/null +++ b/SIGCOMM_2015/flowlet_switching/run_demo.sh @@ -0,0 +1,41 @@ +#!/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. + +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/tools/runtime_CLI.py + +# Probably not very elegant but it works nice here: we enable interactive mode +# to be able to use fg. We start the switch in the background, sleep for 2 +# minutes to give it time to start, then add the entries and put the switch +# process back in the foreground +set -m +$P4C_BM_SCRIPT p4src/simple_router.p4 --json simple_router.json +sudo echo "sudo" > /dev/null +sudo $BMV2_PATH/targets/simple_switch/simple_switch simple_router.json \ + -i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \ + --nanolog ipc:///tmp/bm-0-log.ipc \ + --pcap & +sleep 2 +$CLI_PATH --json simple_router.json < commands.txt +echo "READY!!!" +fg diff --git a/SIGCOMM_2015/flowlet_switching/run_test.py b/SIGCOMM_2015/flowlet_switching/run_test.py new file mode 100755 index 0000000..5e09682 --- /dev/null +++ b/SIGCOMM_2015/flowlet_switching/run_test.py @@ -0,0 +1,135 @@ +#!/usr/bin/env 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. + +import time + +NUM_PACKETS = 500 + +import random + +import threading +from scapy.all import sniff +from scapy.all import Ether, IP, IPv6 +from scapy.all import sendp + +class PacketQueue: + def __init__(self): + self.pkts = [] + self.lock = threading.Lock() + self.ifaces = set() + + def add_iface(self, iface): + self.ifaces.add(iface) + + def get(self): + self.lock.acquire() + if not self.pkts: + self.lock.release() + return None, None + pkt = self.pkts.pop(0) + self.lock.release() + return pkt + + def add(self, iface, pkt): + if iface not in self.ifaces: + return + self.lock.acquire() + self.pkts.append( (iface, pkt) ) + self.lock.release() + +queue = PacketQueue() + +def pkt_handler(pkt, iface): + if IPv6 in pkt: + return + queue.add(iface, pkt) + +class SnifferThread(threading.Thread): + def __init__(self, iface, handler = pkt_handler): + threading.Thread.__init__(self) + self.iface = iface + self.handler = handler + + def run(self): + sniff( + iface = self.iface, + prn = lambda x: self.handler(x, self.iface) + ) + +class PacketDelay: + def __init__(self, bsize, bdelay, imin, imax, num_pkts = 100): + self.bsize = bsize + self.bdelay = bdelay + self.imin = imin + self.imax = imax + self.num_pkts = num_pkts + self.current = 1 + + def __iter__(self): + return self + + def next(self): + if self.num_pkts <= 0: + raise StopIteration + self.num_pkts -= 1 + if self.current == self.bsize: + self.current = 1 + return random.randint(self.imin, self.imax) + else: + self.current += 1 + return self.bdelay + + +pkt = Ether()/IP(dst='10.0.0.1', ttl=64) + +port_map = { + 1: "veth3", + 2: "veth5", + 3: "veth7" +} + +iface_map = {} +for p, i in port_map.items(): + iface_map[i] = p + +queue.add_iface("veth3") +queue.add_iface("veth5") + +for p, iface in port_map.items(): + t = SnifferThread(iface) + t.daemon = True + t.start() + +import socket + +send_socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, + socket.htons(0x03)) +send_socket.bind((port_map[3], 0)) + +delays = PacketDelay(10, 5, 25, 100, NUM_PACKETS) +ports = [] +print "Sending", NUM_PACKETS, "packets ..." +for d in delays: + # sendp is too slow... + # sendp(pkt, iface=port_map[3], verbose=0) + send_socket.send(str(pkt)) + time.sleep(d / 1000.) +time.sleep(1) +iface, pkt = queue.get() +while pkt: + ports.append(iface_map[iface]) + iface, pkt = queue.get() +print ports diff --git a/SIGCOMM_2015/flowlet_switching/solution.tar.gz b/SIGCOMM_2015/flowlet_switching/solution.tar.gz new file mode 100644 index 0000000..4ed6ac6 Binary files /dev/null and b/SIGCOMM_2015/flowlet_switching/solution.tar.gz differ diff --git a/SIGCOMM_2015/flowlet_switching/veth_setup.sh b/SIGCOMM_2015/flowlet_switching/veth_setup.sh new file mode 100755 index 0000000..4083541 --- /dev/null +++ b/SIGCOMM_2015/flowlet_switching/veth_setup.sh @@ -0,0 +1,26 @@ +#!/bin/bash +noOfVeths=18 +if [ $# -eq 1 ]; then + noOfVeths=$1 +fi +echo "No of Veths is $noOfVeths" +idx=0 +let "vethpairs=$noOfVeths/2" +while [ $idx -lt $vethpairs ] +do + intf0="veth$(($idx*2))" + intf1="veth$(($idx*2+1))" + idx=$((idx + 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 +done