added 3 bmv2 examples: copy_to_cpu, meter, TLV_parsing
This commit is contained in:
65
examples/meter/README.md
Normal file
65
examples/meter/README.md
Normal 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.
|
||||
5
examples/meter/commands.txt
Normal file
5
examples/meter/commands.txt
Normal 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
|
||||
82
examples/meter/p4src/meter.p4
Normal file
82
examples/meter/p4src/meter.p4
Normal 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
41
examples/meter/run_switch.sh
Executable 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
|
||||
44
examples/meter/send_and_receive.py
Normal file
44
examples/meter/send_and_receive.py
Normal 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()
|
||||
Reference in New Issue
Block a user