Merge pull request #1 from p4lang/bmv2-samples

Bmv2 samples
This commit is contained in:
Antonin Bas 2015-10-27 09:59:06 -07:00
commit 6630197e9c
20 changed files with 916 additions and 0 deletions

10
examples/.gitignore vendored Normal file
View File

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

59
examples/README.md Normal file
View File

@ -0,0 +1,59 @@
# P4 Code Samples
## Introduction
This directory includes P4 code samples, meant to be run on bmv2, which
illustrates several more advanced features of P4. The following samples are
included:
- `copy_to_cpu`: how to use the `clone_ingress_to_egress primitive` to clone the
packet, encapsulate it and send it to a special port.
- `meter`: how to use indirect meters in P4.
- `TLV_parsing`: how to parse IPv4 options
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
how to run a quick demonstration.
## Obtaining required software
To complete the exercises, you will need to clone 2 p4lang Github repositories
and install their dependencies. To clonde the repositories:
- `git clone https://github.com/p4lang/behavioral-model.git bmv2`
- `git clone https://github.com/p4lang/p4c-bm.git p4c-bmv2`
The first repository ([bmv2](https://github.com/p4lang/behavioral-model)) is the
second version of the behavioral model. It is a C++ software switch that will
behave according to your P4 program. The second repository
([p4c-bmv2](https://github.com/p4lang/p4c-bm)) is the compiler for the
behavioral model: it takes P4 program and output a JSON file which can be loaded
by the behavioral model.
Each of these repositories come with dependencies. `p4c-bmv2` is a Python
repository and installing the required Python dependencies is very easy to do
using `pip`: `sudo pip install -r requirements.txt`.
`bmv2` is a C++ repository and has more external dependencies. They are listed
in the
[README](https://github.com/p4lang/behavioral-model/blob/master/README.md). If
you are running Ubuntu 14.04+, the dependencies should be easy to install (you
can use the `install_deps.sh` script that comes with `bmv2`). Do not forget to
build the code once all the dependencies have been installed:
- `./autogen.sh`
- `./configure`
- `make`
## Before starting the exercises
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.
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.
That's all :)

View File

@ -0,0 +1,62 @@
# TLV parsing of IPv4 options
## Description
This program illustrates how to parse IPv4 options with bmv2. There is a very
easy way to parse IPv4 options in P4 using a single variable length
field. However, this means that options are not parsed individually, but
together, as one block. All the options are parsed to a single field, which is
fine for many use cases but can be insufficient in some. In this example, we use
TLV parsing to parse all options separately to their own header instance.
The program is quite straightforward. The following IPv4 options are supported:
- end of list
- no-op
- security
- timestamp
There is one important caveat: when compiling the P4 program, a strict ordering
of all packet headers has to be known. This is usually done by inspecting the
parse graph and running a topological sorting algorithm on it. However this
algorithm will not work if there exists loops in the header graph, as is the
case with TLV parsing. There is not yet an official way of enforcing your own
header ordeing in the P4 program, so we had to bypass this restriction by using
a `@pragma`, as you can see in the code:
@pragma header_ordering ethernet ipv4_base ipv4_option_security ipv4_option_NOP ipv4_option_timestamp ipv4_option_EOL
This `@pragma` instruction will be interpreted by the P4 -> bmv2 compiler.
This order is used by the deparser, when sending a packet out of the egress
port, which means that the option layout for the outgoing packet may not be the
same as for the incoming packet.
The table `format_options` makes sure that the IPv4 header is formatted
correctly in the outgoing packet.
Note that the P4 program assumes the incoming packet is correctly formatted. We
do not perform any sanity checking because *parser execptions* are not yet
supported by bmv2.
So in a nutshell, all this P4 program does is:
1. parse the IPv4 options for the incoming packet
2. re-serialize the packet again, with a potentially different order for options
### Running the demo
We provide a small demo to let you test the program. It consists of the
following scripts:
- [run_switch.sh] (run_switch.sh): compile the P4 program and starts the switch,
also configures the data plane by running the CLI [commands] (commands.txt).
- [send_one.py] (send_one.py): send an IPv4 packet with options
To run the demo:
- start the switch and configure the tables: `sudo ./run_switch.sh`.
- run the Python script: `sudo python send_one.py`.
Then inspect the `pcap` file for port 0 of the switch (`veth0.pcap`) with
Wireshark. You will observe that the order of the IPv4 options has changed but
that the outgoing packet contains all the options of the incoming packet and is
perfectly valid (with a correct checksum).

View File

@ -0,0 +1,4 @@
table_set_default format_options _nop
table_add format_options format_options_security 1 0 =>
table_add format_options format_options_timestamp 0 1 =>
table_add format_options format_options_both 1 1 =>

View File

@ -0,0 +1,243 @@
/*
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_base_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;
}
}
// End of Option List
#define IPV4_OPTION_EOL_VALUE 0x00
header_type ipv4_option_EOL_t {
fields {
value : 8;
}
}
// No operation
#define IPV4_OPTION_NOP_VALUE 0x01
header_type ipv4_option_NOP_t {
fields {
value : 8;
}
}
#define IPV4_OPTION_SECURITY_VALUE 0x82
header_type ipv4_option_security_t {
fields {
value : 8;
len : 8;
security : 72;
}
}
#define IPV4_OPTION_TIMESTAMP_VALUE 0x44
header_type ipv4_option_timestamp_t {
fields {
value : 8;
len : 8;
data : *;
}
length : len;
max_length : 40;
}
header_type intrinsic_metadata_t {
fields {
mcast_grp : 4;
egress_rid : 4;
mcast_hash : 16;
lf_field_list: 32;
}
}
header_type my_metadata_t {
fields {
parse_ipv4_counter : 8;
}
}
header ethernet_t ethernet;
header ipv4_base_t ipv4_base;
header ipv4_option_EOL_t ipv4_option_EOL[3];
header ipv4_option_NOP_t ipv4_option_NOP[3];
header ipv4_option_security_t ipv4_option_security;
header ipv4_option_timestamp_t ipv4_option_timestamp;
metadata intrinsic_metadata_t intrinsic_metadata;
metadata my_metadata_t my_metadata;
@pragma header_ordering ethernet ipv4_base ipv4_option_security ipv4_option_NOP ipv4_option_timestamp ipv4_option_EOL
parser start {
return parse_ethernet;
}
#define ETHERTYPE_IPV4 0x0800
parser parse_ethernet {
extract(ethernet);
return select(ethernet.etherType) {
ETHERTYPE_IPV4 : parse_ipv4;
default: ingress;
}
}
parser parse_ipv4 {
extract(ipv4_base);
set_metadata(my_metadata.parse_ipv4_counter, ipv4_base.ihl * 4 - 20);
return select(ipv4_base.ihl) {
0x05 : ingress;
default : parse_ipv4_options;
}
}
parser parse_ipv4_options {
// match on byte counter and option value
return select(my_metadata.parse_ipv4_counter, current(0, 8)) {
0x0000 mask 0xff00 : ingress;
0x0000 mask 0x00ff : parse_ipv4_option_EOL;
0x0001 mask 0x00ff : parse_ipv4_option_NOP;
0x0082 mask 0x00ff : parse_ipv4_option_security;
0x0044 mask 0x00ff : parse_ipv4_option_timestamp;
}
}
parser parse_ipv4_option_EOL {
extract(ipv4_option_EOL[next]);
set_metadata(my_metadata.parse_ipv4_counter,
my_metadata.parse_ipv4_counter - 1);
return parse_ipv4_options;
}
parser parse_ipv4_option_NOP {
extract(ipv4_option_NOP[next]);
set_metadata(my_metadata.parse_ipv4_counter,
my_metadata.parse_ipv4_counter - 1);
return parse_ipv4_options;
}
parser parse_ipv4_option_security {
extract(ipv4_option_security);
// security option must have length 11 bytes
set_metadata(my_metadata.parse_ipv4_counter,
my_metadata.parse_ipv4_counter - 11);
return parse_ipv4_options;
}
parser parse_ipv4_option_timestamp {
extract(ipv4_option_timestamp);
set_metadata(my_metadata.parse_ipv4_counter,
my_metadata.parse_ipv4_counter - ipv4_option_timestamp.len);
return parse_ipv4_options;
}
field_list ipv4_checksum_list {
ipv4_base.version;
ipv4_base.ihl;
ipv4_base.diffserv;
ipv4_base.totalLen;
ipv4_base.identification;
ipv4_base.flags;
ipv4_base.fragOffset;
ipv4_base.ttl;
ipv4_base.protocol;
ipv4_base.srcAddr;
ipv4_base.dstAddr;
ipv4_option_security;
ipv4_option_NOP[0];
ipv4_option_timestamp;
}
field_list_calculation ipv4_checksum {
input {
ipv4_checksum_list;
}
algorithm : csum16;
output_width : 16;
}
calculated_field ipv4_base.hdrChecksum {
update ipv4_checksum;
}
action _drop() {
drop();
}
action _nop() {
}
control ingress {
}
action format_options_security() {
pop(ipv4_option_NOP, 3);
pop(ipv4_option_EOL, 3);
push(ipv4_option_EOL, 1);
modify_field(ipv4_base.ihl, 8);
}
action format_options_timestamp() {
pop(ipv4_option_NOP, 3);
pop(ipv4_option_EOL, 3);
// timestamp option is word-aligned so no need for NOP or EOL
modify_field(ipv4_base.ihl, 5 + (ipv4_option_timestamp.len >> 3));
}
action format_options_both() {
pop(ipv4_option_NOP, 3);
pop(ipv4_option_EOL, 3);
push(ipv4_option_NOP, 1);
modify_field(ipv4_option_NOP[0].value, IPV4_OPTION_NOP_VALUE);
modify_field(ipv4_base.ihl, 8 + (ipv4_option_timestamp.len >> 2));
}
table format_options {
reads {
ipv4_option_security : valid;
ipv4_option_timestamp : valid;
}
actions {
format_options_security;
format_options_timestamp;
format_options_both;
_nop;
}
size : 4;
}
control egress {
apply(format_options);
}

View File

@ -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/targets/simple_switch/sswitch_CLI
# 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/TLV_parsing.p4 --json TLV_parsing.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch 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 &
sleep 2
$CLI_PATH TLV_parsing.json < commands.txt
echo "READY!!!"
fg

View File

@ -0,0 +1,6 @@
from scapy.all import *
p = Ether() / IP(options=IPOption('\x44\x0c\x05\x00\x01\x02\x03\x04\x05\x06\x07\x08') / IPOption('\x82\x0b\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9')) / IPOption('\x00') / TCP() / "aaaaaaaaaaa"
# p.show()
hexdump(p)
sendp(p, iface = "veth0")

View File

@ -0,0 +1,47 @@
# Copy to CPU
## Description
This program illustrates as simply as possible how to *send packets to CPU*
(e.g. to a controller).
The P4 program does the following:
- incoming packets are mirrored to the CPU port using the
`clone_ingress_pkt_to_egress` action primitive
- packets mirrored to CPU are encapsulated with a custom `cpu_header` which
includes 2 fields: `device` (1 byte, set to `0`) and `reason` (one byte, set
to `0xab`)
- the original packet is dropped in the egress pipeline
Take a look at the [P4 code] (p4src/copy_to_cpu.p4). The program is very short
and should be easy to understand. You will notice that we use a mirror session
id of `250` in the program. This number is not relevant in itself, but needs to
be consistent between the P4 program and the runtime application.
### Running the demo
We provide a small demo to let you test the program. It consists of the
following scripts:
- [run_switch.sh] (run_switch.sh): compile the P4 program and starts the switch,
also configures the data plane by running the CLI [commands] (commands.txt)
- [receive.py] (receive.py): sniff packets on port 3 (veth6) and print a hexdump
of them
- [send_one.py] (send_one.py): send one simple IPv4 packet on port 0 (veth0)
If you take a look at [commands.txt] (commands.txt), you'll notice the following
command: `mirroring_add 250 3`. This means that all the cloned packets with
mirror id `250` will be sent to port `3`, which is our de facto *CPU port*. This
is the reason why [receive.py] (receive.py) listens for incoming packets on port
`3`.
To run the demo:
- start the switch and configure the tables and the mirroring session: `sudo
./run_switch.sh`
- start the CPU port listener: `sudo python receive.py`
- send packets with `sudo python send_one.py`. Every time you send one packet,
it should be displayed by the listener, encapsulated with our CPU header.
This is a very simple example obviously. Feel free to build upon it. For
example, instead of dropping the original packet, you could try to broadcast it
out of every non-ingress port to have a working L2 switch. You could also build
a L2 controller which receives CPU packets and modifies tables appropriately.

View File

@ -0,0 +1,4 @@
table_set_default copy_to_cpu do_copy_to_cpu
table_set_default redirect _drop
table_add redirect do_cpu_encap 1 =>
mirroring_add 250 3

View File

@ -0,0 +1,103 @@
/*
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 intrinsic_metadata_t {
fields {
mcast_grp : 4;
egress_rid : 4;
mcast_hash : 16;
lf_field_list: 32;
}
}
header_type cpu_header_t {
fields {
device: 8;
reason: 8;
}
}
header cpu_header_t cpu_header;
parser start {
return select(current(0, 64)) {
0 : parse_cpu_header;
default: parse_ethernet;
}
}
header ethernet_t ethernet;
metadata intrinsic_metadata_t intrinsic_metadata;
parser parse_ethernet {
extract(ethernet);
return ingress;
}
parser parse_cpu_header {
extract(cpu_header);
return parse_ethernet;
}
action _drop() {
drop();
}
action _nop() {
}
#define CPU_MIRROR_SESSION_ID 250
field_list copy_to_cpu_fields {
standard_metadata;
}
action do_copy_to_cpu() {
clone_ingress_pkt_to_egress(CPU_MIRROR_SESSION_ID, copy_to_cpu_fields);
}
table copy_to_cpu {
actions {do_copy_to_cpu;}
size : 1;
}
control ingress {
apply(copy_to_cpu);
}
action do_cpu_encap() {
add_header(cpu_header);
modify_field(cpu_header.device, 0);
modify_field(cpu_header.reason, 0xab);
}
table redirect {
reads { standard_metadata.instance_type : exact; }
actions { _drop; do_cpu_encap; }
size : 16;
}
control egress {
apply(redirect);
}

View File

@ -0,0 +1,3 @@
from scapy.all import *
sniff(iface = "veth6", prn = lambda x: hexdump(x))

View File

@ -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/targets/simple_switch/sswitch_CLI
# 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/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 \
-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 copy_to_cpu.json < commands.txt
echo "READY!!!"
fg

View File

@ -0,0 +1,6 @@
from scapy.all import *
p = Ether(dst="aa:bb:cc:dd:ee:ff") / IP(dst="10.0.1.10") / TCP() / "aaaaaaaaaaaaaaaaaaa"
# p.show()
hexdump(p)
sendp(p, iface = "veth0")

8
examples/env.sh Normal file
View File

@ -0,0 +1,8 @@
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 ------------------

65
examples/meter/README.md Normal file
View File

@ -0,0 +1,65 @@
# Meter
## Description
This program illustrates as simply as possible how to use meters in P4 with
bmv2. bmv2 uses two-rate three-color meters as described [here]
(https://tools.ietf.org/html/rfc2698).
For each incoming packet the `m_table` table is applied and the appropriate
meter (based on the packet's source MAC address) is executed. Based on the
observed traffic rate for this sender and the meter's configuration, executing
the meter will yield one of 3 values: `0` (*GREEN*), `1` (*YELLOW*) or `2`
(*RED*). This value will be copied to metadata field `meta.meter_tag`. Note that
if no meter was associated to the sender's MAC address, the table will be a
no-op. This table also redirects all packets - with a known source MAC address-
to port 2 of the switch.
After that, the packet will go through a second table, `m_filter`, which can
either be a no-op or drop the packet based on how the packet was tagged by the
meter. If you take a look at the [runtime commands] (commands.txt) we wrote for
this example, you will see that we configure the table to drop all the packets
for which the color is not *GREEN* (i.e. all packets for which `meta.meter_tag`
is not `0`).
The [commands.txt] (commands.txt) file also gives you the meter
configuration. In this case, the first rate is 0.5 packets per second, with a
burst size of 1, and the second rate is 10 packets per second, with a burst size
of 1 also. Feel free to play with the numbers, but these play nicely with the
demonstration below.
Note that we use an `indirect` meter array, because `direct` ones are not
supported yet by bmv2.
### Running the demo
We provide a small demo to let you test the program. It consists of the
following scripts:
- [run_switch.sh] (run_switch.sh): compile the P4 program and starts the switch,
also configures the data plane by running the CLI [commands] (commands.txt).
- [send_and_receive.py] (send_and_receive.py): send packets periodically on port
0 and listen for packets on port 2.
To run the demo:
- start the switch and configure the tables and the meters: `sudo
./run_switch.sh`.
- run the Python script: `sudo python send_and_receive.py 1`. As you can see,
the script takes one argument, which is the time interval (in seconds) between
two consecutive packets.
If you run the script with an interval of one second, you should observe the
following output:
Received one
Sent one
Sent one
Received one
Sent one
Sent one
Received one
Sent one
...
This is because we send one packet every second, while the first rate of the
meter is 0.5 packets per second. The P4 program therefore drops on average one
packet out of two.

View File

@ -0,0 +1,5 @@
table_set_default m_table _nop
table_add m_table m_action aa:aa:aa:aa:aa:aa => 0
table_set_default m_filter _drop
table_add m_filter _nop 0 =>
meter_set_rates my_meter 0.0000005:1 0.00001:1

View File

@ -0,0 +1,98 @@
/*
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 intrinsic_metadata_t {
fields {
mcast_grp : 4;
egress_rid : 4;
mcast_hash : 16;
lf_field_list: 32;
}
}
header_type meta_t {
fields {
meter_tag : 32;
}
}
metadata meta_t meta;
parser start {
return parse_ethernet;
}
header ethernet_t ethernet;
metadata intrinsic_metadata_t intrinsic_metadata;
parser parse_ethernet {
extract(ethernet);
return ingress;
}
action _drop() {
drop();
}
action _nop() {
}
meter my_meter {
type: packets; // or bytes
static: m_table;
instance_count: 16384;
}
action m_action(meter_idx) {
execute_meter(my_meter, meter_idx, meta.meter_tag);
modify_field(standard_metadata.egress_spec, 1);
}
table m_table {
reads {
ethernet.srcAddr : exact;
}
actions {
m_action; _nop;
}
size : 16384;
}
table m_filter {
reads {
meta.meter_tag : exact;
}
actions {
_drop; _nop;
}
size: 16;
}
control ingress {
apply(m_table);
apply(m_filter);
}
control egress {
}

41
examples/meter/run_switch.sh Executable file
View File

@ -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/targets/simple_switch/sswitch_CLI
# 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/meter.p4 --json meter.json
sudo echo "sudo" > /dev/null
sudo $BMV2_PATH/targets/simple_switch/simple_switch 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 &
sleep 2
$CLI_PATH meter.json < commands.txt
echo "READY!!!"
fg

View File

@ -0,0 +1,44 @@
from scapy.all import *
import sys
import threading
big_lock = threading.Lock()
class Receiver(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True
def received(self, p):
big_lock.acquire()
print "Received one"
big_lock.release()
def run(self):
sniff(iface="veth2", prn=lambda x: self.received(x))
def main():
try:
packet_int = int(sys.argv[1])
print "Sending packet with interval", packet_int
except:
print "Usage: sudo python send_and_receive.py <packet_int (seconds)>"
sys.exit(1)
Receiver().start()
p = Ether(src="aa:aa:aa:aa:aa:aa") / IP(dst="10.0.1.10") / TCP() / "aaaaaaaaaaaaaaaaaaa"
while True:
big_lock.acquire()
sendp(p, iface="veth0", verbose=0)
print "Sent one"
big_lock.release()
time.sleep(packet_int)
if __name__ == '__main__':
main()

26
examples/veth_setup.sh Executable file
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