added 3 bmv2 examples: copy_to_cpu, meter, TLV_parsing
This commit is contained in:
47
examples/copy_to_cpu/README.md
Normal file
47
examples/copy_to_cpu/README.md
Normal 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.
|
||||
4
examples/copy_to_cpu/commands.txt
Normal file
4
examples/copy_to_cpu/commands.txt
Normal 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
|
||||
87
examples/copy_to_cpu/p4src/copy_to_cpu.p4
Normal file
87
examples/copy_to_cpu/p4src/copy_to_cpu.p4
Normal file
@@ -0,0 +1,87 @@
|
||||
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);
|
||||
}
|
||||
3
examples/copy_to_cpu/receive.py
Normal file
3
examples/copy_to_cpu/receive.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from scapy.all import *
|
||||
|
||||
sniff(iface = "veth6", prn = lambda x: hexdump(x))
|
||||
41
examples/copy_to_cpu/run_switch.sh
Executable file
41
examples/copy_to_cpu/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/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
|
||||
6
examples/copy_to_cpu/send_one.py
Normal file
6
examples/copy_to_cpu/send_one.py
Normal 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")
|
||||
Reference in New Issue
Block a user