added 3 bmv2 examples: copy_to_cpu, meter, TLV_parsing

This commit is contained in:
Antonin Bas
2015-10-22 16:04:30 -07:00
parent c8205b938b
commit 996bbbad31
20 changed files with 868 additions and 0 deletions

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

@@ -0,0 +1,65 @@
# Copy to CPU
## 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,82 @@
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()