diff --git a/examples/README.md b/examples/README.md index 2e69755..dfe3a08 100644 --- a/examples/README.md +++ b/examples/README.md @@ -13,7 +13,8 @@ included: - `register`: how to use registers in P4 and read / write the state from the control plane - `counter`: how to use counters in P4 -- `action_profile`: how to use action profiles in P4, using ECMP has support +- `action_profile`: how to use action profiles in P4, using ECMP as support +- `resubmit`: how to resubmit packets to the ingress pipelines 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 diff --git a/examples/resubmit/README.md b/examples/resubmit/README.md new file mode 100644 index 0000000..54a7331 --- /dev/null +++ b/examples/resubmit/README.md @@ -0,0 +1,28 @@ +# Resubmit + +## Description + +This program illustrates as simply as possible how to use the `resubmit()` +primitive. This primitive is used to make a packet go twice through the ingress +pipeline. For more information, please refer to the P4 specification. + +The P4 program only consists of an ingress pipeline, with 2 tables: +`t_ingress_1` and `t_ingress_2`. When a packet enters the pipeline, the +following happens (based on the P4 program and the [commands.txt] (commands.txt) +files): +- the packet hits `table_ingress_1` and the egress port is set to 2. +- the packet hits `table_ingress_2`, `mymeta.f1` is set to 1 and the + `resubmit()` primitive is called. Because `mymeta` is resubmitted along with + the packet, `mymeta.f1` will now be equal to 1 for the second pass. +- the packet hits `table_ingress_1`, this time the egress port is set to 3. +- the packet hits `table_ingress_2` which is a no-op. + +### Running the demo + +We provide a small demo to let you test the program. It consists of the +following scripts, which you need to run one after the other, in 2 separate +terminals: +- [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 a packet on port 0 (veth1), + wait for the forwarded packet on port 3 (veth7). diff --git a/examples/resubmit/commands.txt b/examples/resubmit/commands.txt new file mode 100644 index 0000000..2b58ff3 --- /dev/null +++ b/examples/resubmit/commands.txt @@ -0,0 +1,5 @@ +table_set_default t_ingress_1 _nop +table_set_default t_ingress_2 _nop +table_add t_ingress_1 set_port 0 => 2 +table_add t_ingress_1 set_port 1 => 3 +table_add t_ingress_2 _resubmit 0 => diff --git a/examples/resubmit/p4src/resubmit.p4 b/examples/resubmit/p4src/resubmit.p4 new file mode 100644 index 0000000..8d560fe --- /dev/null +++ b/examples/resubmit/p4src/resubmit.p4 @@ -0,0 +1,101 @@ +/* +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; + resubmit_flag : 16; + } +} + +header_type mymeta_t { + fields { + f1 : 8; + } +} + +header ethernet_t ethernet; +metadata intrinsic_metadata_t intrinsic_metadata; +metadata mymeta_t mymeta; + +parser start { + return parse_ethernet; +} + +parser parse_ethernet { + extract(ethernet); + return ingress; +} + +action _drop() { + drop(); +} + +action _nop() { +} + +action set_port(port) { + modify_field(standard_metadata.egress_spec, port); +} + +field_list resubmit_FL { + standard_metadata; + mymeta; +} + +action _resubmit() { + modify_field(mymeta.f1, 1); + resubmit(resubmit_FL); +} + +table t_ingress_1 { + reads { + mymeta.f1 : exact; + } + actions { + _nop; set_port; + } + size : 128; +} + +table t_ingress_2 { + reads { + mymeta.f1 : exact; + } + actions { + _nop; _resubmit; + } + size : 128; +} + +control ingress { + apply(t_ingress_1); + apply(t_ingress_2); +} + +control egress { +} diff --git a/examples/resubmit/run_switch.sh b/examples/resubmit/run_switch.sh new file mode 100755 index 0000000..72e06aa --- /dev/null +++ b/examples/resubmit/run_switch.sh @@ -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/resubmit.p4 --json resubmit.json +sudo echo "sudo" > /dev/null +sudo $BMV2_PATH/targets/simple_switch/simple_switch 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 & +sleep 2 +$CLI_PATH resubmit.json < commands.txt +echo "READY!!!" +fg diff --git a/examples/resubmit/send_and_receive.py b/examples/resubmit/send_and_receive.py new file mode 100644 index 0000000..cae643f --- /dev/null +++ b/examples/resubmit/send_and_receive.py @@ -0,0 +1,29 @@ +from scapy.all import * +import sys +import threading + + +class Receiver(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + + def received(self, p): + print "Received packet on port 3, exiting" + sys.exit(0) + + def run(self): + sniff(iface="veth7", prn=lambda x: self.received(x)) + + +def main(): + Receiver().start() + + p = Ether(src="aa:aa:aa:aa:aa:aa") / IP(dst="10.0.1.10") / TCP() / "aaaaaaaaaaaaaaaaaaa" + + print "Sending packet on port 0, listening on port 3" + time.sleep(1) + sendp(p, iface="veth1", verbose=0) + + +if __name__ == '__main__': + main()