added ECMP exercise for May 2016 P4 workshop

This commit is contained in:
Antonin Bas 2016-05-10 13:11:23 -07:00
parent f9ccd265f3
commit 547fc0a75f
31 changed files with 769 additions and 61 deletions

View File

@ -10,4 +10,4 @@
*.pcap
# Extracted solutions
solution/
solution*/

View File

@ -80,9 +80,9 @@ like this:
You need to tell us where you cloned the `bmv2` and `p4c-bm` repositories
:). Please update the values of the shell variables `BMV2_PATH` and
`P4C_BM_PATH` in the `env.sh` file - located in this directory. Note that if you
cloned both repositories in the same directory as this one (`tutorials`), you
will not need to change the value of the variables.
`P4C_BM_PATH` in the `env.sh` file - located in the root directory of this
repository. Note that if you cloned both repositories in the same directory as
this one (`tutorials`), you will not need to change the value of the variables.
That's all :)

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -30,8 +30,9 @@ CLI_PATH=$BMV2_PATH/tools/runtime_CLI.py
# 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 \
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH 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 &

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -25,7 +25,9 @@ SWITCH_PATH=$BMV2_PATH/targets/simple_switch/simple_switch
CLI_PATH=$BMV2_PATH/tools/runtime_CLI.py
$P4C_BM_SCRIPT p4src/source_routing.p4 --json source_routing.json
# This gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \
--behavioral-exe $BMV2_PATH/targets/simple_switch/simple_switch \
--behavioral-exe $SWITCH_PATH \
--json source_routing.json \
--cli $CLI_PATH

View File

@ -1,8 +1,8 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
# ---------------- EDIT THIS ------------------
BMV2_PATH=$THIS_DIR/../../bmv2
BMV2_PATH=$THIS_DIR/../bmv2
# e.g. BMV2_PATH=$THIS_DIR/../bmv2
P4C_BM_PATH=$THIS_DIR/../../p4c-bmv2
P4C_BM_PATH=$THIS_DIR/../p4c-bmv2
# e.g P4C_BM_PATH=$THIS_DIR/../p4c-bm
# ---------------- END ------------------

10
examples/.gitignore vendored
View File

@ -1,10 +0,0 @@
# Python byte code
*.pyc
# Emacs
*~
# Compiled JSON
*.json
*.pcap

View File

@ -55,9 +55,9 @@ build the code once all the dependencies have been installed:
You need to tell us where you cloned the `bmv2` and `p4c-bm` repositories
:). Please update the values of the shell variables `BMV2_PATH` and
`P4C_BM_PATH` in the `env.sh` file - located in this directory. Note that if you
cloned both repositories in the same directory as this one (`tutorials`), you
will not need to change the value of the variables.
`P4C_BM_PATH` in the `env.sh` file - located in the root directory of this
repository. Note that if you cloned both repositories in the same directory as
this one (`tutorials`), you will not need to change the value of the variables.
You will also need to run the `veth_setup.sh` script included in this directory
as `sudo` to setup the veth interfaces needed by the switch.

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -30,8 +30,9 @@ CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI
# process back in the foreground
set -m
$P4C_BM_SCRIPT p4src/TLV_parsing.p4 --json TLV_parsing.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch TLV_parsing.json \
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH TLV_parsing.json \
-i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \
--nanolog ipc:///tmp/bm-0-log.ipc \
--pcap &

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -30,8 +30,9 @@ CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI
# process back in the foreground
set -m
$P4C_BM_SCRIPT p4src/action_profile.p4 --json action_profile.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch action_profile.json \
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH action_profile.json \
-i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \
--nanolog ipc:///tmp/bm-0-log.ipc \
--pcap &

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -25,7 +25,9 @@ SWITCH_PATH=$BMV2_PATH/targets/simple_switch/simple_switch
CLI_PATH=$BMV2_PATH/tools/runtime_CLI.py
$P4C_BM_SCRIPT p4src/axon.p4 --json axon.json
# This gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \
--behavioral-exe $BMV2_PATH/targets/simple_switch/simple_switch \
--behavioral-exe $SWITCH_PATH \
--json axon.json \
--cli $CLI_PATH

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -30,8 +30,9 @@ CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI
# process back in the foreground
set -m
$P4C_BM_SCRIPT p4src/copy_to_cpu.p4 --json copy_to_cpu.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch copy_to_cpu.json \
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH copy_to_cpu.json \
-i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \
--nanolog ipc:///tmp/bm-0-log.ipc \
--pcap &

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -30,8 +30,9 @@ CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI
# process back in the foreground
set -m
$P4C_BM_SCRIPT p4src/counter.p4 --json counter.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch counter.json \
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH counter.json \
-i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \
--nanolog ipc:///tmp/bm-0-log.ipc \
--pcap &

View File

@ -1,8 +0,0 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
# ---------------- EDIT THIS ------------------
BMV2_PATH=$THIS_DIR/../../bmv2
# e.g. BMV2_PATH=$THIS_DIR/../bmv2
P4C_BM_PATH=$THIS_DIR/../../p4c-bmv2
# e.g P4C_BM_PATH=$THIS_DIR/../p4c-bm
# ---------------- END ------------------

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -30,8 +30,9 @@ CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI
# process back in the foreground
set -m
$P4C_BM_SCRIPT p4src/meter.p4 --json meter.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch meter.json \
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH meter.json \
-i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \
--nanolog ipc:///tmp/bm-0-log.ipc \
--pcap &

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -30,8 +30,9 @@ CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI
# process back in the foreground
set -m
$P4C_BM_SCRIPT p4src/register.p4 --json register.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch register.json \
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH register.json \
-i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \
--nanolog ipc:///tmp/bm-0-log.ipc \
--pcap

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI

View File

@ -16,7 +16,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -30,8 +30,9 @@ CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI
# process back in the foreground
set -m
$P4C_BM_SCRIPT p4src/resubmit.p4 --json resubmit.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch resubmit.json \
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH resubmit.json \
-i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \
--nanolog ipc:///tmp/bm-0-log.ipc --log-console \
--pcap &

View File

@ -21,7 +21,7 @@ fi
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
@ -46,8 +46,10 @@ 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
# This gives libtool the opportunity to "warm-up"
$SWITCH_PATH >/dev/null 2>&1
PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \
--behavioral-exe $BMV2_PATH/targets/simple_switch/simple_switch \
--behavioral-exe $SWITCH_PATH \
--json simple_nat.json \
--cli $CLI_PATH \
--thrift-port 22222

View File

@ -2,7 +2,7 @@
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $THIS_DIR/../env.sh
source $THIS_DIR/../../env.sh
CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI

View File

@ -0,0 +1,6 @@
table_set_default send_frame _drop
table_set_default forward _drop
table_set_default ipv4_lpm _drop
table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00
table_add forward set_dmac 10.0.1.1 => 00:04:00:00:00:00
table_add ipv4_lpm set_nhop 10.0.0.1/32 => 10.0.1.1 1

View File

@ -0,0 +1,190 @@
/* 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;
}
}
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;
}
action _drop() {
drop();
}
header_type routing_metadata_t {
fields {
nhop_ipv4 : 32;
// TODO: if you need extra metadata for ECMP, define it here
}
}
metadata routing_metadata_t routing_metadata;
action set_nhop(nhop_ipv4, port) {
modify_field(routing_metadata.nhop_ipv4, nhop_ipv4);
modify_field(standard_metadata.egress_spec, port);
add_to_field(ipv4.ttl, -1);
}
table ipv4_lpm {
reads {
ipv4.dstAddr : lpm;
}
actions {
set_nhop;
_drop;
}
size: 1024;
}
action set_dmac(dmac) {
modify_field(ethernet.dstAddr, dmac);
}
table forward {
reads {
routing_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 {
if(valid(ipv4) and ipv4.ttl > 0) {
// TODO: implement ECMP here
apply(ipv4_lpm);
apply(forward);
}
}
control egress {
apply(send_frame);
}

View File

@ -0,0 +1,98 @@
# Implementing ECMP on top of simple_router.p4
## Introduction
simple_router.p4 is a very simple P4 program which does L3 routing. All the P4
code can be found in the [p4src/simple_router.p4] (p4src/simple_router.p4)
file. In this exercise we will try to build ECMP on top of the starter code. We
will be assuming the following network topology:
```
--------------------------------- nhop-0 10.0.1.1
| 00:04:00:00:00:00
1 - 00:aa:bb:00:00:00
|
-------- 3--sw
|
2 - 00:aa:bb:00:00:01
|
--------------------------------- nhop-1 10.0.2.1
00:04:00:00:00:01
```
Note that we do not assign IPv4 addresses to the 3 switch interfaces, we do not
need to for this exercise.
We will be sending test packets on interface `3` of the switch. These packets
will have destination IP `10.0.0.1`. We will assume that both `nhop-0` and
`nhop-1` have a path to `10.0.0.1`, which is the final destination of our test
packets.
## Running the starter code
*Before starting make sure that you run `sudo ./veth_setup.sh` to create the
veth pairs required for the demo.*
To compile and run the starter code, simply use `./run_demo.sh`. The
[run_demo.sh] (run_demo.sh) script will run the P4 compiler (for bmv2), start
the switch and populate the tables using the CLI commands from [commands.txt]
(commands.txt).
When the switch is running, you can send test packets with `sudo
./run_test.py`. Note that this script will take a few seconds to complete. The
test sends a few hundred identical TCP packets through the switch, in bursts,
on port 3. If you take a look at the P4 code and at commands.txt, you will see
that each TCP packet is forwarded out of port 1; since we do not have ECMP
working yet.
## What you need to do
1. In this exercise, you need to update the provided [P4 program]
(p4src/simple_router.p4) to perform ECMP. When you are done, each incoming TCP
test packet should be forwarded to either port 1 or port 2, based on the result
of a crc16 hash computation performed on the TCP 5-tuple (`ipv4.srcAddr`,
`ipv4.dstAddr`, `ipv4.protocol`, `tcp.srcPort`, `tcp.dstPort`). You will need to
refer to the [P4 spec] (http://p4.org/wp-content/uploads/2015/04/p4-latest.pdf)
to familiarize yourself with the P4 constructs you will need.
2. Once you are done with the P4 code, you will need to update [commands.txt]
(commands.txt) to configure your new tables.
3. After that you can run the above test again. Once again, you will observe
that all packets go to the same egress port. Don't panic :)! This is because all
packets are identical and therefore are forwarded in the same way, If you add
`--random-dport` when running `sudo ./run_test.py`, you should observe an even
distribution for the ports. This option assigns a random destination port to
each test TCP packet (the 5-tuple is different, so the hash is likely to be
different).
## Hints and directions
1. You can easily check the syntax of your P4 program with `p4-validate <path to
simple_router.p4>`.
2. There are 2 major ways of implementing ECMP on top of simple_router.p4. The
first one requires 2 tables and the use of the
`modify_field_with_hash_based_offset` primitive. The second one uses a single
table with an action profile. You can read about
`modify_field_with_hash_based_offset` and action profiles in the [P4 spec]
(http://p4.org/wp-content/uploads/2015/04/p4-latest.pdf).
3. If you choose to use the first way (with 2 tables), your first table will
match on the destination IP address and be in charge of computing an index
(using `modify_field_with_hash_based_offset`), while the second table will match
on this computed index to obtain the outgoing interface. This is a high level
view of what needs to be implemented in P4.
```
T1
IP_prefix_1 ---> "random" index in [0, 1] using modify_field_with_hash_based_offset
IP_prefix_2 ---> "random" index in [2, 4] ...
...
T2
index(0) ---> nhop A
index(1) ---> nhop B
index(2) ---> nhop C
index(3) ---> nhop D
index(4) ---> nhop E
```
Remember that `T1` and `T2`'s entries will come from your modified commands.txt.

View File

@ -0,0 +1,6 @@
table_set_default send_frame _drop
table_set_default forward _drop
table_set_default ipv4_lpm _drop
table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00
table_add forward set_dmac 10.0.1.1 => 00:04:00:00:00:00
table_add ipv4_lpm set_nhop 10.0.0.1/32 => 10.0.1.1 1

View File

@ -0,0 +1,190 @@
/* 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;
}
}
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;
}
action _drop() {
drop();
}
header_type routing_metadata_t {
fields {
nhop_ipv4 : 32;
// TODO: if you need extra metadata for ECMP, define it here
}
}
metadata routing_metadata_t routing_metadata;
action set_nhop(nhop_ipv4, port) {
modify_field(routing_metadata.nhop_ipv4, nhop_ipv4);
modify_field(standard_metadata.egress_spec, port);
add_to_field(ipv4.ttl, -1);
}
table ipv4_lpm {
reads {
ipv4.dstAddr : lpm;
}
actions {
set_nhop;
_drop;
}
size: 1024;
}
action set_dmac(dmac) {
modify_field(ethernet.dstAddr, dmac);
}
table forward {
reads {
routing_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 {
if(valid(ipv4) and ipv4.ttl > 0) {
// TODO: implement ECMP here
apply(ipv4_lpm);
apply(forward);
}
}
control egress {
apply(send_frame);
}

View File

@ -0,0 +1,49 @@
#!/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
if [ $? -ne 0 ]; then
echo "p4 compilation failed"
exit 1
fi
# This gets root permissions, and gives libtool the opportunity to "warm-up"
sudo $SWITCH_PATH >/dev/null 2>&1
sudo $SWITCH_PATH simple_router.json \
-i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \
--log-console \
--pcap &
sleep 2
echo "**************************************"
echo "Sending commands to switch through CLI"
echo "**************************************"
$CLI_PATH --json simple_router.json < commands.txt
echo "READY!!!"
fg

147
workshop_05_2016/ecmp/run_test.py Executable file
View File

@ -0,0 +1,147 @@
#!/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 argparse
import threading
from scapy.all import sniff
from scapy.all import Ether, IP, IPv6, TCP
parser = argparse.ArgumentParser(description='run_test.py')
parser.add_argument('--random-dport',
help='Use a random TCP dest port for each packet',
action="store_true", default=False)
args = parser.parse_args()
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)/TCP()
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)
if args.random_dport:
pkt["TCP"].dport = random.randint(1025, 65535)
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
print "DISTRIBUTION..."
for p in port_map:
c = ports.count(p)
print "port {}: {:>3} [ {:>5}% ]".format(p, c, 100. * c / NUM_PACKETS)

Binary file not shown.

Binary file not shown.

View File

@ -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