From 547fc0a75fdf72ea6a57b954f0c3da8689864f9a Mon Sep 17 00:00:00 2001 From: Antonin Bas Date: Tue, 10 May 2016 13:11:23 -0700 Subject: [PATCH] added ECMP exercise for May 2016 P4 workshop --- SIGCOMM_2015/.gitignore => .gitignore | 2 +- SIGCOMM_2015/README.md | 6 +- SIGCOMM_2015/flowlet_switching/run_demo.sh | 7 +- SIGCOMM_2015/source_routing/run_demo.sh | 6 +- SIGCOMM_2015/env.sh => env.sh | 4 +- examples/.gitignore | 10 - examples/README.md | 6 +- examples/TLV_parsing/run_switch.sh | 7 +- examples/action_profile/run_switch.sh | 7 +- examples/axon/run_demo.sh | 6 +- examples/copy_to_cpu/run_switch.sh | 7 +- examples/counter/run_demo.sh | 2 +- examples/counter/run_switch.sh | 7 +- examples/env.sh | 8 - examples/meter/run_switch.sh | 7 +- examples/register/read_register.sh | 2 +- examples/register/run_switch.sh | 7 +- examples/register/write_register.sh | 2 +- examples/resubmit/run_switch.sh | 7 +- examples/simple_nat/run_demo.sh | 6 +- examples/simple_nat/sswitch_CLI.sh | 2 +- .../ecmp/.starter_code/commands.txt | 6 + .../ecmp/.starter_code/p4src/simple_router.p4 | 190 ++++++++++++++++++ workshop_05_2016/ecmp/README.md | 98 +++++++++ workshop_05_2016/ecmp/commands.txt | 6 + workshop_05_2016/ecmp/p4src/simple_router.p4 | 190 ++++++++++++++++++ workshop_05_2016/ecmp/run_demo.sh | 49 +++++ workshop_05_2016/ecmp/run_test.py | 147 ++++++++++++++ workshop_05_2016/ecmp/solution_1.tar.gz | Bin 0 -> 2031 bytes workshop_05_2016/ecmp/solution_2.tar.gz | Bin 0 -> 2046 bytes workshop_05_2016/ecmp/veth_setup.sh | 26 +++ 31 files changed, 769 insertions(+), 61 deletions(-) rename SIGCOMM_2015/.gitignore => .gitignore (90%) rename SIGCOMM_2015/env.sh => env.sh (76%) delete mode 100644 examples/.gitignore delete mode 100644 examples/env.sh create mode 100644 workshop_05_2016/ecmp/.starter_code/commands.txt create mode 100644 workshop_05_2016/ecmp/.starter_code/p4src/simple_router.p4 create mode 100644 workshop_05_2016/ecmp/README.md create mode 100644 workshop_05_2016/ecmp/commands.txt create mode 100644 workshop_05_2016/ecmp/p4src/simple_router.p4 create mode 100755 workshop_05_2016/ecmp/run_demo.sh create mode 100755 workshop_05_2016/ecmp/run_test.py create mode 100644 workshop_05_2016/ecmp/solution_1.tar.gz create mode 100644 workshop_05_2016/ecmp/solution_2.tar.gz create mode 100755 workshop_05_2016/ecmp/veth_setup.sh diff --git a/SIGCOMM_2015/.gitignore b/.gitignore similarity index 90% rename from SIGCOMM_2015/.gitignore rename to .gitignore index b4889e1..a57dea6 100644 --- a/SIGCOMM_2015/.gitignore +++ b/.gitignore @@ -10,4 +10,4 @@ *.pcap # Extracted solutions -solution/ \ No newline at end of file +solution*/ \ No newline at end of file diff --git a/SIGCOMM_2015/README.md b/SIGCOMM_2015/README.md index c8c124f..8db53e8 100644 --- a/SIGCOMM_2015/README.md +++ b/SIGCOMM_2015/README.md @@ -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 :) diff --git a/SIGCOMM_2015/flowlet_switching/run_demo.sh b/SIGCOMM_2015/flowlet_switching/run_demo.sh index 25bf7f6..d386500 100755 --- a/SIGCOMM_2015/flowlet_switching/run_demo.sh +++ b/SIGCOMM_2015/flowlet_switching/run_demo.sh @@ -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 & diff --git a/SIGCOMM_2015/source_routing/run_demo.sh b/SIGCOMM_2015/source_routing/run_demo.sh index bc2f639..c541c52 100755 --- a/SIGCOMM_2015/source_routing/run_demo.sh +++ b/SIGCOMM_2015/source_routing/run_demo.sh @@ -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 diff --git a/SIGCOMM_2015/env.sh b/env.sh similarity index 76% rename from SIGCOMM_2015/env.sh rename to env.sh index aedb306..f56fd78 100644 --- a/SIGCOMM_2015/env.sh +++ b/env.sh @@ -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 ------------------ diff --git a/examples/.gitignore b/examples/.gitignore deleted file mode 100644 index 7a46f98..0000000 --- a/examples/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Python byte code -*.pyc - -# Emacs -*~ - -# Compiled JSON -*.json - -*.pcap diff --git a/examples/README.md b/examples/README.md index 660d2bb..b4fe8b0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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. diff --git a/examples/TLV_parsing/run_switch.sh b/examples/TLV_parsing/run_switch.sh index 5673c64..7197818 100755 --- a/examples/TLV_parsing/run_switch.sh +++ b/examples/TLV_parsing/run_switch.sh @@ -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 & diff --git a/examples/action_profile/run_switch.sh b/examples/action_profile/run_switch.sh index 54bd14a..254e1a2 100755 --- a/examples/action_profile/run_switch.sh +++ b/examples/action_profile/run_switch.sh @@ -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 & diff --git a/examples/axon/run_demo.sh b/examples/axon/run_demo.sh index eb48415..8799b95 100755 --- a/examples/axon/run_demo.sh +++ b/examples/axon/run_demo.sh @@ -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 diff --git a/examples/copy_to_cpu/run_switch.sh b/examples/copy_to_cpu/run_switch.sh index 1763fb4..efdbaf8 100755 --- a/examples/copy_to_cpu/run_switch.sh +++ b/examples/copy_to_cpu/run_switch.sh @@ -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 & diff --git a/examples/counter/run_demo.sh b/examples/counter/run_demo.sh index 55e7595..11b4da8 100755 --- a/examples/counter/run_demo.sh +++ b/examples/counter/run_demo.sh @@ -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 diff --git a/examples/counter/run_switch.sh b/examples/counter/run_switch.sh index 9b4c2e2..9f1ad44 100755 --- a/examples/counter/run_switch.sh +++ b/examples/counter/run_switch.sh @@ -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 & diff --git a/examples/env.sh b/examples/env.sh deleted file mode 100644 index aedb306..0000000 --- a/examples/env.sh +++ /dev/null @@ -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 ------------------ diff --git a/examples/meter/run_switch.sh b/examples/meter/run_switch.sh index f507683..80a637f 100755 --- a/examples/meter/run_switch.sh +++ b/examples/meter/run_switch.sh @@ -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 & diff --git a/examples/register/read_register.sh b/examples/register/read_register.sh index 41fae2b..51b4804 100755 --- a/examples/register/read_register.sh +++ b/examples/register/read_register.sh @@ -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 diff --git a/examples/register/run_switch.sh b/examples/register/run_switch.sh index f5f9b22..287f23b 100755 --- a/examples/register/run_switch.sh +++ b/examples/register/run_switch.sh @@ -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 diff --git a/examples/register/write_register.sh b/examples/register/write_register.sh index c22a25d..73bcbe0 100755 --- a/examples/register/write_register.sh +++ b/examples/register/write_register.sh @@ -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 diff --git a/examples/resubmit/run_switch.sh b/examples/resubmit/run_switch.sh index 72e06aa..2dbec35 100755 --- a/examples/resubmit/run_switch.sh +++ b/examples/resubmit/run_switch.sh @@ -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 & diff --git a/examples/simple_nat/run_demo.sh b/examples/simple_nat/run_demo.sh index d87a509..91d8c9e 100755 --- a/examples/simple_nat/run_demo.sh +++ b/examples/simple_nat/run_demo.sh @@ -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 diff --git a/examples/simple_nat/sswitch_CLI.sh b/examples/simple_nat/sswitch_CLI.sh index da3c710..491642c 100755 --- a/examples/simple_nat/sswitch_CLI.sh +++ b/examples/simple_nat/sswitch_CLI.sh @@ -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 diff --git a/workshop_05_2016/ecmp/.starter_code/commands.txt b/workshop_05_2016/ecmp/.starter_code/commands.txt new file mode 100644 index 0000000..e9965d6 --- /dev/null +++ b/workshop_05_2016/ecmp/.starter_code/commands.txt @@ -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 diff --git a/workshop_05_2016/ecmp/.starter_code/p4src/simple_router.p4 b/workshop_05_2016/ecmp/.starter_code/p4src/simple_router.p4 new file mode 100644 index 0000000..0b450f7 --- /dev/null +++ b/workshop_05_2016/ecmp/.starter_code/p4src/simple_router.p4 @@ -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); +} diff --git a/workshop_05_2016/ecmp/README.md b/workshop_05_2016/ecmp/README.md new file mode 100644 index 0000000..ccd85df --- /dev/null +++ b/workshop_05_2016/ecmp/README.md @@ -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 `. + +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. diff --git a/workshop_05_2016/ecmp/commands.txt b/workshop_05_2016/ecmp/commands.txt new file mode 100644 index 0000000..e9965d6 --- /dev/null +++ b/workshop_05_2016/ecmp/commands.txt @@ -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 diff --git a/workshop_05_2016/ecmp/p4src/simple_router.p4 b/workshop_05_2016/ecmp/p4src/simple_router.p4 new file mode 100644 index 0000000..0b450f7 --- /dev/null +++ b/workshop_05_2016/ecmp/p4src/simple_router.p4 @@ -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); +} diff --git a/workshop_05_2016/ecmp/run_demo.sh b/workshop_05_2016/ecmp/run_demo.sh new file mode 100755 index 0000000..7b5246c --- /dev/null +++ b/workshop_05_2016/ecmp/run_demo.sh @@ -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 diff --git a/workshop_05_2016/ecmp/run_test.py b/workshop_05_2016/ecmp/run_test.py new file mode 100755 index 0000000..81f73fe --- /dev/null +++ b/workshop_05_2016/ecmp/run_test.py @@ -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) diff --git a/workshop_05_2016/ecmp/solution_1.tar.gz b/workshop_05_2016/ecmp/solution_1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..7f243e902f8ed2574247f9c033a9a7cd3ad584ad GIT binary patch literal 2031 zcmV^u zb_3QJ+uPj^VNd(tud4lO8%Q$J%$`n1D@x+(dUd_KWfUaJk3z@loc*ZP#dUq%r@z*9 zzia-@*O}G3wrsm+^)36XYh8BxmuJxbVHz1Lk%|d`GZv~S^uwcgFYW)2E?N5DjW!z= zda|va)OVFZ4!*jwkNW@m+OGM(Z}<9V(EYAj99jS8{#WcF;Ev?V@%WM@fda`x&shq# z;ox{8iW~bV%SddQ@Q&_tcN04+5hd~SDB(J)VldA$no%FBiy>~^{rF=|0`DKC%BE`o$yy{1wEX+E@Hv4 zlqz`71Ybsxf(cjKNPLwo7>91V0YBjn-1#mKCHEi+JuX14Io!m|#Xs3j3x4H7Vq`(D z-GwG0yverSoDYe`E=pj-b`YYBL~`WeOIZ2=2ma*pSb-k`&LnZ*v(V+R_0?L-$+(av z@Fn9BJt&6g3<+ZVTAGN!RHih*TB&%@>1?;#Hq+|1BeCiPDWdG$jc-Sj`RD>w&PYCm z0hbax%I}FU@VE~;InSx7TM*v?SXU|h&&Hosk$(JW9~bwS1t~Hv`TFai3h?~p zQgZQ#P(!?m6btTnXh_%89;Of3P}`-pxeVA!BJ9!t3AUONe?_>)hEhRQU@RgPxls+e z_QdU)yI*CpsR=8scG(-&EnEWkOs3rUxW$m^EE1|F3Yi{&O8)yKs)=Xr*FBiW6e~<% z*c~IcYAf!BRe-AmnZuB|^+PY())a9M8z>Pg^wms>dH}^tpud57Lv9GcRU$&rJ5Cyo`I8dNRm~iHJ~S0c zC&p&L6ql-9q{6x3s+vNBOblg&Vf%C>hzTit8MBGtHX*Es|!$dP4XP8fwNSqtDUBjj)KxIj>`f! z3DWeh*eElPAI3@DR8>3XPN3f`SVh=V))c%hBIJRJ6B@E{w!VjrF^ykTBbUwbGDI4s zikdYd6O69$m%FO$lyMScKJxmG5$NNYGrONIrnl3(xwE*P!Bs7giL@5P1bQxjbGw`I zyC)})U@TDI62NIa(?nH05mZwXAyq94rpT^=801rZWo_n~rXRz45v2(liJc8sjOyF* zib|KW)MVAF*y+Gx`eBL(Tk2scq#;8q3OQ5Vw@0`CnzbPFjjsSJ0XLb;b+garChX)^p785LYhYZpo#IC>p4ER307Pn(4qS`cF^ z$<4cousH0}U~bmgv<;%--^_s(LagMWhqv{Tl54*@H1K2sQ!>0fJ1WY^@*>fK3oCW; zo;?Zjncf`YfVt^Vr86gpH>u@lX6&K!w&^F%(Z&M+%~8mpxUIL%%?(qSf0715V(Vd^ z_+eUB#xqh>Pj<+H=4VNK%riSQF*|D}*UkgOQ9XAyx>g`fvCkU7Nn|=nKX4kRO=}W2ZNawDaxwIs_v3~0dHiAV5v*=?=b!h}Pcvt6^Zst+%*Vft z5TR#RQ6?X!RgA04w|1q=1(&H2D|x%$6sis3NI${y(#{5`5|GVK&vg;)1=8Y32dV0K zw0j8mNb{)Lu~O`6PgG6)KLu14$8MB_2b1+k{O#wkQHe61ntgG&msVk%ibnbnX#yH4 zmCtglQh`n1p*1krDkgN(3r>lz8-O|HR!n5lO8*a@+qWK#bq#fv9aiu;n<0Dt#HwOn zr83DorfV%N^3qqW{AP&I3uu#m9jx2=2%8%j+ZCZ27RX;yUwMABXTIX}xrW0-_9#z3 zfWFn!N@XWAUXq*g#lvOw0I&YF!x?YWhtGGkJyBzN)d>wvUp!>5pUsLtkrwI<0S8 N{{c64Sy%ul008qv0)zkn literal 0 HcmV?d00001 diff --git a/workshop_05_2016/ecmp/solution_2.tar.gz b/workshop_05_2016/ecmp/solution_2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..5c2506eaa43357f1860f02cedd035ad71c12c002 GIT binary patch literal 2046 zcmVUF^U2@TMV0Qrw`)F4~F^B+cQu@!W@kWhA$p7g2s{b#$_yf&vs>Olzf9`)t9y1yVDx-wX$SRW{Xr4qfPL>p+ zgtOvg7iGryn(!ppCrRRya=PZJq|uVZ(1O3dfo8KuNbm8nhkx|vWxDo0x|T=h^w7F# zNzHn*-l{u+&F)rOC~!Yvdq1I*nC3~!X)L3dQ-UhaIa?J#<7Hv~9za{tm=p%qWcYGe7XOl ztFKzl|D9``1#q{0**^CF-(xj?fLm55MT6_f)PK|18d7 zn8)=K_yIp~m&P;~G=Wu~V9${Y3O5Cb@yG6*!%vh8j4Wu^n^02-r*`Y~tgl#X*b0_p z13AiA35q;Y0kbrtK%Zk;NJw+QnY74KlE)NOW6*Le7o`b&vOL(MB#2Ispune@j|fCs zr2!UF7QIGeyZfGkS=OG}LT$#g@ zxyEsltt*Pe`wg_>bM)0%@@fDTgrmPf^#*4MPUVW{pm!n{$9;wTC!B55oWfxGe(*5; zG#*65@lRc7J~vloM{cH6s|VwHof{G6!WVBaS2ud zy9}$+2N_n`)EQQVZBwi&Kvp$zIaUGNRH~|NQb|WaX_qJ_S-i?j`Uf_$=1KEnRW(&; zN9P3fO|m(|p0ZHE8zX`XRGiR|4ZbiX*ceUxq8d&%#W!lDkx|sI5mqp|CY^0U*@p3| zzJ5G&A^0@&HzkKY|RE`qT@eN6zHdZvj& zJrPt<6Ct6NTc*hF1TmCP^$UE>X=)z&)gr1UXe7p0Wl2aYr$oCBH630=A-&mmd24bA!9&rE_g2k!INlT-coob<{IH)Nqqk+PYS#D-o z{P4#Vy^W0WmA=}+!PeZo=mAXWuQ)jcP^eg`mMIs+OwR--J4K^a!U#YjwzMqVlx!rY zqU3}lqCJX1j2TC+1dx^kE!L zZ{FSwqRH@|14L+dLzKzjZg4vdaIQM9yFvHKI?;lNMHJ-IZm|vv^?^E%X<5xu(AMmPt3qIk#ZVR;Zp=-M#dFmmV&& zO4Dc5W7oyO /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