diff --git a/.gitignore b/.gitignore index 93dfdfaf0..54ac4ea59 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ *.json !*p4app.json !topology.json +!*-runtime.json *.pcap diff --git a/P4D2_2017_Fall/exercises/README.md b/P4D2_2017_Fall/exercises/README.md deleted file mode 100644 index 6e95c093c..000000000 --- a/P4D2_2017_Fall/exercises/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# P4 Tutorial - -## Introduction - -Welcome to the P4 Tutorial! - -We've prepared a set of exercises to help you get started with P4 -programming, organized into four modules: - -1. Introduction and Language Basics -* [Basic Forwarding](./basic) -* [Basic Tunneling](./basic_tunnel) - -2. P4 Runtime and the Control Plane -* [P4 Runtime](./p4runtime) - -3. Monitoring and Debugging -* [Explicit Congestion Notification](./ecn) -* [Multi-Hop Route Inspection](./mri) - -4. Advanced Data Structures -* [Source Routing](./source_routing) -* [Calculator](./calc) - -5. Dynamic Behavior -* [Load Balancing](./load_balance) - -## Obtaining required software - -If you are starting this tutorial at the Fall 2017 P4 Developer Day, then we've already -provided you with a virtual machine that has all of the required -software installed. - -Otherwise, to complete the exercises, you will need to either build a -virtual machine or install several dependencies. - -To build the virtual machine: -- Install [Vagrant](https://vagrantup.com) and [VirtualBox](https://virtualbox.org) -- `cd vm` -- `vagrant up` -- Log in with username `p4` and password `p4` and issue the command `sudo shutdown -r now` -- When the machine reboots, you should have a graphical desktop machine with the required -software pre-installed. - -To install dependencies by hand, please reference the [vm](../vm) installation scripts. -They contain the dependencies, versions, and installation procedure. -You can run them directly on an Ubuntu 16.04 machine: -- `sudo ./root-bootstrap.sh` -- `sudo ./user-bootstrap.sh` diff --git a/P4D2_2017_Fall/exercises/basic/Makefile b/P4D2_2017_Fall/exercises/basic/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2017_Fall/exercises/basic/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/basic/README.md b/P4D2_2017_Fall/exercises/basic/README.md deleted file mode 100644 index 70a7e95a2..000000000 --- a/P4D2_2017_Fall/exercises/basic/README.md +++ /dev/null @@ -1,173 +0,0 @@ -# Implementing Basic Forwarding - -## Introduction - -The objective of this exercise is to write a P4 program that -implements basic forwarding. To keep things simple, we will just -implement forwarding for IPv4. - -With IPv4 forwarding, the switch must perform the following actions -for every packet: (i) update the source and destination MAC addresses, -(ii) decrement the time-to-live (TTL) in the IP header, and (iii) -forward the packet out the appropriate port. - -Your switch will have a single table, which the control plane will -populate with static rules. Each rule will map an IP address to the -MAC address and output port for the next hop. We have already defined -the control plane rules, so you only need to implement the data plane -logic of your P4 program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`basic.p4`, which initially drops all packets. Your job will be to -extend this skeleton program to properly forward IPv4 packets. - -Before that, let's compile the incomplete `basic.p4` and bring -up a switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - make run - ``` - This will: - * compile `basic.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) - configured in a triangle, each connected to one host (`h1`, `h2`, - and `h3`). - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, and `10.0.3.3`. - -2. You should now see a Mininet command prompt. Open two terminals -for `h1` and `h2`, respectively: - ```bash - mininet> xterm h1 h2 - ``` -3. Each host includes a small Python-based messaging client and -server. In `h2`'s xterm, start the server: - ```bash - ./receive.py - ``` -4. In `h1`'s xterm, send a message to `h2`: - ```bash - ./send.py 10.0.2.2 "P4 is cool" - ``` - The message will not be received. -5. Type `exit` to leave each xterm and the Mininet command line. - Then, to stop mininet: - ```bash - make stop - ``` - And to delete all pcaps, build files, and logs: - ```bash - make clean - ``` - -The message was not received because each switch is programmed -according to `basic.p4`, which drops all packets on arrival. -Your job is to extend this file so it forwards packets. - -### A note about the control plane - -A P4 program defines a packet-processing pipeline, but the rules -within each table are inserted by the control plane. When a rule -matches a packet, its action is invoked with parameters supplied by -the control plane as part of the rule. - -In this exercise, we have already implemented the the control plane -logic for you. As part of bringing up the Mininet instance, the -`make run` command will install packet-processing rules in the tables of -each switch. These are defined in the `sX-commands.txt` files, where -`X` corresponds to the switch number. - -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. The commands in the files -`sX-commands.txt` refer to specific tables, keys, and actions by name, -and any changes in the P4 program that add or rename tables, keys, or -actions will need to be reflected in these command files. - -## Step 2: Implement L3 forwarding - -The `basic.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. Your implementation should follow -the structure given in this file---replace each `TODO` with logic -implementing the missing piece. - -A complete `basic.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 (`ipv4_t`). -2. **TODO:** Parsers for Ethernet and IPv4 that populate `ethernet_t` and `ipv4_t` fields. -3. An action to drop a packet, using `mark_to_drop()`. -4. **TODO:** An action (called `ipv4_forward`) that: - 1. Sets the egress port for the next hop. - 2. Updates the ethernet destination address with the address of the next hop. - 3. Updates the ethernet source address with the address of the switch. - 4. Decrements the TTL. -5. **TODO:** A control that: - 1. Defines a table that will read an IPv4 destination address, and - invoke either `drop` or `ipv4_forward`. - 2. An `apply` block that applies the table. -6. **TODO:** A deparser that selects the order - in which fields inserted into the outgoing packet. -7. A `package` instantiation supplied with the parser, control, and deparser. - > In general, a package also requires instances of checksum verification - > and recomputation controls. These are not necessary for this tutorial - > and are replaced with instantiations of empty controls. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, your message from -`h1` should be delivered to `h2`. - -### Food for thought - -The "test suite" for your solution---sending a message from `h1` to -`h2`---is not very robust. What else should you test to be confident -of your implementation? - -> Although the Python `scapy` library is outside the scope of this tutorial, -> it can be used to generate packets for testing. The `send.py` file shows how -> to use it. - -Other questions to consider: - - How would you enhance your program to support next hops? - - Is this program enough to replace a router? What's missing? - -### Troubleshooting - -There are several problems that might manifest as you develop your program: - -1. `basic.p4` might fail to compile. In this case, `make run` will -report the error emitted from the compiler and halt. - -2. `basic.p4` might compile but fail to support the control plane -rules in the `s1-commands.txt` through `s3-command.txt` files that -`make run` tries to install using the Bmv2 CLI. In this case, `make run` -will log the CLI tool output in the `logs` directory. Use these error -messages to fix your `basic.p4` implementation. - -3. `basic.p4` might compile, and the control plane rules might be -installed, but the switch might not process packets in the desired -way. The `/tmp/p4s..log` files contain detailed logs -that describing how each switch processes each packet. The output is -detailed and can help pinpoint logic errors in your implementation. - -#### Cleaning up Mininet - -In the latter two cases above, `make run` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -make stop -``` - -## Next Steps - -Congratulations, your implementation works! In the next exercise we -will build on top of this and add support for a basic tunneling -protocol: [basic_tunnel](../basic_tunnel)! - diff --git a/P4D2_2017_Fall/exercises/basic/s1-commands.txt b/P4D2_2017_Fall/exercises/basic/s1-commands.txt deleted file mode 100644 index 3bfdf6156..000000000 --- a/P4D2_2017_Fall/exercises/basic/s1-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 diff --git a/P4D2_2017_Fall/exercises/basic/s2-commands.txt b/P4D2_2017_Fall/exercises/basic/s2-commands.txt deleted file mode 100644 index 35a49d7a0..000000000 --- a/P4D2_2017_Fall/exercises/basic/s2-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 diff --git a/P4D2_2017_Fall/exercises/basic/s3-commands.txt b/P4D2_2017_Fall/exercises/basic/s3-commands.txt deleted file mode 100644 index c111108a2..000000000 --- a/P4D2_2017_Fall/exercises/basic/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:03:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/Makefile b/P4D2_2017_Fall/exercises/basic_tunnel/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2017_Fall/exercises/basic_tunnel/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/README.md b/P4D2_2017_Fall/exercises/basic_tunnel/README.md deleted file mode 100644 index d3c21ed3a..000000000 --- a/P4D2_2017_Fall/exercises/basic_tunnel/README.md +++ /dev/null @@ -1,166 +0,0 @@ -# Implementing Basic Tunneling - -## Introduction - -In this exercise, we will add support for a basic tunneling protocol -to the IP router that you completed in the previous assignment. To do so, -we will define a new header type to encapsulate the IP packet and -modify the switch to perform routing using our new header. - -The new header type will contain a protocol ID, which indicates the type -of packet being encapsulated, along with a destination ID to be used for -routing. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The starter code for this assignment is in a file called `basic_tunnel.p4` -and is simply the solution to the IP router from the previous exercise. - -Let's first compile this code to and send a packet between two end hosts -to ensure that the IP routing is working as expected. - -1. In your shell, run: - ```bash - make run - ``` - This will: - * compile `basic_tunnel.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) - configured in a triangle, each connected to one host (`h1`, `h2`, - and `h3`). - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, and `10.0.3.3`. - -2. You should now see a Mininet command prompt. Open two terminals -for `h1` and `h2`, respectively: - ```bash - mininet> xterm h1 h2 - ``` -3. Each host includes a small Python-based messaging client and -server. In `h2`'s xterm, start the server: - ```bash - ./receive.py - ``` -4. In `h1`'s xterm, send a message to `h2`: - ```bash - ./send.py 10.0.2.2 "P4 is cool" - ``` - The packet should be received at `h2`. If you examine the received - packet you should see that is consists of an Ethernet header, an IP - header, a TCP header, and the message. If you change the destination IP address - (e.g. try to send to `10.0.3.3`) then the message should not be - received by h2. -5. Type `exit` or `Ctrl-D` to leave each xterm and the Mininet command line. - -Each switch is forwarding based on the destination IP address. Your -job is to change the switch functionality so that they instead decide -the destination port using our new tunnel header. - -### A note about the control plane - -A P4 program defines a packet-processing pipeline, but the rules -within each table are inserted by the control plane. When a rule -matches a packet, its action is invoked with parameters supplied by -the control plane as part of the rule. - -For this exercise, we have already added the necessary static control -plane entries. As part of bringing up the Mininet instance, the -`make run` command will install packet-processing rules in the tables -of each switch. These are defined in the `sX-commands.txt` files, -where `X` corresponds to the switch number. - -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. The commands in the files -`sX-commands.txt` refer to specific tables, keys, and actions by name, -and any changes in the P4 program that add or rename tables, keys, or -actions will need to be reflected in these command files. - -## Step 2: Implement Basic Tunneling - -The `basic_tunnel.p4` file contains an implementation of a basic IP router. -It also contains comments marked with `TODO` which indicate the functionality -that you need to implement. A complete implementation of the `basic_tunnel.p4` -switch will be able to forward based on the contents of a custom encapsulation -header as well as perform normal IP forwarding if the encapsulation header -does not exist in the packet. - -Your job will be to do the following: - -1. **NOTE:** A new header type has been added called `myTunnel_t` that contains two 16-bit fields: `proto_id` and `dst_id`. -2. **NOTE:** The `myTunnel_t` header has been added to the `headers` struct. -2. **TODO:** Update the parser to extract either the `myTunnel` header or `ipv4` header based on the `etherType` field in the Ethernet header. The etherType corresponding to the myTunnel header is `0x1212`. The parser should also extract the `ipv4` header after the `myTunnel` header if `proto_id` == `TYPE_IPV4` (i.e. 0x0800). -3. **TODO:** Define a new action called `myTunnel_forward` that simply sets the egress port (i.e. `egress_spec` field of the `standard_metadata` bus) to the port number provided by the control plane. -4. **TODO:** Define a new table called `myTunnel_exact` that perfoms an exact match on the `dst_id` field of the `myTunnel` header. This table should invoke either the `myTunnel_forward` action if the there is a match in the table and it should invoke the `drop` action otherwise. -5. **TODO:** Update the `apply` statement in the `MyIngress` control block to apply your newly defined `myTunnel_exact` table if the `myTunnel` header is valid. Otherwise, invoke the `ipv4_lpm` table if the `ipv4` header is valid. -6. **TODO:** Update the deparser to emit the `ethernet`, then `myTunnel`, then `ipv4` headers. Remember that the deparser will only emit a header if it is valid. A header's implicit validity bit is set by the parser upon extraction. So there is no need to check header validity here. -7. **TODO:** Add static rules for your newly defined table so that the switches will forward correctly for each possible value of `dst_id`. See the diagram below for the topology's port configuration as well as how we will assign IDs to hosts. For this step you will need to add your forwarding rules to the `sX-commands.txt` files. - -![topology](./topo.png) - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time when you send a packet from -`h1` to `h2` try using the following command to send a packet that uses -our new `myTunnel` header. -```bash -./send.py 10.0.2.2 "P4 is cool" --dst_id 2 -``` - -You should see a packet arrive at `h2` which contains the `MyTunnel` header. -Also note that changing the destination IP address will not prevent the packet -from arriving at `h2`. This is because the switch is no longer using the IP header for routing when the `MyTunnel` header is in the packet. - -> Python Scapy does not natively support the `myTunnel` header -> type so we have provided a file called `myTunnel_header.py` which -> adds support to Scapy for our new custom header. Feel free to inspect -> this file if you are interested in learning how to do this. - -### Food for thought - -To make this tunneling exercise a bit more interesting (and realistic) -how might you change the P4 code to have the switches add the `myTunnel` -header to an IP packet upon ingress to the network and then remove the -`myTunnel` header as the packet leaves to the network to an end host? - -Hints: - - - The ingress switch will need to map the destination IP address to the corresponding `dst_id` for the `myTunnel` header. Also remember to set explicitly set the validity bit for the `myTunnel` header so that it can be emitted by the deparser. - - The egress switch will need to remove the `myTunnel` header from the packet after looking up the appropriate output port using the `dst_id` field. - -### Troubleshooting - -There are several problems that might manifest as you develop your program: - -1. `basic_tunnel.p4` might fail to compile. In this case, `make run` will -report the error emitted from the compiler and halt. - -2. `basic_tunnel.p4` might compile but fail to support the control plane -rules in the `s1-commands.txt` through `s3-command.txt` files that -`make run` tries to install using the Bmv2 CLI. In this case, `make run` -will log the CLI tool output in the `logs` directory. Use these error -messages to fix your `basic_tunnel.p4` implementation or forwarding rules. - -3. `basic_tunnel.p4` might compile, and the control plane rules might be -installed, but the switch might not process packets in the desired -way. The `/tmp/p4s..log` files contain detailed logs -that describing how each switch processes each packet. The output is -detailed and can help pinpoint logic errors in your implementation. - -#### Cleaning up Mininet - -In the latter two cases above, `make` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -make stop -``` - -## Next Steps - -Congratulations, your implementation works! Move onto the next assignment -[p4runtime](../p4runtime)! - diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/basic_tunnel.p4 b/P4D2_2017_Fall/exercises/basic_tunnel/basic_tunnel.p4 deleted file mode 100644 index 156c07778..000000000 --- a/P4D2_2017_Fall/exercises/basic_tunnel/basic_tunnel.p4 +++ /dev/null @@ -1,197 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -// NOTE: new type added here -const bit<16> TYPE_MYTUNNEL = 0x1212; -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -// NOTE: added new header type -header myTunnel_t { - bit<16> proto_id; - bit<16> dst_id; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -// NOTE: Added new header type to headers struct -struct headers { - ethernet_t ethernet; - myTunnel_t myTunnel; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -// TODO: Update the parser to parse the myTunnel header as well -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4 : parse_ipv4; - default : accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - - -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - // TODO: declare a new action: myTunnel_forward(egressSpec_t port) - - - // TODO: declare a new table: myTunnel_exact - // TODO: also remember to add table entries! - - - apply { - // TODO: Update control flow - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - // TODO: emit myTunnel header as well - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/s1-commands.txt b/P4D2_2017_Fall/exercises/basic_tunnel/s1-commands.txt deleted file mode 100644 index 179d7a550..000000000 --- a/P4D2_2017_Fall/exercises/basic_tunnel/s1-commands.txt +++ /dev/null @@ -1,9 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:01:00 2 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:01:00 3 - -table_set_default myTunnel_exact drop -table_add myTunnel_exact myTunnel_forward 1 => 1 -table_add myTunnel_exact myTunnel_forward 2 => 2 -table_add myTunnel_exact myTunnel_forward 3 => 3 diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/s2-commands.txt b/P4D2_2017_Fall/exercises/basic_tunnel/s2-commands.txt deleted file mode 100644 index fb5a2bc4a..000000000 --- a/P4D2_2017_Fall/exercises/basic_tunnel/s2-commands.txt +++ /dev/null @@ -1,9 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:02:00 3 - -table_set_default myTunnel_exact drop -table_add myTunnel_exact myTunnel_forward 1 => 2 -table_add myTunnel_exact myTunnel_forward 2 => 1 -table_add myTunnel_exact myTunnel_forward 3 => 3 diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/s3-commands.txt b/P4D2_2017_Fall/exercises/basic_tunnel/s3-commands.txt deleted file mode 100644 index a6c2777a2..000000000 --- a/P4D2_2017_Fall/exercises/basic_tunnel/s3-commands.txt +++ /dev/null @@ -1,9 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:03:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 - -table_set_default myTunnel_exact drop -table_add myTunnel_exact myTunnel_forward 1 => 2 -table_add myTunnel_exact myTunnel_forward 2 => 3 -table_add myTunnel_exact myTunnel_forward 3 => 1 diff --git a/P4D2_2017_Fall/exercises/calc/Makefile b/P4D2_2017_Fall/exercises/calc/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2017_Fall/exercises/calc/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/calc/s1-commands.txt b/P4D2_2017_Fall/exercises/calc/s1-commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2017_Fall/exercises/calc/topology.json b/P4D2_2017_Fall/exercises/calc/topology.json deleted file mode 100644 index 29d7cdf88..000000000 --- a/P4D2_2017_Fall/exercises/calc/topology.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "hosts": [ - "h1", - "h2" - ], - "switches": { - "s1": { "cli_input" : "s1-commands.txt" } - }, - "links": [ - ["h1", "s1"], ["h2", "s1"] - ] -} diff --git a/P4D2_2017_Fall/exercises/ecn/Makefile b/P4D2_2017_Fall/exercises/ecn/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2017_Fall/exercises/ecn/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/ecn/ecn.p4 b/P4D2_2017_Fall/exercises/ecn/ecn.p4 deleted file mode 100644 index 576169f30..000000000 --- a/P4D2_2017_Fall/exercises/ecn/ecn.p4 +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> TCP_PROTOCOL = 0x06; -const bit<16> TYPE_IPV4 = 0x800; -const bit<19> ECN_THRESHOLD = 10; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -/* - * TODO: split tos to two fields 6 bit diffserv and 2 bit ecn - */ -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> tos; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { - /* - * TODO: - * - if ecn is 1 or 2 - * - compare standard_metadata.enq_qdepth with threshold - * and set hdr.ipv4.ecn to 3 if larger - */ - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - /* TODO: replace tos with diffserve and ecn */ - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.tos, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2017_Fall/exercises/ecn/s1-commands.txt b/P4D2_2017_Fall/exercises/ecn/s1-commands.txt deleted file mode 100644 index dc4e2606a..000000000 --- a/P4D2_2017_Fall/exercises/ecn/s1-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 2 -table_add ipv4_lpm ipv4_forward 10.0.1.11/32 => 00:00:00:00:01:0b 1 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:02:00 4 diff --git a/P4D2_2017_Fall/exercises/ecn/s2-commands.txt b/P4D2_2017_Fall/exercises/ecn/s2-commands.txt deleted file mode 100644 index 3b80bedbf..000000000 --- a/P4D2_2017_Fall/exercises/ecn/s2-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 2 -table_add ipv4_lpm ipv4_forward 10.0.2.22/32 => 00:00:00:00:02:16 1 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:03:00 4 diff --git a/P4D2_2017_Fall/exercises/ecn/s3-commands.txt b/P4D2_2017_Fall/exercises/ecn/s3-commands.txt deleted file mode 100644 index c7285fbaa..000000000 --- a/P4D2_2017_Fall/exercises/ecn/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 -table_add ipv4_lpm ipv4_forward 10.0.1.10/24 => 00:00:00:01:04:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.10/24 => 00:00:00:02:04:00 3 diff --git a/P4D2_2017_Fall/exercises/ecn/topology.json b/P4D2_2017_Fall/exercises/ecn/topology.json deleted file mode 100644 index 2640a2b96..000000000 --- a/P4D2_2017_Fall/exercises/ecn/topology.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "hosts": [ - "h1", - "h2", - "h3", - "h11", - "h22" - ], - "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } - }, - "links": [ - ["h1", "s1"], ["h11", "s1"], ["s1", "s2", "0", 0.5], ["s1", "s3"], - ["s3", "s2"], ["s2", "h2"], ["s2", "h22"], ["s3", "h3"] - ] -} diff --git a/P4D2_2017_Fall/exercises/load_balance/Makefile b/P4D2_2017_Fall/exercises/load_balance/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2017_Fall/exercises/load_balance/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/load_balance/s1-commands.txt b/P4D2_2017_Fall/exercises/load_balance/s1-commands.txt deleted file mode 100644 index a53e07c7d..000000000 --- a/P4D2_2017_Fall/exercises/load_balance/s1-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.0.1/32 => 0 2 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:01:02 10.0.2.2 2 -table_add ecmp_nhop set_nhop 1 => 00:00:00:00:01:03 10.0.3.3 3 -table_add send_frame rewrite_mac 2 => 00:00:00:01:02:00 -table_add send_frame rewrite_mac 3 => 00:00:00:01:03:00 diff --git a/P4D2_2017_Fall/exercises/load_balance/s2-commands.txt b/P4D2_2017_Fall/exercises/load_balance/s2-commands.txt deleted file mode 100644 index ca78c19a1..000000000 --- a/P4D2_2017_Fall/exercises/load_balance/s2-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.2.2/32 => 0 1 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:02:02 10.0.2.2 1 -table_add send_frame rewrite_mac 1 => 00:00:00:02:01:00 diff --git a/P4D2_2017_Fall/exercises/load_balance/s3-commands.txt b/P4D2_2017_Fall/exercises/load_balance/s3-commands.txt deleted file mode 100644 index 0f9e6b363..000000000 --- a/P4D2_2017_Fall/exercises/load_balance/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.3.3/32 => 0 1 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:03:03 10.0.3.3 1 -table_add send_frame rewrite_mac 1 => 00:00:00:03:01:00 diff --git a/P4D2_2017_Fall/exercises/mri/Makefile b/P4D2_2017_Fall/exercises/mri/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2017_Fall/exercises/mri/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/mri/mri.p4 b/P4D2_2017_Fall/exercises/mri/mri.p4 deleted file mode 100644 index 15f5c3bc1..000000000 --- a/P4D2_2017_Fall/exercises/mri/mri.p4 +++ /dev/null @@ -1,281 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> UDP_PROTOCOL = 0x11; -const bit<16> TYPE_IPV4 = 0x800; -const bit<5> IPV4_OPTION_MRI = 31; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; -typedef bit<32> switchID_t; -typedef bit<32> qdepth_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -header ipv4_option_t { - bit<1> copyFlag; - bit<2> optClass; - bit<5> option; - bit<8> optionLength; -} - -header mri_t { - bit<16> count; -} - -header switch_t { - switchID_t swid; - qdepth_t qdepth; -} - -struct ingress_metadata_t { - bit<16> count; -} - -struct parser_metadata_t { - bit<16> remaining; -} - -struct metadata { - ingress_metadata_t ingress_metadata; - parser_metadata_t parser_metadata; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - ipv4_option_t ipv4_option; - mri_t mri; - switch_t[MAX_HOPS] swtraces; -} - -error { IPHeaderTooShort } - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - verify(hdr.ipv4.ihl >= 5, error.IPHeaderTooShort); - transition select(hdr.ipv4.ihl) { - 5 : accept; - default : parse_ipv4_option; - } - } - - state parse_ipv4_option { - /* - * TODO: Add logic to: - * - Extract the ipv4_option header. - * - If value is equal to IPV4_OPTION_MRI, transition to parse_mri. - * - Otherwise, accept. - */ - transition accept; - } - - state parse_mri { - /* - * TODO: Add logic to: - * - Extract hdr.mri. - * - Set meta.parser_metadata.remaining to hdr.mri.count - * - Select on the value of meta.parser_metadata.remaining - * - If the value is equal to 0, accept. - * - Otherwise, transition to parse_swtrace. - */ - transition accept; - } - - state parse_swtrace { - /* - * TODO: Add logic to: - * - Extract hdr.swtraces.next. - * - Decrement meta.parser_metadata.remaining by 1 - * - Select on the value of meta.parser_metadata.remaining - * - If the value is equal to 0, accept. - * - Otherwise, transition to parse_swtrace. - */ - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action add_swtrace(switchID_t swid) { - /* - * TODO: add logic to: - - Increment hdr.mri.count by 1 - - Add a new swtrace header by calling push_front(1) on hdr.swtraces. - - Set hdr.swtraces[0].swid to the id parameter - - Set hdr.swtraces[0].qdepth to (qdepth_t)standard_metadata.deq_qdepth - - Increment hdr.ipv4.ihl by 2 - - Increment hdr.ipv4.totalLen by 8 - - Increment hdr.ipv4_option.optionLength by 8 - */ - } - - table swtrace { - actions = { - /* TODO: add the correct action */ - NoAction; - } - - default_action = NoAction(); - } - - apply { - /* - * TODO: add logic to: - * - If hdr.mri is valid: - * - Apply table swtrace - */ - swtrace.apply(); - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - - /* TODO: emit ipv4_option, mri and swtraces headers */ - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2017_Fall/exercises/mri/s1-commands.txt b/P4D2_2017_Fall/exercises/mri/s1-commands.txt deleted file mode 100644 index 5f15633ba..000000000 --- a/P4D2_2017_Fall/exercises/mri/s1-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 1 -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 2 -table_add ipv4_lpm ipv4_forward 10.0.1.11/32 => 00:00:00:00:01:0b 1 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:02:00 4 diff --git a/P4D2_2017_Fall/exercises/mri/s2-commands.txt b/P4D2_2017_Fall/exercises/mri/s2-commands.txt deleted file mode 100644 index 8b9bd15a7..000000000 --- a/P4D2_2017_Fall/exercises/mri/s2-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 2 -table_add ipv4_lpm ipv4_forward 10.0.2.22/32 => 00:00:00:00:02:16 1 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:03:00 4 diff --git a/P4D2_2017_Fall/exercises/mri/s3-commands.txt b/P4D2_2017_Fall/exercises/mri/s3-commands.txt deleted file mode 100644 index d27b73c50..000000000 --- a/P4D2_2017_Fall/exercises/mri/s3-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 3 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:01 1 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:04:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:04:00 3 diff --git a/P4D2_2017_Fall/exercises/p4runtime/mycontroller.py b/P4D2_2017_Fall/exercises/p4runtime/mycontroller.py deleted file mode 100755 index 2819492cf..000000000 --- a/P4D2_2017_Fall/exercises/p4runtime/mycontroller.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python2 -import argparse -import os -from time import sleep - -import p4runtime_lib.bmv2 -import p4runtime_lib.helper - -SWITCH_TO_HOST_PORT = 1 -SWITCH_TO_SWITCH_PORT = 2 - -def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, - dst_eth_addr, dst_ip_addr): - ''' - Installs three rules: - 1) An tunnel ingress rule on the ingress switch in the ipv4_lpm table that - encapsulates traffic into a tunnel with the specified ID - 2) A transit rule on the ingress switch that forwards traffic based on - the specified ID - 3) An tunnel egress rule on the egress switch that decapsulates traffic - with the specified ID and sends it to the host - - :param p4info_helper: the P4Info helper - :param ingress_sw: the ingress switch connection - :param egress_sw: the egress switch connection - :param tunnel_id: the specified tunnel ID - :param dst_eth_addr: the destination IP to match in the ingress rule - :param dst_ip_addr: the destination Ethernet address to write in the - egress rule - ''' - # 1) Tunnel Ingress Rule - table_entry = p4info_helper.buildTableEntry( - table_name="ipv4_lpm", - match_fields={ - "hdr.ipv4.dstAddr": (dst_ip_addr, 32) - }, - action_name="myTunnel_ingress", - action_params={ - "dst_id": tunnel_id, - }) - ingress_sw.WriteTableEntry(table_entry) - print "Installed ingress tunnel rule on %s" % ingress_sw.name - - # 2) Tunnel Transit Rule - # The rule will need to be added to the myTunnel_exact table and match on - # the tunnel ID (hdr.myTunnel.dst_id). Traffic will need to be forwarded - # using the myTunnel_forward action on the port connected to the next switch. - # - # For our simple topology, switch 1 and switch 2 are connected using a - # link attached to port 2 on both switches. We have defined a variable at - # the top of the file, SWITCH_TO_SWITCH_PORT, that you can use as the output - # port for this action. - # - # We will only need a transit rule on the ingress switch because we are - # using a simple topology. In general, you'll need on transit rule for - # each switch in the path (except the last switch, which has the egress rule), - # and you will need to select the port dynamically for each switch based on - # your topology. - - # TODO build the transit rule - # TODO install the transit rule on the ingress switch - print "TODO Install transit tunnel rule" - - # 3) Tunnel Egress Rule - # For our simple topology, the host will always be located on the - # SWITCH_TO_HOST_PORT (port 1). - # In general, you will need to keep track of which port the host is - # connected to. - table_entry = p4info_helper.buildTableEntry( - table_name="myTunnel_exact", - match_fields={ - "hdr.myTunnel.dst_id": tunnel_id - }, - action_name="myTunnel_egress", - action_params={ - "dstAddr": dst_eth_addr, - "port": SWITCH_TO_HOST_PORT - }) - egress_sw.WriteTableEntry(table_entry) - print "Installed egress tunnel rule on %s" % egress_sw.name - -def readTableRules(p4info_helper, sw): - ''' - Reads the table entries from all tables on the switch. - - :param p4info_helper: the P4Info helper - :param sw: the switch connection - ''' - print '\n----- Reading tables rules for %s -----' % sw.name - for response in sw.ReadTableEntries(): - for entity in response.entities: - entry = entity.table_entry - # TODO For extra credit, you can use the p4info_helper to translate - # the IDs the entry to names - print entry - print '-----' - -def printCounter(p4info_helper, sw, counter_name, index): - ''' - Reads the specified counter at the specified index from the switch. In our - program, the index is the tunnel ID. If the index is 0, it will return all - values from the counter. - - :param p4info_helper: the P4Info helper - :param sw: the switch connection - :param counter_name: the name of the counter from the P4 program - :param index: the counter index (in our case, the tunnel ID) - ''' - for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): - for entity in response.entities: - counter = entity.counter_entry - print "%s %s %d: %d packets (%d bytes)" % ( - sw.name, counter_name, index, - counter.data.packet_count, counter.data.byte_count - ) - - -def main(p4info_file_path, bmv2_file_path): - # Instantiate a P4 Runtime helper from the p4info file - p4info_helper = p4runtime_lib.helper.P4InfoHelper(p4info_file_path) - - # Create a switch connection object for s1 and s2; - # this is backed by a P4 Runtime gRPC connection - s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection('s1', address='127.0.0.1:50051') - s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection('s2', address='127.0.0.1:50052') - - # Install the P4 program on the switches - s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) - print "Installed P4 Program using SetForwardingPipelineConfig on %s" % s1.name - s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) - print "Installed P4 Program using SetForwardingPipelineConfig on %s" % s2.name - - # Write the rules that tunnel traffic from h1 to h2 - writeTunnelRules(p4info_helper, ingress_sw=s1, egress_sw=s2, tunnel_id=100, - dst_eth_addr="00:00:00:00:02:02", dst_ip_addr="10.0.2.2") - - # Write the rules that tunnel traffic from h2 to h1 - writeTunnelRules(p4info_helper, ingress_sw=s2, egress_sw=s1, tunnel_id=200, - dst_eth_addr="00:00:00:00:01:01", dst_ip_addr="10.0.1.1") - - # TODO Uncomment the following two lines to read table entries from s1 and s2 - #readTableRules(p4info_helper, s1) - #readTableRules(p4info_helper, s2) - - # Print the tunnel counters every 2 seconds - try: - while True: - sleep(2) - print '\n----- Reading tunnel counters -----' - printCounter(p4info_helper, s1, "ingressTunnelCounter", 100) - printCounter(p4info_helper, s2, "egressTunnelCounter", 100) - printCounter(p4info_helper, s2, "ingressTunnelCounter", 200) - printCounter(p4info_helper, s1, "egressTunnelCounter", 200) - except KeyboardInterrupt: - print " Shutting down." - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='P4Runtime Controller') - parser.add_argument('--p4info', help='p4info proto in text format from p4c', - type=str, action="store", required=False, - default='./build/advanced_tunnel.p4info') - parser.add_argument('--bmv2-json', help='BMv2 JSON file from p4c', - type=str, action="store", required=False, - default='./build/advanced_tunnel.json') - args = parser.parse_args() - - if not os.path.exists(args.p4info): - parser.print_help() - print "\np4info file not found: %s\nHave you run 'make'?" % args.p4info - parser.exit(1) - if not os.path.exists(args.bmv2_json): - parser.print_help() - print "\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json - parser.exit(1) - - main(args.p4info, args.bmv2_json) diff --git a/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/helper.py b/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/helper.py deleted file mode 100644 index 4906567b3..000000000 --- a/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/helper.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright 2017-present Open Networking Foundation -# -# 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. -# -import re - -import google.protobuf.text_format -from p4 import p4runtime_pb2 -from p4.config import p4info_pb2 - -from p4runtime_lib.convert import encode - -class P4InfoHelper(object): - def __init__(self, p4_info_filepath): - p4info = p4info_pb2.P4Info() - # Load the p4info file into a skeleton P4Info object - with open(p4_info_filepath) as p4info_f: - google.protobuf.text_format.Merge(p4info_f.read(), p4info) - self.p4info = p4info - - def get(self, entity_type, name=None, id=None): - if name is not None and id is not None: - raise AssertionError("name or id must be None") - - for o in getattr(self.p4info, entity_type): - pre = o.preamble - if name: - if (pre.name == name or pre.alias == name): - return o - else: - if pre.id == id: - return o - - if name: - raise AttributeError("Could not find %r of type %s" % (name, entity_type)) - else: - raise AttributeError("Could not find id %r of type %s" % (id, entity_type)) - - def get_id(self, entity_type, name): - return self.get(entity_type, name=name).preamble.id - - def get_name(self, entity_type, id): - return self.get(entity_type, id=id).preamble.name - - def get_alias(self, entity_type, id): - return self.get(entity_type, id=id).preamble.alias - - def __getattr__(self, attr): - # Synthesize convenience functions for name to id lookups for top-level entities - # e.g. get_tables_id(name_string) or get_actions_id(name_string) - m = re.search("^get_(\w+)_id$", attr) - if m: - primitive = m.group(1) - return lambda name: self.get_id(primitive, name) - - # Synthesize convenience functions for id to name lookups - # e.g. get_tables_name(id) or get_actions_name(id) - m = re.search("^get_(\w+)_name$", attr) - if m: - primitive = m.group(1) - return lambda id: self.get_name(primitive, id) - - raise AttributeError("%r object has no attribute %r" % (self.__class__, attr)) - - def get_match_field(self, table_name, name=None, id=None): - for t in self.p4info.tables: - pre = t.preamble - if pre.name == table_name: - for mf in t.match_fields: - if name is not None: - if mf.name == name: - return mf - elif id is not None: - if mf.id == id: - return mf - raise AttributeError("%r has no attribute %r" % (table_name, name if name is not None else id)) - - def get_match_field_id(self, table_name, match_field_name): - return self.get_match_field(table_name, name=match_field_name).id - - def get_match_field_name(self, table_name, match_field_id): - return self.get_match_field(table_name, id=match_field_id).name - - def get_match_field_pb(self, table_name, match_field_name, value): - p4info_match = self.get_match_field(table_name, match_field_name) - bitwidth = p4info_match.bitwidth - p4runtime_match = p4runtime_pb2.FieldMatch() - p4runtime_match.field_id = p4info_match.id - match_type = p4info_match.match_type - if match_type == p4info_pb2.MatchField.VALID: - valid = p4runtime_match.valid - valid.value = bool(value) - elif match_type == p4info_pb2.MatchField.EXACT: - exact = p4runtime_match.exact - exact.value = encode(value, bitwidth) - elif match_type == p4info_pb2.MatchField.LPM: - lpm = p4runtime_match.lpm - lpm.value = encode(value[0], bitwidth) - lpm.prefix_len = value[1] - elif match_type == p4info_pb2.MatchField.TERNARY: - lpm = p4runtime_match.ternary - lpm.value = encode(value[0], bitwidth) - lpm.mask = encode(value[1], bitwidth) - elif match_type == p4info_pb2.MatchField.RANGE: - lpm = p4runtime_match.range - lpm.low = encode(value[0], bitwidth) - lpm.high = encode(value[1], bitwidth) - else: - raise Exception("Unsupported match type with type %r" % match_type) - return p4runtime_match - - def get_match_field_value(self, match_field): - match_type = match_field.WhichOneof("field_match_type") - if match_type == 'valid': - return match_field.valid.value - elif match_type == 'exact': - return match_field.exact.value - elif match_type == 'lpm': - return (match_field.lpm.value, match_field.lpm.prefix_len) - elif match_type == 'ternary': - return (match_field.ternary.value, match_field.ternary.mask) - elif match_type == 'range': - return (match_field.range.low, match_field.range.high) - else: - raise Exception("Unsupported match type with type %r" % match_type) - - def get_action_param(self, action_name, name=None, id=None): - for a in self.p4info.actions: - pre = a.preamble - if pre.name == action_name: - for p in a.params: - if name is not None: - if p.name == name: - return p - elif id is not None: - if p.id == id: - return p - raise AttributeError("action %r has no param %r" % (action_name, name if name is not None else id)) - - def get_action_param_id(self, action_name, param_name): - return self.get_action_param(action_name, name=param_name).id - - def get_action_param_name(self, action_name, param_id): - return self.get_action_param(action_name, id=param_id).name - - def get_action_param_pb(self, action_name, param_name, value): - p4info_param = self.get_action_param(action_name, param_name) - p4runtime_param = p4runtime_pb2.Action.Param() - p4runtime_param.param_id = p4info_param.id - p4runtime_param.value = encode(value, p4info_param.bitwidth) - return p4runtime_param - - def buildTableEntry(self, - table_name, - match_fields={}, - action_name=None, - action_params={}): - table_entry = p4runtime_pb2.TableEntry() - table_entry.table_id = self.get_tables_id(table_name) - if match_fields: - table_entry.match.extend([ - self.get_match_field_pb(table_name, match_field_name, value) - for match_field_name, value in match_fields.iteritems() - ]) - if action_name: - action = table_entry.action.action - action.action_id = self.get_actions_id(action_name) - if action_params: - action.params.extend([ - self.get_action_param_pb(action_name, field_name, value) - for field_name, value in action_params.iteritems() - ]) - return table_entry \ No newline at end of file diff --git a/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/switch.py b/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/switch.py deleted file mode 100644 index 077698e30..000000000 --- a/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/switch.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright 2017-present Open Networking Foundation -# -# 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. -# -from abc import abstractmethod - -import grpc -from p4 import p4runtime_pb2 -from p4.tmp import p4config_pb2 - -class SwitchConnection(object): - def __init__(self, name, address='127.0.0.1:50051', device_id=0): - self.name = name - self.address = address - self.device_id = device_id - self.p4info = None - self.channel = grpc.insecure_channel(self.address) - self.client_stub = p4runtime_pb2.P4RuntimeStub(self.channel) - - @abstractmethod - def buildDeviceConfig(self, **kwargs): - return p4config_pb2.P4DeviceConfig() - - def SetForwardingPipelineConfig(self, p4info, dry_run=False, **kwargs): - device_config = self.buildDeviceConfig(**kwargs) - request = p4runtime_pb2.SetForwardingPipelineConfigRequest() - config = request.configs.add() - config.device_id = self.device_id - config.p4info.CopyFrom(p4info) - config.p4_device_config = device_config.SerializeToString() - request.action = p4runtime_pb2.SetForwardingPipelineConfigRequest.VERIFY_AND_COMMIT - if dry_run: - print "P4 Runtime SetForwardingPipelineConfig:", request - else: - self.client_stub.SetForwardingPipelineConfig(request) - - def WriteTableEntry(self, table_entry, dry_run=False): - request = p4runtime_pb2.WriteRequest() - request.device_id = self.device_id - update = request.updates.add() - update.type = p4runtime_pb2.Update.INSERT - update.entity.table_entry.CopyFrom(table_entry) - if dry_run: - print "P4 Runtime Write:", request - else: - self.client_stub.Write(request) - - def ReadTableEntries(self, table_id=None, dry_run=False): - request = p4runtime_pb2.ReadRequest() - request.device_id = self.device_id - entity = request.entities.add() - table_entry = entity.table_entry - if table_id is not None: - table_entry.table_id = table_id - else: - table_entry.table_id = 0 - if dry_run: - print "P4 Runtime Read:", request - else: - for response in self.client_stub.Read(request): - yield response - - def ReadCounters(self, counter_id=None, index=None, dry_run=False): - request = p4runtime_pb2.ReadRequest() - request.device_id = self.device_id - entity = request.entities.add() - counter_entry = entity.counter_entry - if counter_id is not None: - counter_entry.counter_id = counter_id - else: - counter_entry.counter_id = 0 - if index is not None: - counter_entry.index = index - if dry_run: - print "P4 Runtime Read:", request - else: - for response in self.client_stub.Read(request): - yield response diff --git a/P4D2_2017_Fall/exercises/p4runtime/solution/mycontroller.py b/P4D2_2017_Fall/exercises/p4runtime/solution/mycontroller.py deleted file mode 100755 index 7c1c9de05..000000000 --- a/P4D2_2017_Fall/exercises/p4runtime/solution/mycontroller.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env python2 -import argparse -import os -from time import sleep - -# NOTE: Appending to the PYTHON_PATH is only required in the `solution` directory. -# It is not required for mycontroller.py in the top-level directory. -import sys -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) - -import p4runtime_lib.bmv2 -import p4runtime_lib.helper - -SWITCH_TO_HOST_PORT = 1 -SWITCH_TO_SWITCH_PORT = 2 - -def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, - dst_eth_addr, dst_ip_addr): - ''' - Installs three rules: - 1) An tunnel ingress rule on the ingress switch in the ipv4_lpm table that - encapsulates traffic into a tunnel with the specified ID - 2) A transit rule on the ingress switch that forwards traffic based on - the specified ID - 3) An tunnel egress rule on the egress switch that decapsulates traffic - with the specified ID and sends it to the host - - :param p4info_helper: the P4Info helper - :param ingress_sw: the ingress switch connection - :param egress_sw: the egress switch connection - :param tunnel_id: the specified tunnel ID - :param dst_eth_addr: the destination IP to match in the ingress rule - :param dst_ip_addr: the destination Ethernet address to write in the - egress rule - ''' - # 1) Tunnel Ingress Rule - table_entry = p4info_helper.buildTableEntry( - table_name="ipv4_lpm", - match_fields={ - "hdr.ipv4.dstAddr": (dst_ip_addr, 32) - }, - action_name="myTunnel_ingress", - action_params={ - "dst_id": tunnel_id, - }) - ingress_sw.WriteTableEntry(table_entry) - print "Installed ingress tunnel rule on %s" % ingress_sw.name - - # 2) Tunnel Transit Rule - # The rule will need to be added to the myTunnel_exact table and match on - # the tunnel ID (hdr.myTunnel.dst_id). Traffic will need to be forwarded - # using the myTunnel_forward action on the port connected to the next switch. - # - # For our simple topology, switch 1 and switch 2 are connected using a - # link attached to port 2 on both switches. We have defined a variable at - # the top of the file, SWITCH_TO_SWITCH_PORT, that you can use as the output - # port for this action. - # - # We will only need a transit rule on the ingress switch because we are - # using a simple topology. In general, you'll need on transit rule for - # each switch in the path (except the last switch, which has the egress rule), - # and you will need to select the port dynamically for each switch based on - # your topology. - - table_entry = p4info_helper.buildTableEntry( - table_name="myTunnel_exact", - match_fields={ - "hdr.myTunnel.dst_id": tunnel_id - }, - action_name="myTunnel_forward", - action_params={ - "port": SWITCH_TO_SWITCH_PORT - }) - ingress_sw.WriteTableEntry(table_entry) - print "Installed transit tunnel rule on %s" % ingress_sw.name - - # 3) Tunnel Egress Rule - # For our simple topology, the host will always be located on the - # SWITCH_TO_HOST_PORT (port 1). - # In general, you will need to keep track of which port the host is - # connected to. - table_entry = p4info_helper.buildTableEntry( - table_name="myTunnel_exact", - match_fields={ - "hdr.myTunnel.dst_id": tunnel_id - }, - action_name="myTunnel_egress", - action_params={ - "dstAddr": dst_eth_addr, - "port": SWITCH_TO_HOST_PORT - }) - egress_sw.WriteTableEntry(table_entry) - print "Installed egress tunnel rule on %s" % egress_sw.name - -def readTableRules(p4info_helper, sw): - ''' - Reads the table entries from all tables on the switch. - - :param p4info_helper: the P4Info helper - :param sw: the switch connection - ''' - print '\n----- Reading tables rules for %s -----' % sw.name - for response in sw.ReadTableEntries(): - for entity in response.entities: - entry = entity.table_entry - # TODO For extra credit, you can use the p4info_helper to translate - # the IDs the entry to names - table_name = p4info_helper.get_tables_name(entry.table_id) - print '%s: ' % table_name, - for m in entry.match: - print p4info_helper.get_match_field_name(table_name, m.field_id), - print '%r' % (p4info_helper.get_match_field_value(m),), - action = entry.action.action - action_name = p4info_helper.get_actions_name(action.action_id) - print '->', action_name, - for p in action.params: - print p4info_helper.get_action_param_name(action_name, p.param_id), - print '%r' % p.value, - print - -def printCounter(p4info_helper, sw, counter_name, index): - ''' - Reads the specified counter at the specified index from the switch. In our - program, the index is the tunnel ID. If the index is 0, it will return all - values from the counter. - - :param p4info_helper: the P4Info helper - :param sw: the switch connection - :param counter_name: the name of the counter from the P4 program - :param index: the counter index (in our case, the tunnel ID) - ''' - for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): - for entity in response.entities: - counter = entity.counter_entry - print "%s %s %d: %d packets (%d bytes)" % ( - sw.name, counter_name, index, - counter.data.packet_count, counter.data.byte_count - ) - - -def main(p4info_file_path, bmv2_file_path): - # Instantiate a P4 Runtime helper from the p4info file - p4info_helper = p4runtime_lib.helper.P4InfoHelper(p4info_file_path) - - # Create a switch connection object for s1 and s2; - # this is backed by a P4 Runtime gRPC connection - s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection('s1', address='127.0.0.1:50051') - s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection('s2', address='127.0.0.1:50052') - - # Install the P4 program on the switches - s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) - print "Installed P4 Program using SetForwardingPipelineConfig on %s" % s1.name - s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) - print "Installed P4 Program using SetForwardingPipelineConfig on %s" % s2.name - - # Write the rules that tunnel traffic from h1 to h2 - writeTunnelRules(p4info_helper, ingress_sw=s1, egress_sw=s2, tunnel_id=100, - dst_eth_addr="00:00:00:00:02:02", dst_ip_addr="10.0.2.2") - - # Write the rules that tunnel traffic from h2 to h1 - writeTunnelRules(p4info_helper, ingress_sw=s2, egress_sw=s1, tunnel_id=200, - dst_eth_addr="00:00:00:00:01:01", dst_ip_addr="10.0.1.1") - - # TODO Uncomment the following two lines to read table entries from s1 and s2 - readTableRules(p4info_helper, s1) - readTableRules(p4info_helper, s2) - - # Print the tunnel counters every 2 seconds - try: - while True: - sleep(2) - print '\n----- Reading tunnel counters -----' - printCounter(p4info_helper, s1, "ingressTunnelCounter", 100) - printCounter(p4info_helper, s2, "egressTunnelCounter", 100) - printCounter(p4info_helper, s2, "ingressTunnelCounter", 200) - printCounter(p4info_helper, s1, "egressTunnelCounter", 200) - except KeyboardInterrupt: - print " Shutting down." - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='P4Runtime Controller') - parser.add_argument('--p4info', help='p4info proto in text format from p4c', - type=str, action="store", required=False, - default='./build/advanced_tunnel.p4info') - parser.add_argument('--bmv2-json', help='BMv2 JSON file from p4c', - type=str, action="store", required=False, - default='./build/advanced_tunnel.json') - args = parser.parse_args() - - if not os.path.exists(args.p4info): - parser.print_help() - print "\np4info file not found: %s\nHave you run 'make'?" % args.p4info - parser.exit(1) - if not os.path.exists(args.bmv2_json): - parser.print_help() - print "\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json - parser.exit(1) - - main(args.p4info, args.bmv2_json) diff --git a/P4D2_2017_Fall/exercises/source_routing/Makefile b/P4D2_2017_Fall/exercises/source_routing/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2017_Fall/exercises/source_routing/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/source_routing/README.md b/P4D2_2017_Fall/exercises/source_routing/README.md deleted file mode 100644 index 7b5f42a93..000000000 --- a/P4D2_2017_Fall/exercises/source_routing/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# Implementing Source Routing - -## Introduction - -The objective of this exercise is to implement source routing. With -source routing, the source host guides each switch in the network to -send the packet to a specific port. The host puts a stack of output -ports in the packet. In this example, we just put the stack after -Ethernet header and select a special etherType to indicate that. Each -switch pops an item from the stack and forwards the packet according -to the specified port number. - -Your switch must parse the source routing stack. Each item has a bos -(bottom of stack) bit and a port number. The bos bit is 1 only for the -last entry of stack. Then at ingress, it should pop an entry from the -stack and set the egress port accordingly. Note that the last hop can -also revert back the etherType to `TYPE_IPV4`. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`source_routing.p4`, which initially drops all packets. Your job (in -the next step) will be to extend it to properly to route packets. - -Before that, let's compile the incomplete `source_routing.p4` and -bring up a network in Mininet to test its behavior. - -1. In your shell, run: - ```bash - make - ``` - This will: - * compile `source_routing.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle, each connected to one host (`h1`, `h2`, `h3`). - Check the network topology using the `net` command in mininet. - You can also change the topology in topology.json - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc - (`10.0..`). - -2. You should now see a Mininet command prompt. Open two terminals for - `h1` and `h2`, respectively: - ```bash - mininet> xterm h1 h2 - ``` -3. Each host includes a small Python-based messaging client and - server. In `h2`'s xterm, start the server: - ```bash - ./receive.py - ``` -4. In `h1`'s xterm, send a message from the client: - ```bash - ./send.py 10.0.2.2 - ``` - -5. Type a list of port numbers. say `2 3 2 2 1`. This should send the - packet through `h1`, `s1`, `s2`, `s3`, `s1`, `s2`, and - `h2`. However, `h2` will not receive the message. - -6. Type `q` to exit send.py and type `exit` to leave each xterm and - the Mininet command line. - -The message was not received because each switch is programmed with -`source_routing.p4`, which drops all packets on arrival. You can -verify this by looking at `build/logs/s1.log`. Your job is to extend -the P4 code so packets are delivered to their destination. - -## Step 2: Implement source routing - -The `source_routing.p4` file contains a skeleton P4 program with key -pieces of logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the -missing piece. - -A complete `source_routing.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 - (`ipv4_t`) and Source Route (`srcRoute_t`). -2. **TODO:** Parsers for Ethernet and Source Route that populate - `ethernet` and `srcRoutes` fields. -3. An action to drop a packet, using `mark_to_drop()`. -4. **TODO:** An action (called `srcRoute_nhop`), which will: - 1. Set the egress port for the next hop. - 2. remove the first entry of srcRoutes -5. A control with an `apply` block that: - 1. checks the existence of source routes. - 2. **TODO:** if statement to change etherent.etherType if it is the last hop - 3. **TODO:** call srcRoute_nhop action -6. A deparser that selects the order in which fields inserted into the outgoing - packet. -7. A `package` instantiation supplied with the parser, control, and deparser. - > In general, a package also requires instances of checksum verification - > and recomputation controls. These are not necessary for this tutorial - > and are replaced with instantiations of empty controls. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, your message from `h1` -should be delivered to `h2`. - -Check the `ttl` of the IP header. Each hop decrements `ttl`. The port -sequence `2 3 2 2 1`, forces the packet to have a loop, so the `ttl` -should be 59 at `h2`. Can you find the port sequence for the shortest -path? - -### Food for thought -* Can we change the program to handle both IPv4 forwarding and source -routing at the same time? -* How would you enhance your program to let the first switch add the -path, so that source routing would be transparent to end-hosts? - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `source_routing.p4` fails to compile. In this case, `make` will - report the error emitted from the compiler and stop. -2. `source_routing.p4` compiles but switches or mininet do not start. - Do you have another instance of mininet running? Did the previous - run of mininet crash? if yes, check "Cleaning up Mininet" bellow. -3. `source_routing.p4` compiles but the switch does not process - packets in the desired way. The `/tmp/p4s..log` - files contain trace messages describing how each switch processes - each packet. The output is detailed and can help pinpoint logic - errors in your implementation. The - `build/-.pcap` also contains the pcap - of packets on each interface. Use `tcpdump -r -xxx` to - print the hexdump of the packets. - -#### Cleaning up Mininet - -In the cases above, `make` may leave a Mininet instance running in -the background. Use the following command to clean up these -instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to -[Calculator](../calc). diff --git a/P4D2_2017_Fall/exercises/source_routing/s1-commands.txt b/P4D2_2017_Fall/exercises/source_routing/s1-commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2017_Fall/exercises/source_routing/s2-commands.txt b/P4D2_2017_Fall/exercises/source_routing/s2-commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2017_Fall/exercises/source_routing/s3-commands.txt b/P4D2_2017_Fall/exercises/source_routing/s3-commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2017_Fall/utils/run_exercise.py b/P4D2_2017_Fall/utils/run_exercise.py deleted file mode 100755 index 84997d702..000000000 --- a/P4D2_2017_Fall/utils/run_exercise.py +++ /dev/null @@ -1,382 +0,0 @@ -#!/usr/bin/env python2 -# 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. -# -# Adapted by Robert MacDavid (macdavid@cs.princeton.edu) from scripts found in -# the p4app repository (https://github.com/p4lang/p4app) -# -# We encourage you to dissect this script to better understand the BMv2/Mininet -# environment used by the P4 tutorial. -# -import os, sys, json, subprocess, re, argparse -from time import sleep - -from p4_mininet import P4Switch, P4Host - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.link import TCLink -from mininet.cli import CLI - -from p4runtime_switch import P4RuntimeSwitch - -def configureP4Switch(**switch_args): - """ Helper class that is called by mininet to initialize - the virtual P4 switches. The purpose is to ensure each - switch's thrift server is using a unique port. - """ - if "sw_path" in switch_args and 'grpc' in switch_args['sw_path']: - # If grpc appears in the BMv2 switch target, we assume will start P4 Runtime - class ConfiguredP4RuntimeSwitch(P4RuntimeSwitch): - def __init__(self, *opts, **kwargs): - kwargs.update(switch_args) - P4RuntimeSwitch.__init__(self, *opts, **kwargs) - - def describe(self): - print "%s -> gRPC port: %d" % (self.name, self.grpc_port) - - return ConfiguredP4RuntimeSwitch - else: - class ConfiguredP4Switch(P4Switch): - next_thrift_port = 9090 - def __init__(self, *opts, **kwargs): - global next_thrift_port - kwargs.update(switch_args) - kwargs['thrift_port'] = ConfiguredP4Switch.next_thrift_port - ConfiguredP4Switch.next_thrift_port += 1 - P4Switch.__init__(self, *opts, **kwargs) - - def describe(self): - print "%s -> Thrift port: %d" % (self.name, self.thrift_port) - - return ConfiguredP4Switch - - -class ExerciseTopo(Topo): - """ The mininet topology class for the P4 tutorial exercises. - A custom class is used because the exercises make a few topology - assumptions, mostly about the IP and MAC addresses. - """ - def __init__(self, hosts, switches, links, log_dir, **opts): - Topo.__init__(self, **opts) - host_links = [] - switch_links = [] - self.sw_port_mapping = {} - - for link in links: - if link['node1'][0] == 'h': - host_links.append(link) - else: - switch_links.append(link) - - link_sort_key = lambda x: x['node1'] + x['node2'] - # Links must be added in a sorted order so bmv2 port numbers are predictable - host_links.sort(key=link_sort_key) - switch_links.sort(key=link_sort_key) - - for sw in switches: - self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw)) - - for link in host_links: - host_name = link['node1'] - host_sw = link['node2'] - host_num = int(host_name[1:]) - sw_num = int(host_sw[1:]) - host_ip = "10.0.%d.%d" % (sw_num, host_num) - host_mac = '00:00:00:00:%02x:%02x' % (sw_num, host_num) - # Each host IP should be /24, so all exercise traffic will use the - # default gateway (the switch) without sending ARP requests. - self.addHost(host_name, ip=host_ip+'/24', mac=host_mac) - self.addLink(host_name, host_sw, - delay=link['latency'], bw=link['bandwidth'], - addr1=host_mac, addr2=host_mac) - self.addSwitchPort(host_sw, host_name) - - for link in switch_links: - self.addLink(link['node1'], link['node2'], - delay=link['latency'], bw=link['bandwidth']) - self.addSwitchPort(link['node1'], link['node2']) - self.addSwitchPort(link['node2'], link['node1']) - - self.printPortMapping() - - def addSwitchPort(self, sw, node2): - if sw not in self.sw_port_mapping: - self.sw_port_mapping[sw] = [] - portno = len(self.sw_port_mapping[sw])+1 - self.sw_port_mapping[sw].append((portno, node2)) - - def printPortMapping(self): - print "Switch port mapping:" - for sw in sorted(self.sw_port_mapping.keys()): - print "%s: " % sw, - for portno, node2 in self.sw_port_mapping[sw]: - print "%d:%s\t" % (portno, node2), - print - - -class ExerciseRunner: - """ - Attributes: - log_dir : string // directory for mininet log files - pcap_dir : string // directory for mininet switch pcap files - quiet : bool // determines if we print logger messages - - hosts : list // list of mininet host names - switches : dict // mininet host names and their associated properties - links : list // list of mininet link properties - - switch_json : string // json of the compiled p4 example - bmv2_exe : string // name or path of the p4 switch binary - - topo : Topo object // The mininet topology instance - net : Mininet object // The mininet instance - - """ - def logger(self, *items): - if not self.quiet: - print(' '.join(items)) - - def formatLatency(self, l): - """ Helper method for parsing link latencies from the topology json. """ - if isinstance(l, (str, unicode)): - return l - else: - return str(l) + "ms" - - - def __init__(self, topo_file, log_dir, pcap_dir, - switch_json, bmv2_exe='simple_switch', quiet=False): - """ Initializes some attributes and reads the topology json. Does not - actually run the exercise. Use run_exercise() for that. - - Arguments: - topo_file : string // A json file which describes the exercise's - mininet topology. - log_dir : string // Path to a directory for storing exercise logs - pcap_dir : string // Ditto, but for mininet switch pcap files - switch_json : string // Path to a compiled p4 json for bmv2 - bmv2_exe : string // Path to the p4 behavioral binary - quiet : bool // Enable/disable script debug messages - """ - - self.quiet = quiet - self.logger('Reading topology file.') - with open(topo_file, 'r') as f: - topo = json.load(f) - self.hosts = topo['hosts'] - self.switches = topo['switches'] - self.links = self.parse_links(topo['links']) - - # Ensure all the needed directories exist and are directories - for dir_name in [log_dir, pcap_dir]: - if not os.path.isdir(dir_name): - if os.path.exists(dir_name): - raise Exception("'%s' exists and is not a directory!" % dir_name) - os.mkdir(dir_name) - self.log_dir = log_dir - self.pcap_dir = pcap_dir - self.switch_json = switch_json - self.bmv2_exe = bmv2_exe - - - def run_exercise(self): - """ Sets up the mininet instance, programs the switches, - and starts the mininet CLI. This is the main method to run after - initializing the object. - """ - # Initialize mininet with the topology specified by the config - self.create_network() - self.net.start() - sleep(1) - - # some programming that must happen after the net has started - self.program_hosts() - self.program_switches() - - # wait for that to finish. Not sure how to do this better - sleep(1) - - self.do_net_cli() - # stop right after the CLI is exited - self.net.stop() - - - def parse_links(self, unparsed_links): - """ Given a list of links descriptions of the form [node1, node2, latency, bandwidth] - with the latency and bandwidth being optional, parses these descriptions - into dictionaries and store them as self.links - """ - links = [] - for link in unparsed_links: - # make sure each link's endpoints are ordered alphabetically - s, t, = link[0], link[1] - if s > t: - s,t = t,s - - link_dict = {'node1':s, - 'node2':t, - 'latency':'0ms', - 'bandwidth':None - } - if len(link) > 2: - link_dict['latency'] = self.formatLatency(link[2]) - if len(link) > 3: - link_dict['bandwidth'] = link[3] - - if link_dict['node1'][0] == 'h': - assert link_dict['node2'][0] == 's', 'Hosts should be connected to switches, not ' + str(link_dict['node2']) - links.append(link_dict) - return links - - - def create_network(self): - """ Create the mininet network object, and store it as self.net. - - Side effects: - - Mininet topology instance stored as self.topo - - Mininet instance stored as self.net - """ - self.logger("Building mininet topology.") - - self.topo = ExerciseTopo(self.hosts, self.switches.keys(), self.links, self.log_dir) - - switchClass = configureP4Switch( - sw_path=self.bmv2_exe, - json_path=self.switch_json, - log_console=True, - pcap_dump=self.pcap_dir) - - self.net = Mininet(topo = self.topo, - link = TCLink, - host = P4Host, - switch = switchClass, - controller = None) - - - def program_switches(self): - """ If any command files were provided for the switches, - this method will start up the CLI on each switch and use the - contents of the command files as input. - - Assumes: - - A mininet instance is stored as self.net and self.net.start() has - been called. - """ - cli = 'simple_switch_CLI' - for sw_name, sw_dict in self.switches.iteritems(): - if 'cli_input' not in sw_dict: continue - # get the port for this particular switch's thrift server - sw_obj = self.net.get(sw_name) - thrift_port = sw_obj.thrift_port - - cli_input_commands = sw_dict['cli_input'] - self.logger('Configuring switch %s with file %s' % (sw_name, cli_input_commands)) - with open(cli_input_commands, 'r') as fin: - cli_outfile = '%s/%s_cli_output.log'%(self.log_dir, sw_name) - with open(cli_outfile, 'w') as fout: - subprocess.Popen([cli, '--thrift-port', str(thrift_port)], - stdin=fin, stdout=fout) - - def program_hosts(self): - """ Adds static ARP entries and default routes to each mininet host. - - Assumes: - - A mininet instance is stored as self.net and self.net.start() has - been called. - """ - for host_name in self.topo.hosts(): - h = self.net.get(host_name) - h_iface = h.intfs.values()[0] - link = h_iface.link - - sw_iface = link.intf1 if link.intf1 != h_iface else link.intf2 - # phony IP to lie to the host about - host_id = int(host_name[1:]) - sw_ip = '10.0.%d.254' % host_id - - # Ensure each host's interface name is unique, or else - # mininet cannot shutdown gracefully - h.defaultIntf().rename('%s-eth0' % host_name) - # static arp entries and default routes - h.cmd('arp -i %s -s %s %s' % (h_iface.name, sw_ip, sw_iface.mac)) - h.cmd('ethtool --offload %s rx off tx off' % h_iface.name) - h.cmd('ip route add %s dev %s' % (sw_ip, h_iface.name)) - h.setDefaultRoute("via %s" % sw_ip) - - - def do_net_cli(self): - """ Starts up the mininet CLI and prints some helpful output. - - Assumes: - - A mininet instance is stored as self.net and self.net.start() has - been called. - """ - for s in self.net.switches: - s.describe() - for h in self.net.hosts: - h.describe() - self.logger("Starting mininet CLI") - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - print('') - print('======================================================================') - print('Welcome to the BMV2 Mininet CLI!') - print('======================================================================') - print('Your P4 program is installed into the BMV2 software switch') - print('and your initial configuration is loaded. You can interact') - print('with the network using the mininet CLI below.') - print('') - if self.switch_json: - print('To inspect or change the switch configuration, connect to') - print('its CLI from your host operating system using this command:') - print(' simple_switch_CLI --thrift-port ') - print('') - print('To view a switch log, run this command from your host OS:') - print(' tail -f %s/.log' % self.log_dir) - print('') - print('To view the switch output pcap, check the pcap files in %s:' % self.pcap_dir) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap') - print('') - - CLI(self.net) - - -def get_args(): - cwd = os.getcwd() - default_logs = os.path.join(cwd, 'logs') - default_pcaps = os.path.join(cwd, 'pcaps') - parser = argparse.ArgumentParser() - parser.add_argument('-q', '--quiet', help='Suppress log messages.', - action='store_true', required=False, default=False) - parser.add_argument('-t', '--topo', help='Path to topology json', - type=str, required=False, default='./topology.json') - parser.add_argument('-l', '--log-dir', type=str, required=False, default=default_logs) - parser.add_argument('-p', '--pcap-dir', type=str, required=False, default=default_pcaps) - parser.add_argument('-j', '--switch_json', type=str, required=False) - parser.add_argument('-b', '--behavioral-exe', help='Path to behavioral executable', - type=str, required=False, default='simple_switch') - return parser.parse_args() - - -if __name__ == '__main__': - # from mininet.log import setLogLevel - # setLogLevel("info") - - args = get_args() - exercise = ExerciseRunner(args.topo, args.log_dir, args.pcap_dir, - args.switch_json, args.behavioral_exe, args.quiet) - - exercise.run_exercise() - diff --git a/P4D2_2017_Fall/vm/Vagrantfile b/P4D2_2017_Fall/vm/Vagrantfile deleted file mode 100644 index 6d34ece4a..000000000 --- a/P4D2_2017_Fall/vm/Vagrantfile +++ /dev/null @@ -1,22 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -Vagrant.configure(2) do |config| - config.vm.box = "bento/ubuntu-16.04" - config.vm.define "p4-tutorial" do |tutorial| - end - config.vm.provider "virtualbox" do |vb| - vb.name = "P4 Tutorial" + Time.now.strftime(" %Y-%m-%d") - vb.gui = true - vb.memory = 2048 - vb.cpus = 2 - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] - end - config.vm.synced_folder '.', '/vagrant', disabled: true - config.vm.hostname = "p4" - config.vm.provision "file", source: "p4-logo.png", destination: "/home/vagrant/p4-logo.png" - config.vm.provision "file", source: "p4_16-mode.el", destination: "/home/vagrant/p4_16-mode.el" - config.vm.provision "file", source: "p4.vim", destination: "/home/vagrant/p4.vim" - config.vm.provision "shell", path: "root-bootstrap.sh" - config.vm.provision "shell", privileged: false, path: "user-bootstrap.sh" -end diff --git a/P4D2_2017_Fall/vm/root-bootstrap.sh b/P4D2_2017_Fall/vm/root-bootstrap.sh deleted file mode 100755 index 0cc24a721..000000000 --- a/P4D2_2017_Fall/vm/root-bootstrap.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash - -# Print commands and exit on errors -set -xe - -sudo add-apt-repository ppa:webupd8team/sublime-text-3 -sudo add-apt-repository ppa:webupd8team/atom - -apt-get update - -DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade - -apt-get install -y --no-install-recommends \ - atom \ - autoconf \ - automake \ - bison \ - build-essential \ - ca-certificates \ - cmake \ - cpp \ - curl \ - emacs24 \ - flex \ - git \ - libboost-dev \ - libboost-filesystem-dev \ - libboost-iostreams1.58-dev \ - libboost-program-options-dev \ - libboost-system-dev \ - libboost-test-dev \ - libboost-thread-dev \ - libc6-dev \ - libevent-dev \ - libffi-dev \ - libfl-dev \ - libgc-dev \ - libgc1c2 \ - libgflags-dev \ - libgmp-dev \ - libgmp10 \ - libgmpxx4ldbl \ - libjudy-dev \ - libpcap-dev \ - libreadline6 \ - libreadline6-dev \ - libssl-dev \ - libtool \ - lubuntu-desktop \ - make \ - mktemp \ - pkg-config \ - python \ - python-dev \ - python-ipaddr \ - python-pip \ - python-scapy \ - python-setuptools \ - sublime-text-installer \ - tcpdump \ - unzip \ - vim \ - wget \ - xcscope-el \ - xterm - -useradd -m -d /home/p4 -s /bin/bash p4 -echo "p4:p4" | chpasswd -echo "p4 ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/99_p4 -chmod 440 /etc/sudoers.d/99_p4 -usermod -aG vboxsf p4 - -cd /usr/share/lubuntu/wallpapers/ -cp /home/vagrant/p4-logo.png . -rm lubuntu-default-wallpaper.png -ln -s p4-logo.png lubuntu-default-wallpaper.png -rm /home/vagrant/p4-logo.png -cd /home/vagrant -sed -i s@#background=@background=/usr/share/lubuntu/wallpapers/1604-lubuntu-default-wallpaper.png@ /etc/lightdm/lightdm-gtk-greeter.conf - -# Disable screensaver -apt-get -y remove light-locker - -# Automatically log into the P4 user -cat << EOF | tee -a /etc/lightdm/lightdm.conf.d/10-lightdm.conf -[SeatDefaults] -autologin-user=p4 -autologin-user-timeout=0 -user-session=Lubuntu -EOF diff --git a/P4D2_2017_Fall/vm/user-bootstrap.sh b/P4D2_2017_Fall/vm/user-bootstrap.sh deleted file mode 100644 index d00b3c9b9..000000000 --- a/P4D2_2017_Fall/vm/user-bootstrap.sh +++ /dev/null @@ -1,171 +0,0 @@ -#!/bin/bash - -# Print script commands. -set -x -# Exit on errors. -set -e - -BMV2_COMMIT="ae84c2f6d5bc3dd6873a62e351f26c39038804da" -PI_COMMIT="f06a4df7d56413849dbe9ab8f9441321ff140bca" -P4C_COMMIT="3ad8d93f334a34d181e8d9d83100d797bac3f65a" -PROTOBUF_COMMIT="tags/v3.0.2" -GRPC_COMMIT="tags/v1.3.0" - -NUM_CORES=`grep -c ^processor /proc/cpuinfo` - -# Mininet -git clone git://github.com/mininet/mininet mininet -cd mininet -sudo ./util/install.sh -nwv -cd .. - -# Protobuf -git clone https://github.com/google/protobuf.git -cd protobuf -git checkout ${PROTOBUF_COMMIT} -export CFLAGS="-Os" -export CXXFLAGS="-Os" -export LDFLAGS="-Wl,-s" -./autogen.sh -./configure --prefix=/usr -make -j${NUM_CORES} -sudo make install -sudo ldconfig -unset CFLAGS CXXFLAGS LDFLAGS -cd .. - -# gRPC -git clone https://github.com/grpc/grpc.git -cd grpc -git checkout ${GRPC_COMMIT} -git submodule update --init -export LDFLAGS="-Wl,-s" -make -j${NUM_CORES} -sudo make install -sudo ldconfig -unset LDFLAGS -cd .. -# Install gRPC Python Package -sudo pip install grpcio - -# BMv2 deps (needed by PI) -git clone https://github.com/p4lang/behavioral-model.git -cd behavioral-model -git checkout ${BMV2_COMMIT} -# From bmv2's install_deps.sh, we can skip apt-get install. -# Nanomsg is required by p4runtime, p4runtime is needed by BMv2... -tmpdir=`mktemp -d -p .` -cd ${tmpdir} -bash ../travis/install-thrift.sh -bash ../travis/install-nanomsg.sh -sudo ldconfig -bash ../travis/install-nnpy.sh -cd .. -sudo rm -rf $tmpdir -cd .. - -# PI/P4Runtime -git clone https://github.com/p4lang/PI.git -cd PI -git checkout ${PI_COMMIT} -git submodule update --init --recursive -./autogen.sh -./configure --with-proto -make -j${NUM_CORES} -sudo make install -sudo ldconfig -cd .. - -# Bmv2 -cd behavioral-model -./autogen.sh -./configure --enable-debugger --with-pi -make -j${NUM_CORES} -sudo make install -sudo ldconfig -# Simple_switch_grpc target -cd targets/simple_switch_grpc -./autogen.sh -./configure -make -j${NUM_CORES} -sudo make install -sudo ldconfig -cd .. -cd .. -cd .. - -# P4C -git clone https://github.com/p4lang/p4c -cd p4c -git checkout ${P4C_COMMIT} -git submodule update --init --recursive -mkdir -p build -cd build -cmake .. -make -j${NUM_CORES} -sudo make install -sudo ldconfig -cd .. -cd .. - -# Tutorials -sudo pip install crcmod -git clone https://github.com/p4lang/tutorials -sudo mv tutorials /home/p4 -sudo chown -R p4:p4 /home/p4/tutorials - -# Emacs -sudo cp p4_16-mode.el /usr/share/emacs/site-lisp/ -echo "(add-to-list 'auto-mode-alist '(\"\\.p4\\'\" . p4_16-mode))" | sudo tee /home/p4/.emacs -sudo chown p4:p4 /home/p4/.emacs - -# Vim -cd /home/vagrant -mkdir .vim -cd .vim -mkdir ftdetect -mkdir syntax -echo "au BufRead,BufNewFile *.p4 set filetype=p4" >> ftdetect/p4.vim -echo "set bg=dark" >> /home/p4/.vimrc -cp /home/vagrant/p4.vim syntax/p4.vim -cd /home/vagrant -sudo mv .vim /home/p4/.vim -sudo chown -R p4:p4 /home/p4/.vim -sudo chown p4:p4 /home/p4/.vimrc - -# Adding Desktop icons -DESKTOP=/home/${USER}/Desktop -mkdir -p ${DESKTOP} - -cat > ${DESKTOP}/Terminal << EOF -[Desktop Entry] -Encoding=UTF-8 -Type=Application -Name=Terminal -Name[en_US]=Terminal -Icon=konsole -Exec=/usr/bin/x-terminal-emulator -Comment[en_US]= -EOF - -cat > ${DESKTOP}/Wireshark << EOF -[Desktop Entry] -Encoding=UTF-8 -Type=Application -Name=Wireshark -Name[en_US]=Wireshark -Icon=wireshark -Exec=/usr/bin/wireshark -Comment[en_US]= -EOF - -cat > ${DESKTOP}/Sublime\ Text << EOF -[Desktop Entry] -Encoding=UTF-8 -Type=Application -Name=Sublime Text -Name[en_US]=Sublime Text -Icon=sublime-text -Exec=/opt/sublime_text/sublime_text -Comment[en_US]= -EOF diff --git a/P4D2_2017_Spring/exercises/README.md b/P4D2_2017_Spring/exercises/README.md deleted file mode 100644 index 50cbc3eb4..000000000 --- a/P4D2_2017_Spring/exercises/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# P4 Tutorial - -## Introduction - -Welcome to the P4 Tutorial! - -We've prepared a set of four exercises that will help you get started -with P4 programming: - -1. [L3 forwarding](./ipv4_forward) - -2. [Multi-Hop Route Inspection](./mri) - -3. [ARP/ICMP Responder](./arp) - -4. [Calculator](./calc) - -## Obtaining required software - -If you are starting this tutorial as part of the P4 Developer Day, then -we've already provided you with a virtual machine that has all of the -required software installed. - -Otherwise, to complete the exercises, you will need to clone two p4lang Github repositories -and install their dependencies. To clone 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 -``` - - -You will also need to install `mininet`, as well as the following Python -packages: `scapy`, `thrift` (>= 0.9.2) and `networkx`. On Ubuntu, it would look -like this: - -``` -$ sudo apt-get install mininet -$ sudo pip install scapy thrift networkx -``` - diff --git a/P4D2_2017_Spring/exercises/arp/README.md b/P4D2_2017_Spring/exercises/arp/README.md deleted file mode 100644 index 1160e3df2..000000000 --- a/P4D2_2017_Spring/exercises/arp/README.md +++ /dev/null @@ -1,174 +0,0 @@ -# Implementing an ARP/ICMP Responder - -## Introduction - -This exercise extends the [IPv4 Forwarding](../ipv4_forward) program to -allow your switches to respond to ARP and ICMP requests. Once implemented, -your hosts will be able to `ping` other hosts connected to the switch, and -have the switch respond. - -This exercise makes several simplifying assumptions: - -1. The network topology contains exactly one switch and two hosts. -1. ARP and ICMP requests to the hosts are ignored; only requests sent to the - switch receive responses. - -Implementing the full functionality of ARP and ICMP is straightforward but -beyond the scope of this tutorial and left as an exercise to the reader. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`arp.p4`, which initially drops all packets. Your job will be to -extend it to reply to ARP and ICMP requests. - -As a first step, compile the incomplete `arp.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `arp.p4`, and - * start a Mininet instance with one switch (`s1`) connected to two hosts (`h1`, `h2`). - * The hosts are assigned IPs of `10.0.1.10` and `10.0.2.10`. - -2. Once the P4 source compiles without error, you can test your program at the -mininet prompt using the `ping` utility. - - ``` mininet> h1 ping h2 ``` - - Once the program is implemented correctly, you will see a response to the - ping in the mininet window. - -3. Type `exit` in the mininet terminal to exit. - - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules governing packet -processing are inserted into the pipeline by the control plane. When a rule -matches a packet, its action is invoked with parameters supplied by the control -plane as part of the rule. - -In this exercise, the control plane logic has already been implemented. As -part of bringing up the Mininet instance, the `run.sh` script will install -packet-processing rules in the tables of each switch. These are defined in the -`simple_router.config` file. - - -## Step 2: Implement ARP/ICMP Replies - -In this exercise, we are using entries in the `ipv4_lpm` table as a -database, which we can reference when responding to ARP and ICMP requests. -Without the proper entries in the table, the solution will not work. - -From a high-level, the task involves implementing two main components: ARP -replies and ICMP replies. - -### ARP Reply - -When the switch receives and ARP request asking to resolve the switch's IP -address, it will need to perform the following actions: - -1. Swap the source and destination MAC addresses in the Ethernet header, -1. set the ARP operation to ARP_REPLY (`2`) in the ARP header, -1. update the sender hardware address (SHA) and sender protocol address (SPA) in the ARP header to -be the MAC and IP addresses of the switch, and -1. set the target hardware address (THA) and target protocol address (TPA) to be the SHA -and SPA of the arriving ARP packet. - -### ICMP Reply - -When the switch receives and ICMP request containing the switch's IP and MAC -addresses, it will need to perform the following actions: - -1. Swap the source and destination MAC addresses in the Ethernet header, -1. swap the source and destination IP addresses in the ICMP header, and -1. set the type field in the ICMP header to `ICMP_ECHO_REPLY` (`0`). -1. To simplify the exercise, we can ignore the checksum by setting to checksum - field to 0. - - -We have provided a skeleton `arp.p4` file to get you started. In this -file, places to modify are marked by `TODO`. - -There are, of course, different possible solutions. We describe one approach -below. It builds on the [IPv4 Forwarding](../ipv4_forward) solution, which -used the table `ipv4_lpm` for L3 forwarding, by adding a second table named -`forward`, which checks if a packet is an ARP or ICMP packet and invokes -actions to send an ARP reply, forward an IPv4 packet, or send an ICMP reply. - -Broadly speaking, a complete solution will contain the following components: - -1. Header type definitions for `ethernet_t`, `arp_t`, `ipv4_t`, and `icmp_t`. - -1. A structure (named `my_metadata_t` in `arp.p4`) with metadata fields for the -packet's souce and destination MAC addresses, IPv4 address, egress port, as -well as a hard-coded MAC address for the switch. - -1. **TODO:** Parsers for Ethernet, ARP, IPv4, and ICMP packet header types. - -1. **TODO:** A control type declaration for ingress processing, containing: - - 1. An action for `drop`. - - 1. An action (named `set_dst_info`) to store information in the metadata - structure, rather than immediately writing to the packet header. - - 1. A table (named `ipv4_lpm`) that will match on the destination IP address - and invoke the `set_dst_info` action. - - 1. An action to send an ICMP reply. - - 1. An action to send an ARP reply. - - 1. A table (named `forward`) that will forward IPv4 packets, send an ARP - reply, send an ICMP reply, or drop a packet. - - 1. An `apply` block that implements the control logic to invoke the two - tables. - -1. A deparser that emits headers in the proper order. - -To keep the exercise simple, we will ignore the `ipv4_checksum`. You should not -need any control plane rules for this exercise. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, you should be able to -successfully `ping` the switch. - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `arp.p4` fails to compile. In this case, `run.sh` will report the -error emitted from the compiler and stop. - -1. `arp.p4` compiles, but the switch does not process packets in the desired -way. The `build/logs/.log` files contain trace messages -describing how each switch processes each packet. The output is detailed and -can help pinpoint logic errors in your implementation. - -> Note that there are no control plane rules installed in this example, and so -> the `receive.py` and `send.py` scripts from the [IPv4 -> Forwarding](../ipv4_forward) example will not work. - -#### Cleaning up Mininet - -In the latter case above, `run.sh` may leave a Mininet instance running in -the background. Use the following command to clean up these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to the next exercise: -turning your switch into a [Calculator](../calc). diff --git a/P4D2_2017_Spring/exercises/arp/arp.p4 b/P4D2_2017_Spring/exercises/arp/arp.p4 deleted file mode 100644 index c8ae98038..000000000 --- a/P4D2_2017_Spring/exercises/arp/arp.p4 +++ /dev/null @@ -1,243 +0,0 @@ -/* -*- P4_16 -*- */ - -#include -#include - -/************************************************************************* - *********************** C O N S T A N T S ***************************** - *************************************************************************/ - /* Define the useful global constants for your program */ -const bit<16> ETHERTYPE_IPV4 = 0x0800; -const bit<16> ETHERTYPE_ARP = 0x0806; -const bit<8> IPPROTO_ICMP = 0x01; - -/************************************************************************* - *********************** H E A D E R S ********************************* - *************************************************************************/ - /* Define the headers the program will recognize */ - -/* - * Standard ethernet header - */ -typedef bit<48> mac_addr_t; -typedef bit<32> ipv4_addr_t; -typedef bit<9> port_id_t; - -header ethernet_t { - mac_addr_t dstAddr; - mac_addr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ipv4_addr_t srcAddr; - ipv4_addr_t dstAddr; -} - -const bit<16> ARP_HTYPE_ETHERNET = 0x0001; -const bit<16> ARP_PTYPE_IPV4 = 0x0800; -const bit<8> ARP_HLEN_ETHERNET = 6; -const bit<8> ARP_PLEN_IPV4 = 4; -const bit<16> ARP_OPER_REQUEST = 1; -const bit<16> ARP_OPER_REPLY = 2; - -header arp_t { - bit<16> htype; - bit<16> ptype; - bit<8> hlen; - bit<8> plen; - bit<16> oper; -} - -header arp_ipv4_t { - mac_addr_t sha; - ipv4_addr_t spa; - mac_addr_t tha; - ipv4_addr_t tpa; -} - -const bit<8> ICMP_ECHO_REQUEST = 8; -const bit<8> ICMP_ECHO_REPLY = 0; - -header icmp_t { - bit<8> type; - bit<8> code; - bit<16> checksum; -} - -/* Assemble headers in a single struct */ -struct my_headers_t { - ethernet_t ethernet; - arp_t arp; - arp_ipv4_t arp_ipv4; - ipv4_t ipv4; - icmp_t icmp; -} - -/************************************************************************* - *********************** M E T A D A T A ******************************* - *************************************************************************/ - /* Define the global metadata for your program */ - -struct my_metadata_t { - ipv4_addr_t dst_ipv4; - mac_addr_t mac_da; - mac_addr_t mac_sa; - port_id_t egress_port; - mac_addr_t my_mac; -} - -/************************************************************************* - *********************** P A R S E R *********************************** - *************************************************************************/ -parser MyParser( - packet_in packet, - out my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) -{ - state start { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - ETHERTYPE_IPV4 : parse_ipv4; - ETHERTYPE_ARP : parse_arp; - default : accept; - } - } - - state parse_arp { - /* - * TODO: parse ARP. If the ARP protocol field is - * IPV4, transtion to parse_arp_ipv4 - */ - transition accept; - } - - state parse_arp_ipv4 { - /* - * TODO: parse ARP_IPV4. Hint: one - * possible solution is to store the - * target packet address in a meta data - * field when parsing. - */ - transition accept; - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - meta.dst_ipv4 = hdr.ipv4.dstAddr; - transition select(hdr.ipv4.protocol) { - IPPROTO_ICMP : parse_icmp; - default : accept; - } - } - - state parse_icmp { - /* TODO: parse ICMP */ - transition accept; - } -} - -/************************************************************************* - ************ C H E C K S U M V E R I F I C A T I O N ************* - *************************************************************************/ -control MyVerifyChecksum( - inout my_headers_t hdr, - inout my_metadata_t meta) -{ - apply { } -} - -/************************************************************************* - ************** I N G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyIngress( - inout my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) -{ - action drop() { - mark_to_drop(); - exit; - } - - /* TODO: Define actions and tables here */ - - - action set_dst_info(mac_addr_t mac_da, - mac_addr_t mac_sa, - port_id_t egress_port) - { - /* - * TODO: add logic to store mac addresses and - * egress ports in meta data - */ - } - - - table ipv4_lpm { - key = { meta.dst_ipv4 : lpm; } - actions = { set_dst_info; drop; } - default_action = drop(); - } - - - apply { - meta.my_mac = 0x000102030405; - ipv4_lpm.apply(); - /* TODO: add contol logic */ - } -} - -/************************************************************************* - **************** E G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyEgress( - inout my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* - ************* C H E C K S U M C O M P U T A T I O N ************** - *************************************************************************/ -control MyComputeChecksum( - inout my_headers_t hdr, - inout my_metadata_t meta) -{ - - apply { } -} - - -/************************************************************************* - *********************** D E P A R S E R ******************************* - *************************************************************************/ -control MyDeparser( - packet_out packet, - in my_headers_t hdr) -{ - apply { - /* TODO: Implement deparser */ - } -} - -V1Switch( - MyParser(), - MyVerifyChecksum(), - MyIngress(), - MyEgress(), - MyComputeChecksum(), - MyDeparser() -) main; diff --git a/P4D2_2017_Spring/exercises/arp/p4app.json b/P4D2_2017_Spring/exercises/arp/p4app.json deleted file mode 100644 index 8579ccd1e..000000000 --- a/P4D2_2017_Spring/exercises/arp/p4app.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "program": "arp.p4", - "language": "p4-16", - "targets": { - "mininet": { - "num-hosts": 2, - "switch-config": "simple_router.config" - } - } -} diff --git a/P4D2_2017_Spring/exercises/arp/run.sh b/P4D2_2017_Spring/exercises/arp/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/P4D2_2017_Spring/exercises/arp/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/P4D2_2017_Spring/exercises/arp/simple_router.config b/P4D2_2017_Spring/exercises/arp/simple_router.config deleted file mode 100644 index 0229c8d7b..000000000 --- a/P4D2_2017_Spring/exercises/arp/simple_router.config +++ /dev/null @@ -1 +0,0 @@ -table_add ipv4_lpm set_dst_info 10.0.1.10/24 => 00:00:01:00:00:01 00:00:02:00:00:02 1 diff --git a/P4D2_2017_Spring/exercises/arp/solution/arp.p4 b/P4D2_2017_Spring/exercises/arp/solution/arp.p4 deleted file mode 100644 index 1fe7f14f8..000000000 --- a/P4D2_2017_Spring/exercises/arp/solution/arp.p4 +++ /dev/null @@ -1,305 +0,0 @@ -/* -*- P4_16 -*- */ - -#include -#include - -/************************************************************************* - *********************** C O N S T A N T S ***************************** - *************************************************************************/ - /* Define the useful global constants for your program */ -const bit<16> ETHERTYPE_IPV4 = 0x0800; -const bit<16> ETHERTYPE_ARP = 0x0806; -const bit<8> IPPROTO_ICMP = 0x01; - -/************************************************************************* - *********************** H E A D E R S ********************************* - *************************************************************************/ - /* Define the headers the program will recognize */ - -/* - * Standard ethernet header - */ -typedef bit<48> mac_addr_t; -typedef bit<32> ipv4_addr_t; -typedef bit<9> port_id_t; - -header ethernet_t { - mac_addr_t dstAddr; - mac_addr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ipv4_addr_t srcAddr; - ipv4_addr_t dstAddr; -} - -const bit<16> ARP_HTYPE_ETHERNET = 0x0001; -const bit<16> ARP_PTYPE_IPV4 = 0x0800; -const bit<8> ARP_HLEN_ETHERNET = 6; -const bit<8> ARP_PLEN_IPV4 = 4; -const bit<16> ARP_OPER_REQUEST = 1; -const bit<16> ARP_OPER_REPLY = 2; - -header arp_t { - bit<16> htype; - bit<16> ptype; - bit<8> hlen; - bit<8> plen; - bit<16> oper; -} - -header arp_ipv4_t { - mac_addr_t sha; - ipv4_addr_t spa; - mac_addr_t tha; - ipv4_addr_t tpa; -} - -const bit<8> ICMP_ECHO_REQUEST = 8; -const bit<8> ICMP_ECHO_REPLY = 0; - -header icmp_t { - bit<8> type; - bit<8> code; - bit<16> checksum; -} - -/* Assemble headers in a single struct */ -struct my_headers_t { - ethernet_t ethernet; - arp_t arp; - arp_ipv4_t arp_ipv4; - ipv4_t ipv4; - icmp_t icmp; -} - -/************************************************************************* - *********************** M E T A D A T A ******************************* - *************************************************************************/ - /* Define the global metadata for your program */ - -struct my_metadata_t { - ipv4_addr_t dst_ipv4; - mac_addr_t mac_da; - mac_addr_t mac_sa; - port_id_t egress_port; - mac_addr_t my_mac; -} - -/************************************************************************* - *********************** P A R S E R *********************************** - *************************************************************************/ -parser MyParser( - packet_in packet, - out my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) -{ - state start { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - ETHERTYPE_IPV4 : parse_ipv4; - ETHERTYPE_ARP : parse_arp; - default : accept; - } - } - - state parse_arp { - packet.extract(hdr.arp); - transition select(hdr.arp.htype, hdr.arp.ptype, - hdr.arp.hlen, hdr.arp.plen) { - (ARP_HTYPE_ETHERNET, ARP_PTYPE_IPV4, - ARP_HLEN_ETHERNET, ARP_PLEN_IPV4) : parse_arp_ipv4; - default : accept; - } - } - - state parse_arp_ipv4 { - packet.extract(hdr.arp_ipv4); - meta.dst_ipv4 = hdr.arp_ipv4.tpa; - transition accept; - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - meta.dst_ipv4 = hdr.ipv4.dstAddr; - transition select(hdr.ipv4.protocol) { - IPPROTO_ICMP : parse_icmp; - default : accept; - } - } - - state parse_icmp { - packet.extract(hdr.icmp); - transition accept; - } -} - -/************************************************************************* - ************ C H E C K S U M V E R I F I C A T I O N ************* - *************************************************************************/ -control MyVerifyChecksum( - inout my_headers_t hdr, - inout my_metadata_t meta) -{ - apply { } -} - -/************************************************************************* - ************** I N G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyIngress( - inout my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) -{ - action drop() { - mark_to_drop(); - exit; - } - - action set_dst_info(mac_addr_t mac_da, - mac_addr_t mac_sa, - port_id_t egress_port) - { - meta.mac_da = mac_da; - meta.mac_sa = mac_sa; - meta.egress_port = egress_port; - } - - table ipv4_lpm { - key = { meta.dst_ipv4 : lpm; } - actions = { set_dst_info; drop; } - default_action = drop(); - } - - action forward_ipv4() { - hdr.ethernet.dstAddr = meta.mac_da; - hdr.ethernet.srcAddr = meta.mac_sa; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - - standard_metadata.egress_spec = meta.egress_port; - } - - action send_arp_reply() { - hdr.ethernet.dstAddr = hdr.arp_ipv4.sha; - hdr.ethernet.srcAddr = meta.mac_da; - - hdr.arp.oper = ARP_OPER_REPLY; - - hdr.arp_ipv4.tha = hdr.arp_ipv4.sha; - hdr.arp_ipv4.tpa = hdr.arp_ipv4.spa; - hdr.arp_ipv4.sha = meta.mac_da; - hdr.arp_ipv4.spa = meta.dst_ipv4; - - standard_metadata.egress_spec = standard_metadata.ingress_port; - } - - action send_icmp_reply() { - mac_addr_t tmp_mac; - ipv4_addr_t tmp_ip; - - tmp_mac = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = hdr.ethernet.srcAddr; - hdr.ethernet.srcAddr = tmp_mac; - - tmp_ip = hdr.ipv4.dstAddr; - hdr.ipv4.dstAddr = hdr.ipv4.srcAddr; - hdr.ipv4.srcAddr = tmp_ip; - - hdr.icmp.type = ICMP_ECHO_REPLY; - hdr.icmp.checksum = 0; // For now - - standard_metadata.egress_spec = standard_metadata.ingress_port; - } - - table forward { - key = { - hdr.arp.isValid() : exact; - hdr.arp.oper : ternary; - hdr.arp_ipv4.isValid() : exact; - hdr.ipv4.isValid() : exact; - hdr.icmp.isValid() : exact; - hdr.icmp.type : ternary; - } - actions = { - forward_ipv4; - send_arp_reply; - send_icmp_reply; - drop; - } - const default_action = drop(); - const entries = { - ( true, ARP_OPER_REQUEST, true, false, false, _ ) : - send_arp_reply(); - ( false, _, false, true, false, _ ) : - forward_ipv4(); - ( false, _, false, true, true, ICMP_ECHO_REQUEST ) : - send_icmp_reply(); - } - } - - apply { - meta.my_mac = 0x000102030405; - ipv4_lpm.apply(); - forward.apply(); - } -} - -/************************************************************************* - **************** E G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyEgress( - inout my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* - ************* C H E C K S U M C O M P U T A T I O N ************** - *************************************************************************/ -control MyComputeChecksum( - inout my_headers_t hdr, - inout my_metadata_t meta) -{ - apply { } -} - -/************************************************************************* - *********************** D E P A R S E R ******************************* - *************************************************************************/ -control MyDeparser( - packet_out packet, - in my_headers_t hdr) -{ - apply { - packet.emit(hdr.ethernet); - /* ARP Case */ - packet.emit(hdr.arp); - packet.emit(hdr.arp_ipv4); - /* IPv4 case */ - packet.emit(hdr.ipv4); - packet.emit(hdr.icmp); - } -} - -V1Switch( - MyParser(), - MyVerifyChecksum(), - MyIngress(), - MyEgress(), - MyComputeChecksum(), - MyDeparser() -) main; diff --git a/P4D2_2017_Spring/exercises/calc/README.md b/P4D2_2017_Spring/exercises/calc/README.md deleted file mode 100644 index c31e60269..000000000 --- a/P4D2_2017_Spring/exercises/calc/README.md +++ /dev/null @@ -1,115 +0,0 @@ -# Implementing a P4 Calculator - -## Introduction - -The objective of this tutorial is to implement a basic calculator -using a custom protocol header written in P4. The header will contain -an operation to perform and two operands. When a switch receives a -calculator packet header, it will execute the operation on the -operands, and return the result to the sender. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`calc.p4`, which initially drops all packets. Your job will be to -extend it to properly implement the calculator logic. - - -As a first step, compile the incomplete `calc.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `calc.p4`, and - * start a Mininet instance with one switches (`s1`) connected to two hosts (`h1`, `h2`). - * The hosts are assigned IPs of `10.0.1.10` and `10.0.2.10`. - - -2. We've written a small Python-based driver program that will allow you -to test your calculator. You can run the driver program directly from the -Mininet command prompt: - -``` -mininet> h1 python calc.py -> -``` - -3. The driver program will provide a new prompt, at which you can type -basic expressions. The test harness will parse your expression, and prepare -a packet with the corresponding operator and operands. It will then send a packet -to the switch for evaluation. When the switch returns the result of the computation, -the test program will print the result. However, because the calculator program -is not implemented, you should see an error message. - -``` -> 1+1 -Didn't receive response -> -``` - - - -## Step 2: Implement Calculator - -To implement the calculator, you will need to define a custom -calculator header, and implement the switch logic to parse header, -perform the requested operation, write the result in the header, and -return the packet to the sender. - -We will use the following header format: - - - 0 1 2 3 - +----------------+----------------+----------------+---------------+ - | P | 4 | Version | Op | - +----------------+----------------+----------------+---------------+ - | Operand A | - +----------------+----------------+----------------+---------------+ - | Operand B | - +----------------+----------------+----------------+---------------+ - | Result | - +----------------+----------------+----------------+---------------+ - - -- P is an ASCII Letter 'P' (0x50) -- 4 is an ASCII Letter '4' (0x34) -- Version is currently 0.1 (0x01) -- Op is an operation to Perform: - - '+' (0x2b) Result = OperandA + OperandB - - '-' (0x2d) Result = OperandA - OperandB - - '&' (0x26) Result = OperandA & OperandB - - '|' (0x7c) Result = OperandA | OperandB - - '^' (0x5e) Result = OperandA ^ OperandB - - -We will assume that the calculator header is carried over Ethernet, and -we will use the Ethernet type 0x1234 to indicate the presence of the header. - - -Given what you have learned so far, your task is to implement the -P4 calculator program. There is no control plane logic, so you need -only worry about the data plane implementation. - - -A working calculator implementation will parse the custom headers, -execute the mathematical operation, write the result in the result field, -and return the packet to the sender. - - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, you should see the -correct result: - -``` -> 1+1 -2 -> -``` - -If all of this works well. Congratulations! You have finished this -tutorial. - diff --git a/P4D2_2017_Spring/exercises/calc/calc.p4 b/P4D2_2017_Spring/exercises/calc/calc.p4 deleted file mode 100644 index 63e0dea77..000000000 --- a/P4D2_2017_Spring/exercises/calc/calc.p4 +++ /dev/null @@ -1,127 +0,0 @@ -/* -*- P4_16 -*- */ - -/* - * P4 Calculator - * - * This program implements a simple protocol. It can be carried over Ethernet - * (Ethertype 0x1234). - * - * The Protocol header looks like this: - * - * 0 1 2 3 - * +----------------+----------------+----------------+---------------+ - * | P | 4 | Version | Op | - * +----------------+----------------+----------------+---------------+ - * | Operand A | - * +----------------+----------------+----------------+---------------+ - * | Operand B | - * +----------------+----------------+----------------+---------------+ - * | Result | - * +----------------+----------------+----------------+---------------+ - * - * P is an ASCII Letter 'P' (0x50) - * 4 is an ASCII Letter '4' (0x34) - * Version is currently 0.1 (0x01) - * Op is an operation to Perform: - * '+' (0x2b) Result = OperandA + OperandB - * '-' (0x2d) Result = OperandA - OperandB - * '&' (0x26) Result = OperandA & OperandB - * '|' (0x7c) Result = OperandA | OperandB - * '^' (0x5e) Result = OperandA ^ OperandB - * - * The device receives a packet, performs the requested operation, fills in the - * result and sends the packet back out of the same port it came in on, while - * swapping the source and destination addresses. - * - * If an unknown operation is specified or the header is not valid, the packet - * is dropped - */ - -#include -#include - -/* - * Define the headers the program will recognize - */ - - -struct my_headers_t { - /* TODO: fill this in */ -} - -struct my_metadata_t { - /* In our case it is empty */ -} - -/************************************************************************* - *********************** P A R S E R *********************************** - *************************************************************************/ -parser MyParser( - packet_in packet, - out my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) -{ - state start { transition accept; } -} - -/************************************************************************* - ************ C H E C K S U M V E R I F I C A T I O N ************* - *************************************************************************/ -control MyVerifyChecksum( - inout my_headers_t hdr, - inout my_metadata_t meta) -{ - apply { - } -} - -/************************************************************************* - ************** I N G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyIngress( - inout my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) -{ - apply { } -} - -/************************************************************************* - **************** E G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyEgress( - inout my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* - ************* C H E C K S U M C O M P U T A T I O N ************** - *************************************************************************/ -control MyComputeChecksum( - inout my_headers_t hdr, - inout my_metadata_t meta) -{ - apply { } -} - -/************************************************************************* - *********************** D E P A R S E R ******************************* - *************************************************************************/ -control MyDeparser( - packet_out packet, - in my_headers_t hdr) -{ - apply { } -} - -V1Switch( - MyParser(), - MyVerifyChecksum(), - MyIngress(), - MyEgress(), - MyComputeChecksum(), - MyDeparser() -) main; diff --git a/P4D2_2017_Spring/exercises/calc/calc.py b/P4D2_2017_Spring/exercises/calc/calc.py deleted file mode 100755 index 9136831f3..000000000 --- a/P4D2_2017_Spring/exercises/calc/calc.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys -import socket -import random -import struct -import re - -from scapy.all import sendp, send, srp1 -from scapy.all import Packet, hexdump -from scapy.all import Ether, StrFixedLenField, XByteField, IntField -from scapy.all import bind_layers - -class P4calc(Packet): - name = "P4calc" - fields_desc = [ StrFixedLenField("P", "P", length=1), - StrFixedLenField("Four", "4", length=1), - XByteField("version", 0x01), - StrFixedLenField("op", "+", length=1), - IntField("operand_a", 0), - IntField("operand_b", 0), - IntField("result", 0xDEADBABE)] - -bind_layers(Ether, P4calc, type=0x1234) - -class NumParseError(Exception): - pass - -class OpParseError(Exception): - pass - -class Token: - def __init__(self,type,value = None): - self.type = type - self.value = value - -def num_parser(s, i, ts): - pattern = "^\s*([0-9]+)\s*" - match = re.match(pattern,s[i:]) - if match: - ts.append(Token('num', match.group(1))) - return i + match.end(), ts - raise NumParseError('Expected number literal.') - - -def op_parser(s, i, ts): - pattern = "^\s*([-+&|^])\s*" - match = re.match(pattern,s[i:]) - if match: - ts.append(Token('num', match.group(1))) - return i + match.end(), ts - raise NumParseError("Expected binary operator '-', '+', '&', '|', or '^'.") - - -def make_seq(p1, p2): - def parse(s, i, ts): - i,ts2 = p1(s,i,ts) - return p2(s,i,ts2) - return parse - - -def main(): - - p = make_seq(num_parser, make_seq(op_parser,num_parser)) - s = '' - iface = 'h1-eth0' - - while True: - s = str(raw_input('> ')) - if s == "quit": - break - print s - try: - i,ts = p(s,0,[]) - pkt = Ether(dst='00:04:00:00:00:00', type=0x1234) / P4calc(op=ts[1].value, - operand_a=int(ts[0].value), - operand_b=int(ts[2].value)) - - #pkt.show() - resp = srp1(pkt, iface=iface, timeout=1, verbose=False) - if resp: - p4calc=resp[P4calc] - if p4calc: - print p4calc.result - else: - print "cannot find P4calc header in the packet" - else: - print "Didn't receive response" - except Exception as error: - print error - - -if __name__ == '__main__': - main() diff --git a/P4D2_2017_Spring/exercises/calc/p4app.json b/P4D2_2017_Spring/exercises/calc/p4app.json deleted file mode 100644 index c81fdac46..000000000 --- a/P4D2_2017_Spring/exercises/calc/p4app.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "program": "calc.p4", - "language": "p4-16", - "targets": { - "mininet": { - "num-hosts": 2, - "switch-config": "simple_router.config" - } - } -} diff --git a/P4D2_2017_Spring/exercises/calc/run.sh b/P4D2_2017_Spring/exercises/calc/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/P4D2_2017_Spring/exercises/calc/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/P4D2_2017_Spring/exercises/calc/simple_router.config b/P4D2_2017_Spring/exercises/calc/simple_router.config deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2017_Spring/exercises/calc/solution/calc.p4 b/P4D2_2017_Spring/exercises/calc/solution/calc.p4 deleted file mode 100644 index d03b129f8..000000000 --- a/P4D2_2017_Spring/exercises/calc/solution/calc.p4 +++ /dev/null @@ -1,263 +0,0 @@ -/* -*- P4_16 -*- */ - -/* - * P4 Calculator - * - * This program implements a simple protocol. It can be carried over Ethernet - * (Ethertype 0x1234). - * - * The Protocol header looks like this: - * - * 0 1 2 3 - * +----------------+----------------+----------------+---------------+ - * | P | 4 | Version | Op | - * +----------------+----------------+----------------+---------------+ - * | Operand A | - * +----------------+----------------+----------------+---------------+ - * | Operand B | - * +----------------+----------------+----------------+---------------+ - * | Result | - * +----------------+----------------+----------------+---------------+ - * - * P is an ASCII Letter 'P' (0x50) - * 4 is an ASCII Letter '4' (0x34) - * Version is currently 0.1 (0x01) - * Op is an operation to Perform: - * '+' (0x2b) Result = OperandA + OperandB - * '-' (0x2d) Result = OperandA - OperandB - * '&' (0x26) Result = OperandA & OperandB - * '|' (0x7c) Result = OperandA | OperandB - * '^' (0x5e) Result = OperandA ^ OperandB - * - * The device receives a packet, performs the requested operation, fills in the - * result and sends the packet back out of the same port it came in on, while - * swapping the source and destination addresses. - * - * If an unknown operation is specified or the header is not valid, the packet - * is dropped - */ - -#include -#include - -/* - * Define the headers the program will recognize - */ - -/* - * Standard ethernet header - */ -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -/* - * This is a custom protocol header for the calculator. We'll use - * ethertype 0x1234 for is (see parser) - */ -const bit<16> P4CALC_ETYPE = 0x1234; -const bit<8> P4CALC_P = 0x50; // 'P' -const bit<8> P4CALC_4 = 0x34; // '4' -const bit<8> P4CALC_VER = 0x01; // v0.1 -const bit<8> P4CALC_PLUS = 0x2b; // '+' -const bit<8> P4CALC_MINUS = 0x2d; // '-' -const bit<8> P4CALC_AND = 0x26; // '&' -const bit<8> P4CALC_OR = 0x7c; // '|' -const bit<8> P4CALC_CARET = 0x5e; // '^' - -header p4calc_t { - bit<8> p; - bit<8> four; - bit<8> ver; - bit<8> op; - bit<32> operand_a; - bit<32> operand_b; - bit<32> res; -} - -/* - * All headers, used in the program needs to be assembed into a single struct. - * We only need to declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ -struct my_headers_t { - ethernet_t ethernet; - p4calc_t p4calc; -} - -/* - * All metadata, globally used in the program, also needs to be assembed - * into a single struct. As in the case of the headers, we only need to - * declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ - -struct my_metadata_t { - /* In our case it is empty */ -} - -/************************************************************************* - *********************** P A R S E R *********************************** - *************************************************************************/ -parser MyParser( - packet_in packet, - out my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) -{ - state start { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - P4CALC_ETYPE : check_p4calc; - default : accept; - } - } - - state check_p4calc { - transition select(packet.lookahead().p, - packet.lookahead().four, - packet.lookahead().ver) { - (P4CALC_P, P4CALC_4, P4CALC_VER) : parse_p4calc; - default : accept; - } - } - - state parse_p4calc { - packet.extract(hdr.p4calc); - transition accept; - } -} - -/************************************************************************* - ************ C H E C K S U M V E R I F I C A T I O N ************* - *************************************************************************/ -control MyVerifyChecksum( - inout my_headers_t hdr, - inout my_metadata_t meta) -{ - apply { - } -} - -/************************************************************************* - ************** I N G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyIngress( - inout my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) -{ - action send_back(bit<32> result) { - bit<48> tmp; - - /* Put the result back in */ - hdr.p4calc.res = result; - - /* Swap the MAC addresses */ - tmp = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = hdr.ethernet.srcAddr; - hdr.ethernet.srcAddr = tmp; - - /* Send the packet back to the port it came from */ - standard_metadata.egress_spec = standard_metadata.ingress_port; - } - - action operation_add() { - send_back(hdr.p4calc.operand_a + hdr.p4calc.operand_b); - } - - action operation_sub() { - send_back(hdr.p4calc.operand_a - hdr.p4calc.operand_b); - } - - action operation_and() { - send_back(hdr.p4calc.operand_a & hdr.p4calc.operand_b); - } - - action operation_or() { - send_back(hdr.p4calc.operand_a | hdr.p4calc.operand_b); - } - - action operation_xor() { - send_back(hdr.p4calc.operand_a ^ hdr.p4calc.operand_b); - } - - action operation_drop() { - mark_to_drop(); - } - - table calculate { - key = { - hdr.p4calc.op : exact; - } - actions = { - operation_add; - operation_sub; - operation_and; - operation_or; - operation_xor; - operation_drop; - } - const default_action = operation_drop(); - const entries = { - P4CALC_PLUS : operation_add(); - P4CALC_MINUS: operation_sub(); - P4CALC_AND : operation_and(); - P4CALC_OR : operation_or(); - P4CALC_CARET: operation_xor(); - } - } - - - apply { - if (hdr.p4calc.isValid()) { - calculate.apply(); - } else { - operation_drop(); - } - } -} - -/************************************************************************* - **************** E G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyEgress( - inout my_headers_t hdr, - inout my_metadata_t meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* - ************* C H E C K S U M C O M P U T A T I O N ************** - *************************************************************************/ -control MyComputeChecksum( - inout my_headers_t hdr, - inout my_metadata_t meta) -{ - apply { } -} - -/************************************************************************* - *********************** D E P A R S E R ******************************* - *************************************************************************/ -control MyDeparser( - packet_out packet, - in my_headers_t hdr) -{ - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.p4calc); - } -} - -V1Switch( - MyParser(), - MyVerifyChecksum(), - MyIngress(), - MyEgress(), - MyComputeChecksum(), - MyDeparser() -) main; diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/README.md b/P4D2_2017_Spring/exercises/ipv4_forward/README.md deleted file mode 100644 index fa59d2e53..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/README.md +++ /dev/null @@ -1,156 +0,0 @@ -# Implementing L3 Forwarding - -## Introduction - -The objective of this tutorial is to implement basic L3 forwarding. To -keep the exercise small, we will just implement forwarding for IPv4. - -With IPv4 forwarding, the switch must perform the following actions -for every packet: (i) update the source and destination MAC addresses, -(ii) decrement the time-to-live (TTL) in the IP header, and (iii) -forward the packet out the appropriate port. - -Your switch will have a single table, which the control plane will -populate with static rules. Each rule will map an IP address to the -MAC address and output port for the next hop. We have already defined -the control plane rules, so you only need to implement the data plane -logic of your P4 program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the reference. - - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`ipv4_forward.p4`, which initially drops all packets. Your job (in the next -step) will be to extend it to properly forward IPv4 packets. - -Before that, let's compile the incomplete `ip4v_forward.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `ip4v_forward.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle, each connected to one host (`h1`, `h2`, `h3`). - * The hosts are assigned IPs of `10.0.1.10`, `10.0.2.10`, etc. - -2. You should now see a Mininet command prompt. Open two terminals for `h1` and `h2`, respectively: - ```bash - mininet> xterm h1 h2 - ``` -3. Each host includes a small Python-based messaging client and server. In `h2`'s xterm, start the server: - ```bash - ./receive.py - ``` -4. In `h1`'s xterm, send a message from the client: - ```bash - ./send.py 10.0.2.10 "P4 is cool" - ``` - The message will not be received. -5. Type `exit` to leave each xterm and the Mininet command line. - -The message was not received because each switch is programmed with -`ip4v_forward.p4`, which drops all packets on arrival. Your job is to extend -this file. - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules governing packet -processing are inserted into the pipeline by the control plane. When a rule -matches a packet, its action is invoked with parameters supplied by the control -plane as part of the rule. - -In this exercise, the control plane logic has already been implemented. As -part of bringing up the Mininet instance, the `run.sh` script will install -packet-processing rules in the tables of each switch. These are defined in the -`sX-commands.txt` files, where `X` corresponds to the switch number. - -**Important:** A P4 program also defines the interface between the switch -pipeline and control plane. The `sX-commands.txt` files contain lists of -commands for the BMv2 switch API. These commands refer to specific tables, -keys, and actions by name, and any changes in the P4 program that add or rename -tables, keys, or actions will need to be reflected in these command files. - -## Step 2: Implement L3 forwarding - -The `ipv4_forward.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the missing piece. - -A complete `ipv4_forward.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 (`ipv4_t`). -2. **TODO:** Parsers for Ethernet and IPv4 that populate `ethernet_t` and `ipv4_t` fields. -3. An action to drop a packet, using `mark_to_drop()`. -4. **TODO:** An action (called `ipv4_forward`), which will: - 1. Set the egress port for the next hop. - 2. Update the ethernet destination address with the address of the next hop. - 3. Update the ethernet source address with the address of the switch. - 4. Decrement the TTL. -5. **TODO:** A control that: - 1. Defines a table that will read an IPv4 destination address, and - invoke either `drop` or `ipv4_forward`. - 1. An `apply` block that applies the table. -7. A deparser that selects the order in which fields inserted into the outgoing - packet. -8. A `package` instantiation supplied with the parser, control, and deparser. - > In general, a package also requires instances of checksum verification - > and recomputation controls. These are not necessary for this tutorial - > and are replaced with instantiations of empty controls. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, your message from `h1` should -be delivered to `h2`. - -### Food for thought - -The "test suite" for your solution---sending a message from `h1` to `h2`---is -not very robust. What else should you test to be confident of your -implementation? - -> Although the Python `scapy` library is outside the scope of this tutorial, -> it can be used to generate packets for testing. The `send.py` file shows how -> to use it. - -Other questions to consider: - - - How would you enhance your program to support next hops? - - Is this program enough to replace a router? What's missing? - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `ipv4_forward.p4` fails to compile. In this case, `run.sh` will report the -error emitted from the compiler and stop. - -2. `ipv4_forward.p4` compiles but does not support the control plane rules in -the `sX-commands.txt` files that `run.sh` tries to install using the BMv2 CLI. -In this case, `run.sh` will report these errors to `stderr`. Use these error -messages to fix your `ipv4_forward.p4` implementation. - -3. `ipv4_forward.p4` compiles, and the control plane rules are installed, but -the switch does not process packets in the desired way. The -`build/logs/.log` files contain trace messages describing how each -switch processes each packet. The output is detailed and can help pinpoint -logic errors in your implementation. - -#### Cleaning up Mininet - -In the latter two cases above, `run.sh` may leave a Mininet instance running in -the background. Use the following command to clean up these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to the next exercise: -implementing basic network telemetry [Multi-Hop Route Inspection](../mri). diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/ipv4_forward.p4 b/P4D2_2017_Spring/exercises/ipv4_forward/ipv4_forward.p4 deleted file mode 100644 index f069145d4..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/ipv4_forward.p4 +++ /dev/null @@ -1,170 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser ParserImpl(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - - state start { - /* TODO: add transition to parsing ethernet */ - transition accept; - } - - state parse_ethernet { - /* TODO: add parsing ethernet */ - } - - state parse_ipv4 { - /* TODO: add parsing ipv4 */ - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control verifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - - /* This action will drop packets */ - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - /* - * TODO: Implement the logic to: - * 1. Set the standard_metadata.egress_spec to the output port. - * 2. Set the ethernet srcAddr to the ethernet dstAddr. - * 3. Set the ethernet dstAddr to the dstAddr passed as a parameter. - * 4. Decrement the IP TTL. - * BONUS: Handle the case where TTL is 0. - */ - } - - table ipv4_lpm { - key = { - /* TODO: declare that the table will do a longest-prefix match (lpm) - on the IP destination address. */ - } - actions = { - /* TODO: declare the possible actions: ipv4_forward or drop. */ - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - /* TODO: replace drop with logic to: - * 1. Check if the ipv4 header is valid. - * 2. apply the table ipv4_lpm. - */ - drop(); - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control computeChecksum( - inout headers hdr, - inout metadata meta) -{ - /* - * Ignore checksum for now. The reference solution contains a checksum - * implementation. - */ - apply { } -} - - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control DeparserImpl(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -ParserImpl(), -verifyChecksum(), -ingress(), -egress(), -computeChecksum(), -DeparserImpl() -) main; diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/p4app.json b/P4D2_2017_Spring/exercises/ipv4_forward/p4app.json deleted file mode 100644 index 05e314d77..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/p4app.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "program": "ipv4_forward.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["s1", "s2"], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s3", "h3"]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - } - - }, - "switches": { - "s1": { - "entries": "s1-commands.txt" - }, - "s2": { - "entries": "s2-commands.txt" - }, - "s3": { - "entries": "s3-commands.txt" - } - } - } - } -} diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/receive.py b/P4D2_2017_Spring/exercises/ipv4_forward/receive.py deleted file mode 100755 index 6056a02ab..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/receive.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -import sys -import struct - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - iface = 'h2-eth0' - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/run.sh b/P4D2_2017_Spring/exercises/ipv4_forward/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/s1-commands.txt b/P4D2_2017_Spring/exercises/ipv4_forward/s1-commands.txt deleted file mode 100644 index 2045b533a..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/s1-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.10/32 => 00:aa:00:01:00:01 1 -table_add ipv4_lpm ipv4_forward 10.0.2.10/32 => f2:ed:e6:df:4e:fa 2 -table_add ipv4_lpm ipv4_forward 10.0.3.10/32 => f2:ed:e6:df:4e:fb 3 diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/s2-commands.txt b/P4D2_2017_Spring/exercises/ipv4_forward/s2-commands.txt deleted file mode 100644 index 48f1ed1d2..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/s2-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.2.10/32 => 00:aa:00:02:00:02 1 -table_add ipv4_lpm ipv4_forward 10.0.1.10/32 => 22:a8:04:41:ab:d3 2 -table_add ipv4_lpm ipv4_forward 10.0.3.10/32 => 22:a8:04:41:ab:d4 3 diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/s3-commands.txt b/P4D2_2017_Spring/exercises/ipv4_forward/s3-commands.txt deleted file mode 100644 index 7a182b615..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.3.10/32 => 00:aa:00:03:00:01 1 -table_add ipv4_lpm ipv4_forward 10.0.1.10/32 => f2:ed:e6:df:4e:fb 2 -table_add ipv4_lpm ipv4_forward 10.0.2.10/32 => f2:ed:e6:df:4e:fa 3 diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/send.py b/P4D2_2017_Spring/exercises/ipv4_forward/send.py deleted file mode 100755 index ba7321173..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/send.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr -from scapy.all import Packet -from scapy.all import Ether, IP, UDP - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - print "sending on interface %s to %s" % (iface, str(addr)) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') / IP(dst=addr) / UDP(dport=4321, sport=1234) / sys.argv[2] - pkt.show2() - sendp(pkt, iface=iface, verbose=False) - - -if __name__ == '__main__': - main() diff --git a/P4D2_2017_Spring/exercises/ipv4_forward/solution/ipv4_forward.p4 b/P4D2_2017_Spring/exercises/ipv4_forward/solution/ipv4_forward.p4 deleted file mode 100644 index c9c52b08c..000000000 --- a/P4D2_2017_Spring/exercises/ipv4_forward/solution/ipv4_forward.p4 +++ /dev/null @@ -1,176 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser ParserImpl(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control verifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control computeChecksum( - inout headers hdr, - inout metadata meta) -{ - apply { - update_checksum(true, - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr - }, - hdr.ipv4.hdrChecksum, HashAlgorithm.csum16); - } -} - - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control DeparserImpl(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -ParserImpl(), -verifyChecksum(), -ingress(), -egress(), -computeChecksum(), -DeparserImpl() -) main; diff --git a/P4D2_2017_Spring/exercises/mri/README.md b/P4D2_2017_Spring/exercises/mri/README.md deleted file mode 100644 index 2971bbc59..000000000 --- a/P4D2_2017_Spring/exercises/mri/README.md +++ /dev/null @@ -1,206 +0,0 @@ -# Implementing MRI - -## Introduction - -The objective of this tutorial is to extend basic L3 forwarding with a -scaled-down version of In-Band Network Telemetry (INT), which we call -Multi-Hop Route Inspection (MRI). - -MRI allows users to track the path that every packet travels through -the network. To support this functionality, you will need to write a -P4 program that appends an ID to the header stack of every packet. At -the destination, the sequence of switch IDs correspond to the path. - -As before, we have already defined the control plane rules, so you -only need to implement the data plane logic of your P4 program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`mri.p4`, which initially implements L3 forwarding. Your job (in the -next step) will be to extend it to properly append the MRI custom -headers. - -Before that, let's compile the incomplete `mri.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `mri.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle, each connected to one host (`h1`, `h2`, `h3`). - * The hosts are assigned IPs of `10.0.1.10`, `10.0.2.10`, etc. - -2. You should now see a Mininet command prompt. Open two terminals for `h1` and `h2`, respectively: - ```bash - mininet> xterm h1 h2 - ``` -3. Each host includes a small Python-based messaging client and server. In `h2`'s xterm, start the server: - ```bash - ./receive.py - ``` -4. In `h1`'s xterm, send a message from the client: - ```bash - ./send.py 10.0.2.10 "P4 is cool" - ``` - The message "P4 is cool" should be received in `h2`'s xterm, -5. Type `exit` to leave each xterm and the Mininet command line. - -You should see the message received at host `h2`, but without any information -about the path the message took. Your job is to extend the code in `mri.p4` to -implement the MRI logic to record the path. - - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules governing packet -processing are inserted into the pipeline by the control plane. When a rule -matches a packet, its action is invoked with parameters supplied by the control -plane as part of the rule. - -In this exercise, the control plane logic has already been implemented. As -part of bringing up the Mininet instance, the `run.sh` script will install -packet-processing rules in the tables of each switch. These are defined in the -`sX-commands.txt` files, where `X` corresponds to the switch number. - - -## Step 2: Implement MRI - - -The `mri.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the missing piece. - -MRI will require two custom headers. The first header, `mri_t`, -contains a single field `count`, which indicates the number of switch -IDs that follow. The second header, `switch_t`, contains a single -field with the switch ID. - -One of the biggest challenges in implementing MRI is handling the -recursive logic for parsing these two headers. We will use a -`parser_metadata` field, `remaining`, to keep track of how many -`switch_t` headers we need to parse. In the `parse_mri` state, this -field should be set to `hdr.mri.count`. In the `parse_swid` state, -this field should be decremented. The `parse_swid` state will -transition to itself until `remaining` is 0. - -The MRI custom headers will be carried inside an IP Options -header. The IP Options header contains a field, `option`, which -indicates the type of the option. We will use a special type 31 to -indicate the presence of the MRI headers. - -Beyond the parser logic, you will add a table, `swid` to store the -switch ID, and actions that add the `mri_t` header if it doesn't -exist, increment the `count` field, and append a `switch_t` header. - - -A complete `mri.p4` will contain the following components: - - -1. Header type definitions for Ethernet (`ethernet_t`), IPv4 (`ipv4_t`), - IP Options (`ipv4_option_t`), MRI (`mri_t`), and Switch (`switch_t`). -2. Parsers for Ethernet, IPv4, IP Options, MRI, and Switch that will -populate `ethernet_t`, `ipv4_t`, `ipv4_option_t`, `mri_t`, and -`switch_t`. -3. An action to drop a packet, using `mark_to_drop()`. -4. An action (called `ipv4_forward`), which will: - 1. Set the egress port for the next hop. - 2. Update the ethernet destination address with the address of the next hop. - 3. Update the ethernet source address with the address of the switch. - 4. Decrement the TTL. -5. An action (called `add_mri_option`) that will add the IP Options and MRI -header. Note that you can use the `setValid()` function, which adds a -header if it does not exist, but otherwise leaves the packet -unmodified. -6. An action (called `add_swid`) that will add the switch ID header. -7. A table (`swid`) to store the switch ID, and calls `add_swid`. -8. A control that: - 1. Defines a table that will read an IPv4 destination address, and - invoke either `drop` or `ipv4_forward`. - 1. An `apply` block that applies the table. -9. A deparser that selects the order in which fields inserted into the outgoing - packet. -10. A `package` instantiation supplied with the parser, control, and deparser. - - > In general, a package also requires instances of checksum verification - > and recomputation controls. These are not necessary for this tutorial - > and are replaced with instantiations of empty controls. - - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, when your message from `h1` is - delivered to `h2`, you should see the seqeunce of switches -through which the packet traveled. The expected output will look like the -following, which shows the MRI header, with a `count` of 2, and switch ids (`swids`) 2 and 1. - -``` - -got a packet -###[ Ethernet ]### - dst = 00:aa:00:02:00:02 - src = f2:ed:e6:df:4e:fa - type = 0x800 -###[ IP ]### - version = 4L - ihl = 8L - tos = 0x0 - len = 33 - id = 1 - flags = - frag = 0L - ttl = 62 - proto = udp - chksum = 0x63b8 - src = 10.0.1.10 - dst = 10.0.2.10 - \options \ - |###[ MRI ]### - | copy_flag = 1L - | optclass = debug - | option = 31L - | length = 12 - | count = 2 - | swids = [2, 1] -``` - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `mri.p4` fails to compile. In this case, `run.sh` will report the -error emitted from the compiler and stop. - -1. `mri.p4` compiles but does not support the control plane rules in -the `sX-commands.txt` files that `run.sh` tries to install using the BMv2 CLI. -In this case, `run.sh` will report these errors to `stderr`. Use these error -messages to fix your `ipv4_forward.p4` implementation. - -1. `mri.p4` compiles, and the control plane rules are installed, but -the switch does not process packets in the desired way. The -`build/logs/.log` files contain trace messages describing how each -switch processes each packet. The output is detailed and can help pinpoint -logic errors in your implementation. - -#### Cleaning up Mininet - -In the latter two cases above, `run.sh` may leave a Mininet instance running in -the background. Use the following command to clean up these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to the next exercise: -implementing an [ARP and ICMP Responder](../arp). - - - diff --git a/P4D2_2017_Spring/exercises/mri/mri.p4 b/P4D2_2017_Spring/exercises/mri/mri.p4 deleted file mode 100644 index 11f8bfd38..000000000 --- a/P4D2_2017_Spring/exercises/mri/mri.p4 +++ /dev/null @@ -1,286 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> UDP_PROTOCOL = 0x11; -const bit<16> TYPE_IPV4 = 0x800; -const bit<5> IPV4_OPTION_MRI = 31; - - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; -typedef bit<32> switchID_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -header ipv4_option_t { - bit<1> copyFlag; - bit<2> optClass; - bit<5> option; - bit<8> optionLength; -} - -header mri_t { - bit<16> count; -} - -header switch_t { - switchID_t swid; -} - -struct ingress_metadata_t { - bit<16> count; -} - -struct parser_metadata_t { - bit<16> remaining; -} - -struct metadata { - ingress_metadata_t ingress_metadata; - parser_metadata_t parser_metadata; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - ipv4_option_t ipv4_option; - mri_t mri; - switch_t[MAX_HOPS] swids; -} - -error { IPHeaderTooShort } - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser ParserImpl(packet_in packet, -out headers hdr, -inout metadata meta, -inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - verify(hdr.ipv4.ihl >= 5, error.IPHeaderTooShort); - /* - * TODO: Modify the next line to select on the value of hdr.ipv4.ihl. - * If the value of hdr.ipv4.ihl is set to 5, accept. - * Otherwise, transition to parse_ipv4_option. - */ - transition accept; - } - - /* TODO: Implement the logic for parse_ipv4_options, parse_mri, and parse_swid */ - - - state parse_ipv4_option { - /* - * TODO: Add logic to: - * - Extract the ipv4_option header. - * - If the value is equal to IPV4_OPTION_MRI, transition to parse_mri. - * - Otherwise, accept. - */ - } - - state parse_mri { - /* - * TODO: Add logic to: - * - Extract hdr.mri. - * - Set meta.parser_metadata.remaining to hdr.mri.count - * - Select on the value of meta.parser_metadata.remaining - * - If the value is equal to 0, accept. - * - Otherwise, transition to parse_swid. - */ - } - - state parse_swid { - /* - * TODO: Add logic to: - * - Extract hdr.swids.next. - * - Decrement meta.parser_metadata.remaining by 1 - * - Select on the value of meta.parser_metadata.remaining - * - If the value is equal to 0, accept. - * - Otherwise, transition to parse_swid. - */ - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control verifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action add_mri_option() { - /* - * TODO: add logic to: - * - Call setValid() on hdr.ipv4_option, which will add the header if it is not - * there, or leave the packet unchanged. - * - Set hdr.ipv4_option.copyFlag to 1 - * - Set hdr.ipv4_option.optClass to 2 - * - Set hdr.ipv4_option.option to IPV4_OPTION_MRI - * - Set the hdr.ipv4_option.optionLength to 4 - * - Call setValid() on hdr.mri - * - Set hdr.mri.count to 0 - * - Increment hdr.ipv4.ihl by 1 - */ - } - - action add_swid(switchID_t id) { - - /* - * TODO: add logic to: - - Increment hdr.mri.count by 1 - - Add a new swid header by calling push_front(1) on hdr.swids. - - Set hdr.swids[0].swid to the id paremeter - - Incremement hdr.ipv4.ihl by 1 - - Incrememtn hdr.ipv4_option.optionLength by 4 - */ - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table swid { - actions = - { - /* TODO: repace NoAction with the correct action */ - NoAction; - } - /* TODO: set a default action. */ - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - - ipv4_lpm.apply(); - - /* - * TODO: add logic to: - * - If hdr.ipv4 is valid: - * - Apply table ipv4_lpm - * - If hdr.mri is not valid, call add_mri_option() - * - Apply table swid - */ - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - - -control computeChecksum( - inout headers hdr, - inout metadata meta) -{ - /* - * Ignore checksum for now. The reference solution contains a checksum - * implementation. - */ - apply { } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control DeparserImpl(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.ipv4_option); - packet.emit(hdr.mri); - packet.emit(hdr.swids); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -ParserImpl(), -verifyChecksum(), -ingress(), -egress(), -computeChecksum(), -DeparserImpl() -) main; diff --git a/P4D2_2017_Spring/exercises/mri/p4app.json b/P4D2_2017_Spring/exercises/mri/p4app.json deleted file mode 100644 index 6ffe32f29..000000000 --- a/P4D2_2017_Spring/exercises/mri/p4app.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "program": "mri.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["s1", "s2"], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s3", "h3"]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - } - - }, - "switches": { - "s1": { - "entries": "s1-commands.txt" - }, - "s2": { - "entries": "s2-commands.txt" - }, - "s3": { - "entries": "s3-commands.txt" - } - } - } - } -} diff --git a/P4D2_2017_Spring/exercises/mri/receive.py b/P4D2_2017_Spring/exercises/mri/receive.py deleted file mode 100755 index 6056a02ab..000000000 --- a/P4D2_2017_Spring/exercises/mri/receive.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -import sys -import struct - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - iface = 'h2-eth0' - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/P4D2_2017_Spring/exercises/mri/run.sh b/P4D2_2017_Spring/exercises/mri/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/P4D2_2017_Spring/exercises/mri/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/P4D2_2017_Spring/exercises/mri/s1-commands.txt b/P4D2_2017_Spring/exercises/mri/s1-commands.txt deleted file mode 100644 index 5242cc140..000000000 --- a/P4D2_2017_Spring/exercises/mri/s1-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swid add_swid 1 -table_add ipv4_lpm ipv4_forward 10.0.1.10/32 => 00:aa:00:01:00:01 1 -table_add ipv4_lpm ipv4_forward 10.0.2.10/32 => f2:ed:e6:df:4e:fa 2 -table_add ipv4_lpm ipv4_forward 10.0.3.10/32 => f2:ed:e6:df:4e:fb 3 diff --git a/P4D2_2017_Spring/exercises/mri/s2-commands.txt b/P4D2_2017_Spring/exercises/mri/s2-commands.txt deleted file mode 100644 index b8d2baeff..000000000 --- a/P4D2_2017_Spring/exercises/mri/s2-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swid add_swid 2 -table_add ipv4_lpm ipv4_forward 10.0.2.10/32 => 00:aa:00:02:00:02 1 -table_add ipv4_lpm ipv4_forward 10.0.1.10/32 => 22:a8:04:41:ab:d3 2 -table_add ipv4_lpm ipv4_forward 10.0.3.10/32 => 22:a8:04:41:ab:d4 3 diff --git a/P4D2_2017_Spring/exercises/mri/s3-commands.txt b/P4D2_2017_Spring/exercises/mri/s3-commands.txt deleted file mode 100644 index 3da942832..000000000 --- a/P4D2_2017_Spring/exercises/mri/s3-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swid add_swid 3 -table_add ipv4_lpm ipv4_forward 10.0.3.10/32 => 00:aa:00:03:00:01 1 -table_add ipv4_lpm ipv4_forward 10.0.1.10/32 => f2:ed:e6:df:4e:fb 2 -table_add ipv4_lpm ipv4_forward 10.0.2.10/32 => f2:ed:e6:df:4e:fa 3 diff --git a/P4D2_2017_Spring/exercises/mri/send.py b/P4D2_2017_Spring/exercises/mri/send.py deleted file mode 100755 index 368be7c88..000000000 --- a/P4D2_2017_Spring/exercises/mri/send.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import Ether, IP, UDP -from scapy.all import IntField, FieldListField, FieldLenField, ShortField -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] - - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(dst=addr) / UDP(dport=4321, sport=1234) / sys.argv[2] - #pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(dst=addr, options = IPOption_MRI(count=2, swids=[3,4])) / UDP(dport=4321, sport=1234) / sys.argv[2] - pkt.show2() - #hexdump(pkt) - sendp(pkt, iface=iface) - - -if __name__ == '__main__': - main() diff --git a/P4D2_2017_Spring/exercises/mri/solution/mri.p4 b/P4D2_2017_Spring/exercises/mri/solution/mri.p4 deleted file mode 100644 index 152ef92cb..000000000 --- a/P4D2_2017_Spring/exercises/mri/solution/mri.p4 +++ /dev/null @@ -1,279 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> UDP_PROTOCOL = 0x11; -const bit<16> TYPE_IPV4 = 0x800; -const bit<5> IPV4_OPTION_MRI = 31; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; -typedef bit<32> switchID_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -header ipv4_option_t { - bit<1> copyFlag; - bit<2> optClass; - bit<5> option; - bit<8> optionLength; -} - -header mri_t { - bit<16> count; -} - -header switch_t { - switchID_t swid; -} - -struct ingress_metadata_t { - bit<16> count; -} - -struct parser_metadata_t { - bit<16> remaining; -} - -struct metadata { - ingress_metadata_t ingress_metadata; - parser_metadata_t parser_metadata; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - ipv4_option_t ipv4_option; - mri_t mri; - switch_t[MAX_HOPS] swids; -} - -error { IPHeaderTooShort } - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser ParserImpl(packet_in packet, -out headers hdr, -inout metadata meta, -inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - verify(hdr.ipv4.ihl >= 5, error.IPHeaderTooShort); - transition select(hdr.ipv4.ihl) { - 5 : accept; - default : parse_ipv4_option; - } - } - - state parse_ipv4_option { - packet.extract(hdr.ipv4_option); - transition select(hdr.ipv4_option.option) { - IPV4_OPTION_MRI: parse_mri; - default: accept; - } - } - - state parse_mri { - packet.extract(hdr.mri); - meta.parser_metadata.remaining = hdr.mri.count; - transition select(meta.parser_metadata.remaining) { - 0 : accept; - default: parse_swid; - } - } - - state parse_swid { - packet.extract(hdr.swids.next); - meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1; - transition select(meta.parser_metadata.remaining) { - 0 : accept; - default: parse_swid; - } - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control verifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action add_mri_option() { - hdr.ipv4_option.setValid(); - hdr.ipv4_option.copyFlag = 1; - hdr.ipv4_option.optClass = 2; /* Debugging and Measurement */ - hdr.ipv4_option.option = IPV4_OPTION_MRI; - hdr.ipv4_option.optionLength = 4; /* sizeof(ipv4_option) + sizeof(mri) */ - - hdr.mri.setValid(); - hdr.mri.count = 0; - hdr.ipv4.ihl = hdr.ipv4.ihl + 1; - } - - action add_swid(switchID_t id) { - hdr.mri.count = hdr.mri.count + 1; - hdr.swids.push_front(1); - // According to the P4_16 spec, pushed elements are invalid, so we need - // to call setValid(). Older bmv2 versions would mark the new header(s) - // valid automatically (P4_14 behavior), but starting with version 1.11, - // bmv2 conforms with the P4_16 spec. - hdr.swids[0].setValid(); - hdr.swids[0].swid = id; - - hdr.ipv4.ihl = hdr.ipv4.ihl + 1; - hdr.ipv4_option.optionLength = hdr.ipv4_option.optionLength + 4; - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table swid { - actions = { add_swid; NoAction; } - default_action = NoAction(); - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - - if (!hdr.mri.isValid()) { - add_mri_option(); - } - - swid.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - - -control computeChecksum( -inout headers hdr, -inout metadata meta) -{ - apply { - update_checksum(true, - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr - }, - hdr.ipv4.hdrChecksum, HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control DeparserImpl(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.ipv4_option); - packet.emit(hdr.mri); - packet.emit(hdr.swids); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -ParserImpl(), -verifyChecksum(), -ingress(), -egress(), -computeChecksum(), -DeparserImpl() -) main; diff --git a/P4D2_2017_Spring/utils/mininet/appcontroller.py b/P4D2_2017_Spring/utils/mininet/appcontroller.py deleted file mode 100644 index 0989cce73..000000000 --- a/P4D2_2017_Spring/utils/mininet/appcontroller.py +++ /dev/null @@ -1,103 +0,0 @@ -import subprocess - -from shortest_path import ShortestPath - -class AppController: - - def __init__(self, manifest=None, target=None, topo=None, net=None, links=None): - self.manifest = manifest - self.target = target - self.conf = manifest['targets'][target] - self.topo = topo - self.net = net - self.links = links - - def read_entries(self, filename): - entries = [] - with open(filename, 'r') as f: - for line in f: - line = line.strip() - if line == '': continue - entries.append(line) - return entries - - def add_entries(self, thrift_port=9090, sw=None, entries=None): - assert entries - if sw: thrift_port = sw.thrift_port - - print '\n'.join(entries) - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE) - p.communicate(input='\n'.join(entries)) - - def read_register(self, register, idx, thrift_port=9090, sw=None): - if sw: thrift_port = sw.thrift_port - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate(input="register_read %s %d" % (register, idx)) - reg_val = filter(lambda l: ' %s[%d]' % (register, idx) in l, stdout.split('\n'))[0].split('= ', 1)[1] - return long(reg_val) - - def start(self): - shortestpath = ShortestPath(self.links) - entries = {} - for sw in self.topo.switches(): - entries[sw] = [] - if 'switches' in self.conf and sw in self.conf['switches'] and 'entries' in self.conf['switches'][sw]: - extra_entries = self.conf['switches'][sw]['entries'] - if type(extra_entries) == list: # array of entries - entries[sw] += extra_entries - else: # path to file that contains entries - entries[sw] += self.read_entries(extra_entries) - #entries[sw] += [ - # 'table_set_default send_frame _drop', - # 'table_set_default forward _drop', - # 'table_set_default ipv4_lpm _drop'] - - for host_name in self.topo._host_links: - h = self.net.get(host_name) - for link in self.topo._host_links[host_name].values(): - sw = link['sw'] - #entries[sw].append('table_add send_frame rewrite_mac %d => %s' % (link['sw_port'], link['sw_mac'])) - #entries[sw].append('table_add forward set_dmac %s => %s' % (link['host_ip'], link['host_mac'])) - #entries[sw].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (link['host_ip'], link['host_ip'], link['sw_port'])) - iface = h.intfNames()[link['idx']] - # use mininet to set ip and mac to let it know the change - h.setIP(link['host_ip'], 16) - h.setMAC(link['host_mac']) - #h.cmd('ifconfig %s %s hw ether %s' % (iface, link['host_ip'], link['host_mac'])) - h.cmd('arp -i %s -s %s %s' % (iface, link['sw_ip'], link['sw_mac'])) - h.cmd('ethtool --offload %s rx off tx off' % iface) - h.cmd('ip route add %s dev %s' % (link['sw_ip'], iface)) - h.setDefaultRoute("via %s" % link['sw_ip']) - - for h in self.net.hosts: - h_link = self.topo._host_links[h.name].values()[0] - for sw in self.net.switches: - path = shortestpath.get(sw.name, h.name, exclude=lambda n: n[0]=='h') - if not path: continue - if not path[1][0] == 's': continue # next hop is a switch - sw_link = self.topo._sw_links[sw.name][path[1]] - #entries[sw.name].append('table_add send_frame rewrite_mac %d => %s' % (sw_link[0]['port'], sw_link[0]['mac'])) - #entries[sw.name].append('table_add forward set_dmac %s => %s' % (h_link['host_ip'], sw_link[1]['mac'])) - #entries[sw.name].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (h_link['host_ip'], h_link['host_ip'], sw_link[0]['port'])) - - for h2 in self.net.hosts: - if h == h2: continue - path = shortestpath.get(h.name, h2.name, exclude=lambda n: n[0]=='h') - if not path: continue - h_link = self.topo._host_links[h.name][path[1]] - h2_link = self.topo._host_links[h2.name].values()[0] - h.cmd('ip route add %s via %s' % (h2_link['host_ip'], h_link['sw_ip'])) - - - print "**********" - print "Configuring entries in p4 tables" - for sw_name in entries: - print - print "Configuring switch... %s" % sw_name - sw = self.net.get(sw_name) - self.add_entries(sw=sw, entries=entries[sw_name]) - print "Configuration complete." - print "**********" - - def stop(self): - pass diff --git a/P4D2_2017_Spring/utils/mininet/apptopo.py b/P4D2_2017_Spring/utils/mininet/apptopo.py deleted file mode 100644 index 2976c9aa0..000000000 --- a/P4D2_2017_Spring/utils/mininet/apptopo.py +++ /dev/null @@ -1,69 +0,0 @@ -from mininet.topo import Topo - -class AppTopo(Topo): - - def __init__(self, links, latencies={}, manifest=None, target=None, - log_dir="/tmp", **opts): - Topo.__init__(self, **opts) - - nodes = sum(map(list, zip(*links)), []) - host_names = sorted(list(set(filter(lambda n: n[0] == 'h', nodes)))) - sw_names = sorted(list(set(filter(lambda n: n[0] == 's', nodes)))) - sw_ports = dict([(sw, []) for sw in sw_names]) - - self._host_links = {} - self._sw_links = dict([(sw, {}) for sw in sw_names]) - - for sw_name in sw_names: - self.addSwitch(sw_name, log_file="%s/%s.log" %(log_dir, sw_name)) - - for host_name in host_names: - host_num = int(host_name[1:]) - - host_ip = "10.0.%d.10" % host_num - host_mac = '00:04:00:00:00:%02x' % host_num - - self.addHost(host_name) - - self._host_links[host_name] = {} - host_links = filter(lambda l: l[0]==host_name or l[1]==host_name, links) - - sw_idx = 0 - for link in host_links: - sw = link[0] if link[0] != host_name else link[1] - sw_num = int(sw[1:]) - assert sw[0]=='s', "Hosts should be connected to switches, not " + str(sw) - - delay_key = ''.join([host_name, sw]) - delay = latencies[delay_key] if delay_key in latencies else '0ms' - sw_ports[sw].append(host_name) - self._host_links[host_name][sw] = dict( - idx=sw_idx, - host_mac = host_mac, - host_ip = host_ip, - sw = sw, - sw_mac = "00:aa:00:%02x:00:%02x" % (sw_num, host_num), - sw_ip = "10.0.%d.%d" % (host_num, sw_idx+1), - sw_port = sw_ports[sw].index(host_name)+1 - ) - self.addLink(host_name, sw, delay=delay, - addr1=host_mac, addr2=self._host_links[host_name][sw]['sw_mac']) - sw_idx += 1 - - for link in links: # only check switch-switch links - sw1, sw2 = link - if sw1[0] != 's' or sw2[0] != 's': continue - - delay_key = ''.join(sorted([host_name, sw])) - delay = latencies[delay_key] if delay_key in latencies else '0ms' - self.addLink(sw1, sw2, delay=delay) - sw_ports[sw1].append(sw2) - sw_ports[sw2].append(sw1) - - sw1_num, sw2_num = int(sw1[1:]), int(sw2[1:]) - sw1_port = dict(mac="00:aa:00:%02x:%02x:00" % (sw1_num, sw2_num), port=sw_ports[sw1].index(sw2)+1) - sw2_port = dict(mac="00:aa:00:%02x:%02x:00" % (sw2_num, sw1_num), port=sw_ports[sw2].index(sw1)+1) - - self._sw_links[sw1][sw2] = [sw1_port, sw2_port] - self._sw_links[sw2][sw1] = [sw2_port, sw1_port] - diff --git a/P4D2_2017_Spring/utils/mininet/multi_switch_mininet.py b/P4D2_2017_Spring/utils/mininet/multi_switch_mininet.py deleted file mode 100755 index 497980d44..000000000 --- a/P4D2_2017_Spring/utils/mininet/multi_switch_mininet.py +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/env python2 - -# 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. -# - -import signal -import os -import sys -import subprocess -import argparse -import json -import importlib -import re -from time import sleep - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.link import TCLink -from mininet.log import setLogLevel, info -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host -import apptopo -import appcontroller - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--bmv2-log', help='verbose messages in log file', action="store_true") -parser.add_argument('--cli', help="start the mininet cli", action="store_true") -parser.add_argument('--auto-control-plane', help='enable automatic control plane population', action="store_true") -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - action="store_true") -parser.add_argument('--manifest', '-m', help='Path to manifest file', - type=str, action="store", required=True) -parser.add_argument('--target', '-t', help='Target in manifest file to run', - type=str, action="store", required=True) -parser.add_argument('--log-dir', '-l', help='Location to save output to', - type=str, action="store", required=True) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) - - -args = parser.parse_args() - - -next_thrift_port = args.thrift_port - -def run_command(command): - return os.WEXITSTATUS(os.system(command)) - -def configureP4Switch(**switch_args): - class ConfiguredP4Switch(P4Switch): - def __init__(self, *opts, **kwargs): - global next_thrift_port - kwargs.update(switch_args) - kwargs['thrift_port'] = next_thrift_port - next_thrift_port += 1 - P4Switch.__init__(self, *opts, **kwargs) - return ConfiguredP4Switch - - -def main(): - - with open(args.manifest, 'r') as f: - manifest = json.load(f) - - conf = manifest['targets'][args.target] - params = conf['parameters'] if 'parameters' in conf else {} - - os.environ.update(dict(map(lambda (k,v): (k, str(v)), params.iteritems()))) - - def formatParams(s): - for param in params: - s = re.sub('\$'+param+'(\W|$)', str(params[param]) + r'\1', s) - s = s.replace('${'+param+'}', str(params[param])) - return s - - AppTopo = apptopo.AppTopo - AppController = appcontroller.AppController - - if 'topo_module' in conf: - sys.path.insert(0, os.path.dirname(args.manifest)) - topo_module = importlib.import_module(conf['topo_module']) - AppTopo = topo_module.CustomAppTopo - - if 'controller_module' in conf: - sys.path.insert(0, os.path.dirname(args.manifest)) - controller_module = importlib.import_module(conf['controller_module']) - AppController = controller_module.CustomAppController - - if not os.path.isdir(args.log_dir): - if os.path.exists(args.log_dir): raise Exception('Log dir exists and is not a dir') - os.mkdir(args.log_dir) - os.environ['P4APP_LOGDIR'] = args.log_dir - - - links = [l[:2] for l in conf['links']] - latencies = dict([(''.join(sorted(l[:2])), l[2]) for l in conf['links'] if len(l)==3]) - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'latency' not in host: continue - for a, b in links: - if a != host_name and b != host_name: continue - other = a if a != host_name else b - latencies[host_name+other] = host['latency'] - - for l in latencies: - if isinstance(latencies[l], (str, unicode)): - latencies[l] = formatParams(latencies[l]) - else: - latencies[l] = str(latencies[l]) + "ms" - - bmv2_log = args.bmv2_log or ('bmv2_log' in conf and conf['bmv2_log']) - pcap_dump = args.pcap_dump or ('pcap_dump' in conf and conf['pcap_dump']) - - topo = AppTopo(links, latencies, manifest=manifest, target=args.target, - log_dir=args.log_dir) - switchClass = configureP4Switch( - sw_path=args.behavioral_exe, - json_path=args.json, - log_console=bmv2_log, - pcap_dump=pcap_dump) - net = Mininet(topo = topo, - link = TCLink, - host = P4Host, - switch = switchClass, - controller = None) - net.start() - - sleep(1) - - controller = None - if args.auto_control_plane or 'controller_module' in conf: - controller = AppController(manifest=manifest, target=args.target, - topo=topo, net=net, links=links) - controller.start() - - - for h in net.hosts: - h.describe() - - if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: - print message_file.read() - - if args.cli or ('cli' in conf and conf['cli']): - CLI(net) - - stdout_files = dict() - return_codes = [] - host_procs = [] - - - def formatCmd(cmd): - for h in net.hosts: - cmd = cmd.replace(h.name, h.defaultIntf().updateIP()) - return cmd - - def _wait_for_exit(p, host): - print p.communicate() - if p.returncode is None: - p.wait() - print p.communicate() - return_codes.append(p.returncode) - if host_name in stdout_files: - stdout_files[host_name].flush() - stdout_files[host_name].close() - - print '\n'.join(map(lambda (k,v): "%s: %s"%(k,v), params.iteritems())) + '\n' - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'cmd' not in host: continue - - h = net.get(host_name) - stdout_filename = os.path.join(args.log_dir, h.name + '.stdout') - stdout_files[h.name] = open(stdout_filename, 'w') - cmd = formatCmd(host['cmd']) - print h.name, cmd - p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, preexec_fn=os.setpgrp) - if 'startup_sleep' in host: sleep(host['startup_sleep']) - - if 'wait' in host and host['wait']: - _wait_for_exit(p, host_name) - else: - host_procs.append((p, host_name)) - - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: - _wait_for_exit(p, host_name) - - - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: - continue - if p.returncode is None: - run_command('pkill -INT -P %d' % p.pid) - sleep(0.2) - rc = run_command('pkill -0 -P %d' % p.pid) # check if it's still running - if rc == 0: # the process group is still running, send TERM - sleep(1) # give it a little more time to exit gracefully - run_command('pkill -TERM -P %d' % p.pid) - _wait_for_exit(p, host_name) - - if 'after' in conf and 'cmd' in conf['after']: - cmds = conf['after']['cmd'] if type(conf['after']['cmd']) == list else [conf['after']['cmd']] - for cmd in cmds: - os.system(cmd) - - if controller: controller.stop() - - net.stop() - -# if bmv2_log: -# os.system('bash -c "cp /tmp/p4s.s*.log \'%s\'"' % args.log_dir) -# if pcap_dump: -# os.system('bash -c "cp *.pcap \'%s\'"' % args.log_dir) - - bad_codes = [rc for rc in return_codes if rc != 0] - if len(bad_codes): sys.exit(1) - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/P4D2_2017_Spring/utils/mininet/p4_mininet.py b/P4D2_2017_Spring/utils/mininet/p4_mininet.py deleted file mode 100644 index 8abe79f3a..000000000 --- a/P4D2_2017_Spring/utils/mininet/p4_mininet.py +++ /dev/null @@ -1,161 +0,0 @@ -# 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. -# - -from mininet.net import Mininet -from mininet.node import Switch, Host -from mininet.log import setLogLevel, info, error, debug -from mininet.moduledeps import pathCheck -from sys import exit -from time import sleep -import os -import tempfile -import socket - -class P4Host(Host): - def config(self, **params): - r = super(P4Host, self).config(**params) - - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf().name, off) - self.cmd(cmd) - - # disable IPv6 - self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - - return r - - def describe(self, sw_addr=None, sw_mac=None): - print "**********" - print "Network configuration for: %s" % self.name - print "Default interface: %s\t%s\t%s" %( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC() - ) - if sw_addr is not None or sw_mac is not None: - print "Default route to switch: %s (%s)" % (sw_addr, sw_mac) - print "**********" - -class P4Switch(Switch): - """P4 virtual switch""" - device_id = 0 - - def __init__(self, name, sw_path = None, json_path = None, - log_file = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): - Switch.__init__(self, name, **kwargs) - assert(sw_path) - assert(json_path) - # make sure that the provided sw_path is valid - pathCheck(sw_path) - # make sure that the provided JSON file exists - if not os.path.isfile(json_path): - error("Invalid JSON file.\n") - exit(1) - self.sw_path = sw_path - self.json_path = json_path - self.verbose = verbose - self.log_file = log_file - if self.log_file is None: - self.log_file = "/tmp/p4s.{}.log".format(self.name) - self.output = open(self.log_file, 'w') - self.thrift_port = thrift_port - self.pcap_dump = pcap_dump - self.enable_debugger = enable_debugger - self.log_console = log_console - if device_id is not None: - self.device_id = device_id - P4Switch.device_id = max(P4Switch.device_id, device_id) - else: - self.device_id = P4Switch.device_id - P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) - - @classmethod - def setup(cls): - pass - - def check_switch_started(self, pid): - """While the process is running (pid exists), we check if the Thrift - server has been started. If the Thrift server is ready, we assume that - the switch was started successfully. This is only reliable if the Thrift - server is started at the end of the init process""" - while True: - if not os.path.exists(os.path.join("/proc", str(pid))): - return False - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(0.5) - result = sock.connect_ex(("localhost", self.thrift_port)) - if result == 0: - return True - - def start(self, controllers): - "Start up a new P4 switch" - info("Starting P4 switch {}.\n".format(self.name)) - args = [self.sw_path] - for port, intf in self.intfs.items(): - if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) - if self.pcap_dump: - args.append("--pcap") - # args.append("--useFiles") - if self.thrift_port: - args.extend(['--thrift-port', str(self.thrift_port)]) - if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) - P4Switch.device_id += 1 - args.append(self.json_path) - if self.enable_debugger: - args.append("--debugger") - if self.log_console: - args.append("--log-console") - info(' '.join(args) + "\n") - - pid = None - with tempfile.NamedTemporaryFile() as f: - # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &') - self.cmd(' '.join(args) + ' >' + self.log_file + ' 2>&1 & echo $! >> ' + f.name) - pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) - sleep(1) - if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly." - "Check the switch log file.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) - - def stop(self): - "Terminate P4 switch." - self.output.flush() - self.cmd('kill %' + self.sw_path) - self.cmd('wait') - self.deleteIntfs() - - def attach(self, intf): - "Connect a data port" - assert(0) - - def detach(self, intf): - "Disconnect a data port" - assert(0) diff --git a/P4D2_2017_Spring/utils/mininet/shortest_path.py b/P4D2_2017_Spring/utils/mininet/shortest_path.py deleted file mode 100644 index 971b1b446..000000000 --- a/P4D2_2017_Spring/utils/mininet/shortest_path.py +++ /dev/null @@ -1,78 +0,0 @@ -class ShortestPath: - - def __init__(self, edges=[]): - self.neighbors = {} - for edge in edges: - self.addEdge(*edge) - - def addEdge(self, a, b): - if a not in self.neighbors: self.neighbors[a] = [] - if b not in self.neighbors[a]: self.neighbors[a].append(b) - - if b not in self.neighbors: self.neighbors[b] = [] - if a not in self.neighbors[b]: self.neighbors[b].append(a) - - def get(self, a, b, exclude=lambda node: False): - # Shortest path from a to b - return self._recPath(a, b, [], exclude) - - def _recPath(self, a, b, visited, exclude): - if a == b: return [a] - new_visited = visited + [a] - paths = [] - for neighbor in self.neighbors[a]: - if neighbor in new_visited: continue - if exclude(neighbor) and neighbor != b: continue - path = self._recPath(neighbor, b, new_visited, exclude) - if path: paths.append(path) - - paths.sort(key=len) - return [a] + paths[0] if len(paths) else None - -if __name__ == '__main__': - - edges = [ - (1, 2), - (1, 3), - (1, 5), - (2, 4), - (3, 4), - (3, 5), - (3, 6), - (4, 6), - (5, 6), - (7, 8) - - ] - sp = ShortestPath(edges) - - assert sp.get(1, 1) == [1] - assert sp.get(2, 2) == [2] - - assert sp.get(1, 2) == [1, 2] - assert sp.get(2, 1) == [2, 1] - - assert sp.get(1, 3) == [1, 3] - assert sp.get(3, 1) == [3, 1] - - assert sp.get(4, 6) == [4, 6] - assert sp.get(6, 4) == [6, 4] - - assert sp.get(2, 6) == [2, 4, 6] - assert sp.get(6, 2) == [6, 4, 2] - - assert sp.get(1, 6) in [[1, 3, 6], [1, 5, 6]] - assert sp.get(6, 1) in [[6, 3, 1], [6, 5, 1]] - - assert sp.get(2, 5) == [2, 1, 5] - assert sp.get(5, 2) == [5, 1, 2] - - assert sp.get(4, 5) in [[4, 3, 5], [4, 6, 5]] - assert sp.get(5, 4) in [[5, 3, 4], [6, 6, 4]] - - assert sp.get(7, 8) == [7, 8] - assert sp.get(8, 7) == [8, 7] - - assert sp.get(1, 7) == None - assert sp.get(7, 2) == None - diff --git a/P4D2_2017_Spring/utils/mininet/single_switch_mininet.py b/P4D2_2017_Spring/utils/mininet/single_switch_mininet.py deleted file mode 100755 index e2e76366c..000000000 --- a/P4D2_2017_Spring/utils/mininet/single_switch_mininet.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python2 - -# 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. -# - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.log import setLogLevel, info -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host - -import argparse -from subprocess import PIPE, Popen -from time import sleep - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--num-hosts', help='Number of hosts to connect to switch', - type=int, action="store", default=2) -parser.add_argument('--mode', choices=['l2', 'l3'], type=str, default='l3') -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--log-file', help='Path to write the switch log file', - type=str, action="store", required=False) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - type=str, action="store", required=False, default=False) -parser.add_argument('--switch-config', help='simple_switch_CLI script to configure switch', - type=str, action="store", required=False, default=False) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) - -args = parser.parse_args() - - -class SingleSwitchTopo(Topo): - "Single switch connected to n (< 256) hosts." - def __init__(self, sw_path, json_path, log_file, - thrift_port, pcap_dump, n, **opts): - # Initialize topology and default options - Topo.__init__(self, **opts) - - switch = self.addSwitch('s1', - sw_path = sw_path, - json_path = json_path, - log_console = True, - log_file = log_file, - thrift_port = thrift_port, - enable_debugger = False, - pcap_dump = pcap_dump) - - for h in xrange(n): - host = self.addHost('h%d' % (h + 1), - ip = "10.0.%d.10/24" % h, - mac = '00:04:00:00:00:%02x' %h) - print "Adding host", str(host) - self.addLink(host, switch) - -def main(): - num_hosts = args.num_hosts - mode = args.mode - - topo = SingleSwitchTopo(args.behavioral_exe, - args.json, - args.log_file, - args.thrift_port, - args.pcap_dump, - num_hosts) - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None) - net.start() - - - sw_mac = ["00:aa:bb:00:00:%02x" % n for n in xrange(num_hosts)] - - sw_addr = ["10.0.%d.1" % n for n in xrange(num_hosts)] - - for n in xrange(num_hosts): - h = net.get('h%d' % (n + 1)) - if mode == "l2": - h.setDefaultRoute("dev %s" % h.defaultIntf().name) - else: - h.setARP(sw_addr[n], sw_mac[n]) - h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n])) - - for n in xrange(num_hosts): - h = net.get('h%d' % (n + 1)) - h.describe(sw_addr[n], sw_mac[n]) - - sleep(1) - - if args.switch_config is not None: - print - print "Reading switch configuration script:", args.switch_config - with open(args.switch_config, 'r') as config_file: - switch_config = config_file.read() - - print "Configuring switch..." - proc = Popen(["simple_switch_CLI"], stdin=PIPE) - proc.communicate(input=switch_config) - - print "Configuration complete." - print - - print "Ready !" - - if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: - print message_file.read() - - CLI( net ) - net.stop() - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/P4D2_2017_Spring/utils/p4apprunner.py b/P4D2_2017_Spring/utils/p4apprunner.py deleted file mode 100755 index 36b9eeab8..000000000 --- a/P4D2_2017_Spring/utils/p4apprunner.py +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env python2 -# 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. - -from __future__ import print_function - -import argparse -from collections import OrderedDict -import json -import os -import sys -import tarfile - -parser = argparse.ArgumentParser(description='p4apprunner') -parser.add_argument('--build-dir', help='Directory to build in.', - type=str, action='store', required=False, default='/tmp') -parser.add_argument('--quiet', help='Suppress log messages.', - action='store_true', required=False, default=False) -parser.add_argument('--manifest', help='Path to manifest file.', - type=str, action='store', required=False, default='./p4app.json') -parser.add_argument('app', help='.p4app package to run.', type=str) -parser.add_argument('target', help=('Target to run. Defaults to the first target ' - 'in the package.'), - nargs='?', type=str) - -args = parser.parse_args() - -def log(*items): - if args.quiet != True: - print(*items) - -def log_error(*items): - print(*items, file=sys.stderr) - -def run_command(command): - log('>', command) - return os.WEXITSTATUS(os.system(command)) - -class Manifest: - def __init__(self, program_file, language, target, target_config): - self.program_file = program_file - self.language = language - self.target = target - self.target_config = target_config - -def read_manifest(manifest_file): - manifest = json.load(manifest_file, object_pairs_hook=OrderedDict) - - if 'program' not in manifest: - log_error('No program defined in manifest.') - sys.exit(1) - program_file = manifest['program'] - - if 'language' not in manifest: - log_error('No language defined in manifest.') - sys.exit(1) - language = manifest['language'] - - if 'targets' not in manifest or len(manifest['targets']) < 1: - log_error('No targets defined in manifest.') - sys.exit(1) - - if args.target is not None: - chosen_target = args.target - elif 'default-target' in manifest: - chosen_target = manifest['default-target'] - else: - chosen_target = manifest['targets'].keys()[0] - - if chosen_target not in manifest['targets']: - log_error('Target not found in manifest:', chosen_target) - sys.exit(1) - - return Manifest(program_file, language, chosen_target, manifest['targets'][chosen_target]) - - -def run_compile_bmv2(manifest): - if 'run-before-compile' in manifest.target_config: - commands = manifest.target_config['run-before-compile'] - if not isinstance(commands, list): - log_error('run-before-compile should be a list:', commands) - sys.exit(1) - for command in commands: - run_command(command) - - compiler_args = [] - - if manifest.language == 'p4-14': - compiler_args.append('--p4v 14') - elif manifest.language == 'p4-16': - compiler_args.append('--p4v 16') - else: - log_error('Unknown language:', manifest.language) - sys.exit(1) - - if 'compiler-flags' in manifest.target_config: - flags = manifest.target_config['compiler-flags'] - if not isinstance(flags, list): - log_error('compiler-flags should be a list:', flags) - sys.exit(1) - compiler_args.extend(flags) - - # Compile the program. - output_file = manifest.program_file + '.json' - compiler_args.append('"%s"' % manifest.program_file) - compiler_args.append('-o "%s"' % output_file) - rv = run_command('p4c-bm2-ss %s' % ' '.join(compiler_args)) - - if 'run-after-compile' in manifest.target_config: - commands = manifest.target_config['run-after-compile'] - if not isinstance(commands, list): - log_error('run-after-compile should be a list:', commands) - sys.exit(1) - for command in commands: - run_command(command) - - if rv != 0: - log_error('Compile failed.') - sys.exit(1) - - return output_file - -def run_mininet(manifest): - output_file = run_compile_bmv2(manifest) - - # Run the program using the BMV2 Mininet simple switch. - switch_args = [] - - # We'll place the switch's log file in current (build) folder. - cwd = os.getcwd() - log_file = os.path.join(cwd, manifest.program_file + '.log') - print ("*** Log file %s" % log_file) - switch_args.append('--log-file "%s"' % log_file) - - pcap_dir = os.path.join(cwd) - print ("*** Pcap folder %s" % pcap_dir) - switch_args.append('--pcap-dump "%s" '% pcap_dir) - - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: - - print(file=message) - print('======================================================================', - file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', - file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) - print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI', file=message) - print(file=message) - print('To view the switch log, run this command from your host OS:', file=message) - print(' tail -f %s' % log_file, file=message) - print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) - print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) - - switch_args.append('--cli-message "%s"' % message_file) - - if 'num-hosts' in manifest.target_config: - switch_args.append('--num-hosts %s' % manifest.target_config['num-hosts']) - - if 'switch-config' in manifest.target_config: - switch_args.append('--switch-config "%s"' % manifest.target_config['switch-config']) - - switch_args.append('--behavioral-exe "%s"' % 'simple_switch') - switch_args.append('--json "%s"' % output_file) - - program = '"%s/mininet/single_switch_mininet.py"' % sys.path[0] - return run_command('python2 %s %s' % (program, ' '.join(switch_args))) - -def run_multiswitch(manifest): - output_file = run_compile_bmv2(manifest) - - script_args = [] - cwd = os.getcwd() - log_dir = os.path.join(cwd, cwd + '/logs') - print ("*** Log directory %s" % log_dir) - script_args.append('--log-dir "%s"' % log_dir) - pcap_dir = os.path.join(cwd) - print ("*** Pcap directory %s" % cwd) - script_args.append('--manifest "%s"' % args.manifest) - script_args.append('--target "%s"' % manifest.target) - if 'auto-control-plane' in manifest.target_config and manifest.target_config['auto-control-plane']: - script_args.append('--auto-control-plane' ) - script_args.append('--behavioral-exe "%s"' % 'simple_switch') - script_args.append('--json "%s"' % output_file) - #script_args.append('--cli') - - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: - - print(file=message) - print('======================================================================', - file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', - file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) - print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI --thrift-port ', file=message) - print(file=message) - print('To view a switch log, run this command from your host OS:', file=message) - print(' tail -f %s/.log' % log_dir, file=message) - print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) - print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) - - script_args.append('--cli-message "%s"' % message_file) - - program = '"%s/mininet/multi_switch_mininet.py"' % sys.path[0] - return run_command('python2 %s %s' % (program, ' '.join(script_args))) - -def run_stf(manifest): - output_file = run_compile_bmv2(manifest) - - if not 'test' in manifest.target_config: - log_error('No STF test file provided.') - sys.exit(1) - stf_file = manifest.target_config['test'] - - # Run the program using the BMV2 STF interpreter. - stf_args = [] - stf_args.append('-v') - stf_args.append(os.path.join(args.build_dir, output_file)) - stf_args.append(os.path.join(args.build_dir, stf_file)) - - program = '"%s/stf/bmv2stf.py"' % sys.path[0] - rv = run_command('python2 %s %s' % (program, ' '.join(stf_args))) - if rv != 0: - sys.exit(1) - return rv - -def run_custom(manifest): - output_file = run_compile_bmv2(manifest) - python_path = 'PYTHONPATH=$PYTHONPATH:/scripts/mininet/' - script_args = [] - script_args.append('--behavioral-exe "%s"' % 'simple_switch') - script_args.append('--json "%s"' % output_file) - script_args.append('--cli "%s"' % 'simple_switch_CLI') - if not 'program' in manifest.target_config: - log_error('No mininet program file provided.') - sys.exit(1) - program = manifest.target_config['program'] - rv = run_command('%s python2 %s %s' % (python_path, program, ' '.join(script_args))) - - if rv != 0: - sys.exit(1) - return rv - -def main(): - log('Entering build directory.') - os.chdir(args.build_dir) - - # A '.p4app' package is really just a '.tar.gz' archive. Extract it so we - # can process its contents. - log('Extracting package.') - tar = tarfile.open(args.app) - tar.extractall() - tar.close() - - log('Reading package manifest.') - with open(args.manifest, 'r') as manifest_file: - manifest = read_manifest(manifest_file) - - # Dispatch to the backend implementation for this target. - backend = manifest.target - if 'use' in manifest.target_config: - backend = manifest.target_config['use'] - - if backend == 'mininet': - rc = run_mininet(manifest) - elif backend == 'multiswitch': - rc = run_multiswitch(manifest) - elif backend == 'stf': - rc = run_stf(manifest) - elif backend == 'custom': - rc = run_custom(manifest) - elif backend == 'compile-bmv2': - run_compile_bmv2(manifest) - rc = 0 - else: - log_error('Target specifies unknown backend:', backend) - sys.exit(1) - - sys.exit(rc) - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/P4_tutorial_labs.pdf b/P4D2_2018_East/P4_tutorial_labs.pdf deleted file mode 100644 index 687663cc8..000000000 Binary files a/P4D2_2018_East/P4_tutorial_labs.pdf and /dev/null differ diff --git a/P4D2_2018_East/exercises/README.md b/P4D2_2018_East/exercises/README.md deleted file mode 100644 index 6e95c093c..000000000 --- a/P4D2_2018_East/exercises/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# P4 Tutorial - -## Introduction - -Welcome to the P4 Tutorial! - -We've prepared a set of exercises to help you get started with P4 -programming, organized into four modules: - -1. Introduction and Language Basics -* [Basic Forwarding](./basic) -* [Basic Tunneling](./basic_tunnel) - -2. P4 Runtime and the Control Plane -* [P4 Runtime](./p4runtime) - -3. Monitoring and Debugging -* [Explicit Congestion Notification](./ecn) -* [Multi-Hop Route Inspection](./mri) - -4. Advanced Data Structures -* [Source Routing](./source_routing) -* [Calculator](./calc) - -5. Dynamic Behavior -* [Load Balancing](./load_balance) - -## Obtaining required software - -If you are starting this tutorial at the Fall 2017 P4 Developer Day, then we've already -provided you with a virtual machine that has all of the required -software installed. - -Otherwise, to complete the exercises, you will need to either build a -virtual machine or install several dependencies. - -To build the virtual machine: -- Install [Vagrant](https://vagrantup.com) and [VirtualBox](https://virtualbox.org) -- `cd vm` -- `vagrant up` -- Log in with username `p4` and password `p4` and issue the command `sudo shutdown -r now` -- When the machine reboots, you should have a graphical desktop machine with the required -software pre-installed. - -To install dependencies by hand, please reference the [vm](../vm) installation scripts. -They contain the dependencies, versions, and installation procedure. -You can run them directly on an Ubuntu 16.04 machine: -- `sudo ./root-bootstrap.sh` -- `sudo ./user-bootstrap.sh` diff --git a/P4D2_2018_East/exercises/basic/Makefile b/P4D2_2018_East/exercises/basic/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2018_East/exercises/basic/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2018_East/exercises/basic/basic.p4 b/P4D2_2018_East/exercises/basic/basic.p4 deleted file mode 100644 index 8b89237a8..000000000 --- a/P4D2_2018_East/exercises/basic/basic.p4 +++ /dev/null @@ -1,162 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - /* TODO: add parser logic */ - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - /* TODO: fill out code in action body */ - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - /* TODO: fix ingress control logic - * - ipv4_lpm should be applied only when IPv4 header is valid - */ - ipv4_lpm.apply(); - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - /* TODO: add deparser logic */ - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/basic/receive.py b/P4D2_2018_East/exercises/basic/receive.py deleted file mode 100755 index f72968210..000000000 --- a/P4D2_2018_East/exercises/basic/receive.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -import sys -import struct -import os - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, TCP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - if TCP in pkt and pkt[TCP].dport == 1234: - print "got a packet" - pkt.show2() - # hexdump(pkt) - sys.stdout.flush() - - -def main(): - ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/')) - iface = ifaces[0] - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/basic/s1-commands.txt b/P4D2_2018_East/exercises/basic/s1-commands.txt deleted file mode 100644 index 3bfdf6156..000000000 --- a/P4D2_2018_East/exercises/basic/s1-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 diff --git a/P4D2_2018_East/exercises/basic/s2-commands.txt b/P4D2_2018_East/exercises/basic/s2-commands.txt deleted file mode 100644 index 35a49d7a0..000000000 --- a/P4D2_2018_East/exercises/basic/s2-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 diff --git a/P4D2_2018_East/exercises/basic/s3-commands.txt b/P4D2_2018_East/exercises/basic/s3-commands.txt deleted file mode 100644 index c111108a2..000000000 --- a/P4D2_2018_East/exercises/basic/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:03:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 diff --git a/P4D2_2018_East/exercises/basic/send.py b/P4D2_2018_East/exercises/basic/send.py deleted file mode 100755 index 00496d9de..000000000 --- a/P4D2_2018_East/exercises/basic/send.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr -from scapy.all import Packet -from scapy.all import Ether, IP, UDP, TCP - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - print "sending on interface %s to %s" % (iface, str(addr)) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2] - pkt.show2() - sendp(pkt, iface=iface, verbose=False) - - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/basic/solution/basic.p4 b/P4D2_2018_East/exercises/basic/solution/basic.p4 deleted file mode 100644 index 7d491ea43..000000000 --- a/P4D2_2018_East/exercises/basic/solution/basic.p4 +++ /dev/null @@ -1,176 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/basic/topology.json b/P4D2_2018_East/exercises/basic/topology.json deleted file mode 100644 index e33477cc9..000000000 --- a/P4D2_2018_East/exercises/basic/topology.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "hosts": [ - "h1", - "h2", - "h3" - ], - "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } - }, - "links": [ - ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], - ["s3", "s2"], ["s2", "h2"], ["s3", "h3"] - ] -} diff --git a/P4D2_2018_East/exercises/basic_tunnel/Makefile b/P4D2_2018_East/exercises/basic_tunnel/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2018_East/exercises/basic_tunnel/myTunnel_header.py b/P4D2_2018_East/exercises/basic_tunnel/myTunnel_header.py deleted file mode 100644 index 5a5922ec8..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/myTunnel_header.py +++ /dev/null @@ -1,20 +0,0 @@ - -from scapy.all import * -import sys, os - -TYPE_MYTUNNEL = 0x1212 -TYPE_IPV4 = 0x0800 - -class MyTunnel(Packet): - name = "MyTunnel" - fields_desc = [ - ShortField("pid", 0), - ShortField("dst_id", 0) - ] - def mysummary(self): - return self.sprintf("pid=%pid%, dst_id=%dst_id%") - - -bind_layers(Ether, MyTunnel, type=TYPE_MYTUNNEL) -bind_layers(MyTunnel, IP, pid=TYPE_IPV4) - diff --git a/P4D2_2018_East/exercises/basic_tunnel/receive.py b/P4D2_2018_East/exercises/basic_tunnel/receive.py deleted file mode 100755 index 167a9c867..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/receive.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -import sys -import struct -import os - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, TCP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR -from myTunnel_header import MyTunnel - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def handle_pkt(pkt): - if MyTunnel in pkt or (TCP in pkt and pkt[TCP].dport == 1234): - print "got a packet" - pkt.show2() -# hexdump(pkt) -# print "len(pkt) = ", len(pkt) - sys.stdout.flush() - - -def main(): - ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/')) - iface = ifaces[0] - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/basic_tunnel/s1-commands.txt b/P4D2_2018_East/exercises/basic_tunnel/s1-commands.txt deleted file mode 100644 index 179d7a550..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/s1-commands.txt +++ /dev/null @@ -1,9 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:01:00 2 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:01:00 3 - -table_set_default myTunnel_exact drop -table_add myTunnel_exact myTunnel_forward 1 => 1 -table_add myTunnel_exact myTunnel_forward 2 => 2 -table_add myTunnel_exact myTunnel_forward 3 => 3 diff --git a/P4D2_2018_East/exercises/basic_tunnel/s2-commands.txt b/P4D2_2018_East/exercises/basic_tunnel/s2-commands.txt deleted file mode 100644 index fb5a2bc4a..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/s2-commands.txt +++ /dev/null @@ -1,9 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:02:00 3 - -table_set_default myTunnel_exact drop -table_add myTunnel_exact myTunnel_forward 1 => 2 -table_add myTunnel_exact myTunnel_forward 2 => 1 -table_add myTunnel_exact myTunnel_forward 3 => 3 diff --git a/P4D2_2018_East/exercises/basic_tunnel/s3-commands.txt b/P4D2_2018_East/exercises/basic_tunnel/s3-commands.txt deleted file mode 100644 index a6c2777a2..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/s3-commands.txt +++ /dev/null @@ -1,9 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:03:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 - -table_set_default myTunnel_exact drop -table_add myTunnel_exact myTunnel_forward 1 => 2 -table_add myTunnel_exact myTunnel_forward 2 => 3 -table_add myTunnel_exact myTunnel_forward 3 => 1 diff --git a/P4D2_2018_East/exercises/basic_tunnel/send.py b/P4D2_2018_East/exercises/basic_tunnel/send.py deleted file mode 100755 index 4f9828c3e..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/send.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct -import argparse - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr, hexdump -from scapy.all import Packet -from scapy.all import Ether, IP, UDP, TCP -from myTunnel_header import MyTunnel - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('ip_addr', type=str, help="The destination IP address to use") - parser.add_argument('message', type=str, help="The message to include in packet") - parser.add_argument('--dst_id', type=int, default=None, help='The myTunnel dst_id to use, if unspecified then myTunnel header will not be included in packet') - args = parser.parse_args() - - addr = socket.gethostbyname(args.ip_addr) - dst_id = args.dst_id - iface = get_if() - - if (dst_id is not None): - print "sending on interface {} to dst_id {}".format(iface, str(dst_id)) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt / MyTunnel(dst_id=dst_id) / IP(dst=addr) / args.message - else: - print "sending on interface {} to IP addr {}".format(iface, str(addr)) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt / IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / args.message - - pkt.show2() -# hexdump(pkt) -# print "len(pkt) = ", len(pkt) - sendp(pkt, iface=iface, verbose=False) - - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/basic_tunnel/solution/basic_tunnel.p4 b/P4D2_2018_East/exercises/basic_tunnel/solution/basic_tunnel.p4 deleted file mode 100644 index 83b29dbc5..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/solution/basic_tunnel.p4 +++ /dev/null @@ -1,215 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_MYTUNNEL = 0x1212; -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header myTunnel_t { - bit<16> proto_id; - bit<16> dst_id; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - myTunnel_t myTunnel; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_MYTUNNEL: parse_myTunnel; - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_myTunnel { - packet.extract(hdr.myTunnel); - transition select(hdr.myTunnel.proto_id) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - action myTunnel_forward(egressSpec_t port) { - standard_metadata.egress_spec = port; - } - - table myTunnel_exact { - key = { - hdr.myTunnel.dst_id: exact; - } - actions = { - myTunnel_forward; - drop; - } - size = 1024; - default_action = drop(); - } - - apply { - if (hdr.ipv4.isValid() && !hdr.myTunnel.isValid()) { - // Process only non-tunneled IPv4 packets - ipv4_lpm.apply(); - } - - if (hdr.myTunnel.isValid()) { - // process tunneled packets - myTunnel_exact.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.myTunnel); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/basic_tunnel/topo.pdf b/P4D2_2018_East/exercises/basic_tunnel/topo.pdf deleted file mode 100644 index 111acd608..000000000 Binary files a/P4D2_2018_East/exercises/basic_tunnel/topo.pdf and /dev/null differ diff --git a/P4D2_2018_East/exercises/basic_tunnel/topo.png b/P4D2_2018_East/exercises/basic_tunnel/topo.png deleted file mode 100644 index 12ff49e1d..000000000 Binary files a/P4D2_2018_East/exercises/basic_tunnel/topo.png and /dev/null differ diff --git a/P4D2_2018_East/exercises/basic_tunnel/topology.json b/P4D2_2018_East/exercises/basic_tunnel/topology.json deleted file mode 100644 index e33477cc9..000000000 --- a/P4D2_2018_East/exercises/basic_tunnel/topology.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "hosts": [ - "h1", - "h2", - "h3" - ], - "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } - }, - "links": [ - ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], - ["s3", "s2"], ["s2", "h2"], ["s3", "h3"] - ] -} diff --git a/P4D2_2018_East/exercises/calc/Makefile b/P4D2_2018_East/exercises/calc/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2018_East/exercises/calc/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2018_East/exercises/calc/README.md b/P4D2_2018_East/exercises/calc/README.md deleted file mode 100644 index 4b6d92b4f..000000000 --- a/P4D2_2018_East/exercises/calc/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# Implementing a P4 Calculator - -## Introduction - -The objective of this tutorial is to implement a basic calculator -using a custom protocol header written in P4. The header will contain -an operation to perform and two operands. When a switch receives a -calculator packet header, it will execute the operation on the -operands, and return the result to the sender. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`calc.p4`, which initially drops all packets. Your job will be to -extend it to properly implement the calculator logic. - -As a first step, compile the incomplete `calc.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - make - ``` - This will: - * compile `calc.p4`, and - - * start a Mininet instance with one switches (`s1`) connected to - two hosts (`h1`, `h2`). - * The hosts are assigned IPs of `10.0.1.1` and `10.0.1.2`. - -2. We've written a small Python-based driver program that will allow -you to test your calculator. You can run the driver program directly -from the Mininet command prompt: - -``` -mininet> h1 python calc.py -> -``` - -3. The driver program will provide a new prompt, at which you can type -basic expressions. The test harness will parse your expression, and -prepare a packet with the corresponding operator and operands. It will -then send a packet to the switch for evaluation. When the switch -returns the result of the computation, the test program will print the -result. However, because the calculator program is not implemented, -you should see an error message. - -``` -> 1+1 -Didn't receive response -> -``` - -## Step 2: Implement Calculator - -To implement the calculator, you will need to define a custom -calculator header, and implement the switch logic to parse header, -perform the requested operation, write the result in the header, and -return the packet to the sender. - -We will use the following header format: - - 0 1 2 3 - +----------------+----------------+----------------+---------------+ - | P | 4 | Version | Op | - +----------------+----------------+----------------+---------------+ - | Operand A | - +----------------+----------------+----------------+---------------+ - | Operand B | - +----------------+----------------+----------------+---------------+ - | Result | - +----------------+----------------+----------------+---------------+ - - -- P is an ASCII Letter 'P' (0x50) -- 4 is an ASCII Letter '4' (0x34) -- Version is currently 0.1 (0x01) -- Op is an operation to Perform: - - '+' (0x2b) Result = OperandA + OperandB - - '-' (0x2d) Result = OperandA - OperandB - - '&' (0x26) Result = OperandA & OperandB - - '|' (0x7c) Result = OperandA | OperandB - - '^' (0x5e) Result = OperandA ^ OperandB - - -We will assume that the calculator header is carried over Ethernet, -and we will use the Ethernet type 0x1234 to indicate the presence of -the header. - -Given what you have learned so far, your task is to implement the P4 -calculator program. There is no control plane logic, so you need only -worry about the data plane implementation. - -A working calculator implementation will parse the custom headers, -execute the mathematical operation, write the result in the result -field, and return the packet to the sender. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, you should see the -correct result: - -``` -> 1+1 -2 -> -``` - -## Next Steps - -Congratulations, your implementation works! Move on to -[Load Balancer](../load_balance). diff --git a/P4D2_2018_East/exercises/calc/calc.p4 b/P4D2_2018_East/exercises/calc/calc.p4 deleted file mode 100644 index 557cbc5c8..000000000 --- a/P4D2_2018_East/exercises/calc/calc.p4 +++ /dev/null @@ -1,250 +0,0 @@ -/* -*- P4_16 -*- */ - -/* - * P4 Calculator - * - * This program implements a simple protocol. It can be carried over Ethernet - * (Ethertype 0x1234). - * - * The Protocol header looks like this: - * - * 0 1 2 3 - * +----------------+----------------+----------------+---------------+ - * | P | 4 | Version | Op | - * +----------------+----------------+----------------+---------------+ - * | Operand A | - * +----------------+----------------+----------------+---------------+ - * | Operand B | - * +----------------+----------------+----------------+---------------+ - * | Result | - * +----------------+----------------+----------------+---------------+ - * - * P is an ASCII Letter 'P' (0x50) - * 4 is an ASCII Letter '4' (0x34) - * Version is currently 0.1 (0x01) - * Op is an operation to Perform: - * '+' (0x2b) Result = OperandA + OperandB - * '-' (0x2d) Result = OperandA - OperandB - * '&' (0x26) Result = OperandA & OperandB - * '|' (0x7c) Result = OperandA | OperandB - * '^' (0x5e) Result = OperandA ^ OperandB - * - * The device receives a packet, performs the requested operation, fills in the - * result and sends the packet back out of the same port it came in on, while - * swapping the source and destination addresses. - * - * If an unknown operation is specified or the header is not valid, the packet - * is dropped - */ - -#include -#include - -/* - * Define the headers the program will recognize - */ - -/* - * Standard Ethernet header - */ -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -/* - * This is a custom protocol header for the calculator. We'll use - * etherType 0x1234 for it (see parser) - */ -const bit<16> P4CALC_ETYPE = 0x1234; -const bit<8> P4CALC_P = 0x50; // 'P' -const bit<8> P4CALC_4 = 0x34; // '4' -const bit<8> P4CALC_VER = 0x01; // v0.1 -const bit<8> P4CALC_PLUS = 0x2b; // '+' -const bit<8> P4CALC_MINUS = 0x2d; // '-' -const bit<8> P4CALC_AND = 0x26; // '&' -const bit<8> P4CALC_OR = 0x7c; // '|' -const bit<8> P4CALC_CARET = 0x5e; // '^' - -header p4calc_t { - bit<8> op; -/* TODO - * fill p4calc_t header with P, four, ver, op, operand_a, operand_b, and res - entries based on above protocol header definition. - */ -} - -/* - * All headers, used in the program needs to be assembled into a single struct. - * We only need to declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ -struct headers { - ethernet_t ethernet; - p4calc_t p4calc; -} - -/* - * All metadata, globally used in the program, also needs to be assembled - * into a single struct. As in the case of the headers, we only need to - * declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ - -struct metadata { - /* In our case it is empty */ -} - -/************************************************************************* - *********************** P A R S E R *********************************** - *************************************************************************/ -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - state start { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - P4CALC_ETYPE : check_p4calc; - default : accept; - } - } - - state check_p4calc { - /* TODO: just uncomment the following parse block */ - /* - transition select(packet.lookahead().p, - packet.lookahead().four, - packet.lookahead().ver) { - (P4CALC_P, P4CALC_4, P4CALC_VER) : parse_p4calc; - default : accept; - } - */ - } - - state parse_p4calc { - packet.extract(hdr.p4calc); - transition accept; - } -} - -/************************************************************************* - ************ C H E C K S U M V E R I F I C A T I O N ************* - *************************************************************************/ -control MyVerifyChecksum(inout headers hdr, - inout metadata meta) { - apply { } -} - -/************************************************************************* - ************** I N G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action send_back(bit<32> result) { - /* TODO - * - put the result back in hdr.p4calc.res - * - swap MAC addresses in hdr.ethernet.dstAddr and - * hdr.ethernet.srcAddr using a temp variable - * - Send the packet back to the port it came from - by saving standard_metadata.ingress_port into - standard_metadata.egress_spec - */ - } - - action operation_add() { - /* TODO call send_back with operand_a + operand_b */ - } - - action operation_sub() { - /* TODO call send_back with operand_a - operand_b */ - } - - action operation_and() { - /* TODO call send_back with operand_a & operand_b */ - } - - action operation_or() { - /* TODO call send_back with operand_a | operand_b */ - } - - action operation_xor() { - /* TODO call send_back with operand_a ^ operand_b */ - } - - action operation_drop() { - mark_to_drop(); - } - - table calculate { - key = { - hdr.p4calc.op : exact; - } - actions = { - operation_add; - operation_sub; - operation_and; - operation_or; - operation_xor; - operation_drop; - } - const default_action = operation_drop(); - const entries = { - P4CALC_PLUS : operation_add(); - P4CALC_MINUS: operation_sub(); - P4CALC_AND : operation_and(); - P4CALC_OR : operation_or(); - P4CALC_CARET: operation_xor(); - } - } - - apply { - if (hdr.p4calc.isValid()) { - calculate.apply(); - } else { - operation_drop(); - } - } -} - -/************************************************************************* - **************** E G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* - ************* C H E C K S U M C O M P U T A T I O N ************** - *************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* - *********************** D E P A R S E R ******************************* - *************************************************************************/ -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.p4calc); - } -} - -/************************************************************************* - *********************** S W I T T C H ********************************** - *************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/calc/calc.py b/P4D2_2018_East/exercises/calc/calc.py deleted file mode 100755 index d2fb035a5..000000000 --- a/P4D2_2018_East/exercises/calc/calc.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys -import socket -import random -import struct -import re - -from scapy.all import sendp, send, srp1 -from scapy.all import Packet, hexdump -from scapy.all import Ether, StrFixedLenField, XByteField, IntField -from scapy.all import bind_layers -import readline - -class P4calc(Packet): - name = "P4calc" - fields_desc = [ StrFixedLenField("P", "P", length=1), - StrFixedLenField("Four", "4", length=1), - XByteField("version", 0x01), - StrFixedLenField("op", "+", length=1), - IntField("operand_a", 0), - IntField("operand_b", 0), - IntField("result", 0xDEADBABE)] - -bind_layers(Ether, P4calc, type=0x1234) - -class NumParseError(Exception): - pass - -class OpParseError(Exception): - pass - -class Token: - def __init__(self,type,value = None): - self.type = type - self.value = value - -def num_parser(s, i, ts): - pattern = "^\s*([0-9]+)\s*" - match = re.match(pattern,s[i:]) - if match: - ts.append(Token('num', match.group(1))) - return i + match.end(), ts - raise NumParseError('Expected number literal.') - - -def op_parser(s, i, ts): - pattern = "^\s*([-+&|^])\s*" - match = re.match(pattern,s[i:]) - if match: - ts.append(Token('num', match.group(1))) - return i + match.end(), ts - raise NumParseError("Expected binary operator '-', '+', '&', '|', or '^'.") - - -def make_seq(p1, p2): - def parse(s, i, ts): - i,ts2 = p1(s,i,ts) - return p2(s,i,ts2) - return parse - - -def main(): - - p = make_seq(num_parser, make_seq(op_parser,num_parser)) - s = '' - iface = 'h1-eth0' - - while True: - s = str(raw_input('> ')) - if s == "quit": - break - print s - try: - i,ts = p(s,0,[]) - pkt = Ether(dst='00:04:00:00:00:00', type=0x1234) / P4calc(op=ts[1].value, - operand_a=int(ts[0].value), - operand_b=int(ts[2].value)) - pkt = pkt/' ' - -# pkt.show() - resp = srp1(pkt, iface=iface, timeout=1, verbose=False) - if resp: - p4calc=resp[P4calc] - if p4calc: - print p4calc.result - else: - print "cannot find P4calc header in the packet" - else: - print "Didn't receive response" - except Exception as error: - print error - - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/calc/s1-commands.txt b/P4D2_2018_East/exercises/calc/s1-commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2018_East/exercises/calc/solution/calc.p4 b/P4D2_2018_East/exercises/calc/solution/calc.p4 deleted file mode 100644 index 3c7e86c8a..000000000 --- a/P4D2_2018_East/exercises/calc/solution/calc.p4 +++ /dev/null @@ -1,256 +0,0 @@ -/* -*- P4_16 -*- */ - -/* - * P4 Calculator - * - * This program implements a simple protocol. It can be carried over Ethernet - * (Ethertype 0x1234). - * - * The Protocol header looks like this: - * - * 0 1 2 3 - * +----------------+----------------+----------------+---------------+ - * | P | 4 | Version | Op | - * +----------------+----------------+----------------+---------------+ - * | Operand A | - * +----------------+----------------+----------------+---------------+ - * | Operand B | - * +----------------+----------------+----------------+---------------+ - * | Result | - * +----------------+----------------+----------------+---------------+ - * - * P is an ASCII Letter 'P' (0x50) - * 4 is an ASCII Letter '4' (0x34) - * Version is currently 0.1 (0x01) - * Op is an operation to Perform: - * '+' (0x2b) Result = OperandA + OperandB - * '-' (0x2d) Result = OperandA - OperandB - * '&' (0x26) Result = OperandA & OperandB - * '|' (0x7c) Result = OperandA | OperandB - * '^' (0x5e) Result = OperandA ^ OperandB - * - * The device receives a packet, performs the requested operation, fills in the - * result and sends the packet back out of the same port it came in on, while - * swapping the source and destination addresses. - * - * If an unknown operation is specified or the header is not valid, the packet - * is dropped - */ - -#include -#include - -/* - * Define the headers the program will recognize - */ - -/* - * Standard ethernet header - */ -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -/* - * This is a custom protocol header for the calculator. We'll use - * ethertype 0x1234 for is (see parser) - */ -const bit<16> P4CALC_ETYPE = 0x1234; -const bit<8> P4CALC_P = 0x50; // 'P' -const bit<8> P4CALC_4 = 0x34; // '4' -const bit<8> P4CALC_VER = 0x01; // v0.1 -const bit<8> P4CALC_PLUS = 0x2b; // '+' -const bit<8> P4CALC_MINUS = 0x2d; // '-' -const bit<8> P4CALC_AND = 0x26; // '&' -const bit<8> P4CALC_OR = 0x7c; // '|' -const bit<8> P4CALC_CARET = 0x5e; // '^' - -header p4calc_t { - bit<8> p; - bit<8> four; - bit<8> ver; - bit<8> op; - bit<32> operand_a; - bit<32> operand_b; - bit<32> res; -} - -/* - * All headers, used in the program needs to be assembed into a single struct. - * We only need to declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ -struct headers { - ethernet_t ethernet; - p4calc_t p4calc; -} - -/* - * All metadata, globally used in the program, also needs to be assembed - * into a single struct. As in the case of the headers, we only need to - * declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ - -struct metadata { - /* In our case it is empty */ -} - -/************************************************************************* - *********************** P A R S E R *********************************** - *************************************************************************/ -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - P4CALC_ETYPE : check_p4calc; - default : accept; - } - } - - state check_p4calc { - transition select(packet.lookahead().p, - packet.lookahead().four, - packet.lookahead().ver) { - (P4CALC_P, P4CALC_4, P4CALC_VER) : parse_p4calc; - default : accept; - } - } - - state parse_p4calc { - packet.extract(hdr.p4calc); - transition accept; - } -} - -/************************************************************************* - ************ C H E C K S U M V E R I F I C A T I O N ************* - *************************************************************************/ -control MyVerifyChecksum(inout headers hdr, - inout metadata meta) { - apply { } -} - -/************************************************************************* - ************** I N G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action send_back(bit<32> result) { - bit<48> tmp; - - /* Put the result back in */ - hdr.p4calc.res = result; - - /* Swap the MAC addresses */ - tmp = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = hdr.ethernet.srcAddr; - hdr.ethernet.srcAddr = tmp; - - /* Send the packet back to the port it came from */ - standard_metadata.egress_spec = standard_metadata.ingress_port; - } - - action operation_add() { - send_back(hdr.p4calc.operand_a + hdr.p4calc.operand_b); - } - - action operation_sub() { - send_back(hdr.p4calc.operand_a - hdr.p4calc.operand_b); - } - - action operation_and() { - send_back(hdr.p4calc.operand_a & hdr.p4calc.operand_b); - } - - action operation_or() { - send_back(hdr.p4calc.operand_a | hdr.p4calc.operand_b); - } - - action operation_xor() { - send_back(hdr.p4calc.operand_a ^ hdr.p4calc.operand_b); - } - - action operation_drop() { - mark_to_drop(); - } - - table calculate { - key = { - hdr.p4calc.op : exact; - } - actions = { - operation_add; - operation_sub; - operation_and; - operation_or; - operation_xor; - operation_drop; - } - const default_action = operation_drop(); - const entries = { - P4CALC_PLUS : operation_add(); - P4CALC_MINUS: operation_sub(); - P4CALC_AND : operation_and(); - P4CALC_OR : operation_or(); - P4CALC_CARET: operation_xor(); - } - } - - - apply { - if (hdr.p4calc.isValid()) { - calculate.apply(); - } else { - operation_drop(); - } - } -} - -/************************************************************************* - **************** E G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* - ************* C H E C K S U M C O M P U T A T I O N ************** - *************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* - *********************** D E P A R S E R ******************************* - *************************************************************************/ -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.p4calc); - } -} - -/************************************************************************* - *********************** S W I T T C H ********************************** - *************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/ecn/Makefile b/P4D2_2018_East/exercises/ecn/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2018_East/exercises/ecn/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2018_East/exercises/ecn/README.md b/P4D2_2018_East/exercises/ecn/README.md deleted file mode 100644 index eb30bb6b4..000000000 --- a/P4D2_2018_East/exercises/ecn/README.md +++ /dev/null @@ -1,198 +0,0 @@ -# Implementing ECN - -## Introduction - -The objective of this tutorial is to extend basic L3 forwarding with -an implementation of Explicit Congestion Notification (ECN). - -ECN allows end-to-end notification of network congestion without -dropping packets. If an end-host supports ECN, it puts the value of 1 -or 2 in the `ipv4.ecn` field. For such packets, each switch may -change the value to 3 if the queue size is larger than a threshold. -The receiver copies the value to sender, and the sender can lower the -rate. - -As before, we have already defined the control plane rules for -routing, so you only need to implement the data plane logic of your P4 -program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`ecn.p4`, which initially implements L3 forwarding. Your job (in the -next step) will be to extend it to properly append set the ECN bits - -Before that, let's compile the incomplete `ecn.p4` and bring up a -network in Mininet to test its behavior. - -1. In your shell, run: - ```bash - make - ``` - This will: - * compile `ecn.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle. There are 5 hosts. `h1` and `h11` are connected to `s1`. - `h2` and `h22` are connected to `s2` and `h3` is connected to `s3`. - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc - (`10.0..`). - * The control plane programs the P4 tables in each switch based on - `sx-commands.txt` - -2. We want to send a low rate traffic from `h1` to `h2` and a high -rate iperf traffic from `h11` to `h22`. The link between `s1` and -`s2` is common between the flows and is a bottleneck because we -reduced its bandwidth to 512kbps in topology.json. Therefore, if we -capture packets at `h2`, we should see the right ECN value. - -![Setup](setup.png) - -3. You should now see a Mininet command prompt. Open four terminals -for `h1`, `h11`, `h2`, `h22`, respectively: - ```bash - mininet> xterm h1 h11 h2 h22 - ``` -3. In `h2`'s XTerm, start the server that captures packets: - ```bash - ./receive.py - ``` -4. in `h22`'s XTerm, start the iperf UDP server: - ```bash - iperf -s -u - ``` -5. In `h1`'s XTerm, send one packet per second to `h2` using send.py -say for 30 seconds: - ```bash - ./send.py 10.0.2.2 "P4 is cool" 30 - ``` - The message "P4 is cool" should be received in `h2`'s xterm, -6. In `h11`'s XTerm, start iperf client sending for 15 seconds - ```bash - iperf -c 10.0.2.22 -t 15 -u - ``` -7. At `h2`, the `ipv4.tos` field (DiffServ+ECN) is always 1 -8. type `exit` to close each XTerm window - -Your job is to extend the code in `ecn.p4` to implement the ECN logic -for setting the ECN flag. - -## Step 2: Implement ECN - -The `ecn.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the -missing piece. - -First we have to change the ipv4_t header by splitting the TOS field -into DiffServ and ECN fields. Remember to update the checksum block -accordingly. Then, in the egress control block we must compare the -queue length with ECN_THRESHOLD. If the queue length is larger than -the threshold, the ECN flag will be set. Note that this logic should -happen only if the end-host declared supporting ECN by setting the -original ECN to 1 or 2. - -A complete `ecn.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 (`ipv4_t`). -2. Parsers for Ethernet, IPv4, -3. An action to drop a packet, using `mark_to_drop()`. -4. An action (called `ipv4_forward`), which will: - 1. Set the egress port for the next hop. - 2. Update the ethernet destination address with the address of - the next hop. - 3. Update the ethernet source address with the address of the switch. - 4. Decrement the TTL. -5. An egress control block that checks the ECN and -`standard_metadata.enq_qdepth` and sets the ipv4.ecn. -6. A deparser that selects the order in which fields inserted into the outgoing - packet. -7. A `package` instantiation supplied with the parser, control, - checksum verification and recomputation and deparser. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, when your message from -`h1` is delivered to `h2`, you should see `tos` values change from 1 -to 3 as the queue builds up. `tos` may change back to 1 when iperf -finishes and the queue depletes. - -To easily track the `tos` values you may want to redirect the output -of `h2` to a file by running the following for `h2` - ```bash - ./receive.py > h2.log - ``` -and just print the `tos` values `grep tos h2.log` in a separate window -``` - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x3 - tos = 0x3 - tos = 0x3 - tos = 0x3 - tos = 0x3 - tos = 0x3 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 -``` - -### Food for thought - -How can we let the user configure the threshold? - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `ecn.p4` fails to compile. In this case, `make` will report the - error emitted from the compiler and stop. -2. `ecn.p4` compiles but does not support the control plane rules in - the `sX-commands.txt` files that `make` tries to install using - the BMv2 CLI. In this case, `make` will log the CLI tool output - in the `logs` directory. Use these error messages to fix your `ecn.p4` - implementation. -3. `ecn.p4` compiles, and the control plane rules are installed, but - the switch does not process packets in the desired way. The - `/tmp/p4s..log` files contain trace messages - describing how each switch processes each packet. The output is - detailed and can help pinpoint logic errors in your implementation. - The `build/-.pcap` also contains the - pcap of packets on each interface. Use `tcpdump -r -xxx` - to print the hexdump of the packets. -4. `ecn.p4` compiles and all rules are installed. Packets go through - and the logs show that the queue length was not high enough to set - the ECN bit. Then either lower the threshold in the p4 code or - reduce the link bandwidth in `topology.json` - -#### Cleaning up Mininet - -In the latter two cases above, `make` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -make stop -``` - -## Next Steps - -Congratulations, your implementation works! Move on to the next -exercise: [Multi-Hop Route Inspection](../mri), which identifies which -link is the source of congestion. diff --git a/P4D2_2018_East/exercises/ecn/ecn.p4 b/P4D2_2018_East/exercises/ecn/ecn.p4 deleted file mode 100644 index 576169f30..000000000 --- a/P4D2_2018_East/exercises/ecn/ecn.p4 +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> TCP_PROTOCOL = 0x06; -const bit<16> TYPE_IPV4 = 0x800; -const bit<19> ECN_THRESHOLD = 10; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -/* - * TODO: split tos to two fields 6 bit diffserv and 2 bit ecn - */ -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> tos; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { - /* - * TODO: - * - if ecn is 1 or 2 - * - compare standard_metadata.enq_qdepth with threshold - * and set hdr.ipv4.ecn to 3 if larger - */ - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - /* TODO: replace tos with diffserve and ecn */ - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.tos, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/ecn/receive.py b/P4D2_2018_East/exercises/ecn/receive.py deleted file mode 100755 index ca5f11903..000000000 --- a/P4D2_2018_East/exercises/ecn/receive.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -import sys -import struct - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - iface = 'h2-eth0' - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/ecn/s1-commands.txt b/P4D2_2018_East/exercises/ecn/s1-commands.txt deleted file mode 100644 index dc4e2606a..000000000 --- a/P4D2_2018_East/exercises/ecn/s1-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 2 -table_add ipv4_lpm ipv4_forward 10.0.1.11/32 => 00:00:00:00:01:0b 1 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:02:00 4 diff --git a/P4D2_2018_East/exercises/ecn/s2-commands.txt b/P4D2_2018_East/exercises/ecn/s2-commands.txt deleted file mode 100644 index 3b80bedbf..000000000 --- a/P4D2_2018_East/exercises/ecn/s2-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 2 -table_add ipv4_lpm ipv4_forward 10.0.2.22/32 => 00:00:00:00:02:16 1 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:03:00 4 diff --git a/P4D2_2018_East/exercises/ecn/s3-commands.txt b/P4D2_2018_East/exercises/ecn/s3-commands.txt deleted file mode 100644 index c7285fbaa..000000000 --- a/P4D2_2018_East/exercises/ecn/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 -table_add ipv4_lpm ipv4_forward 10.0.1.10/24 => 00:00:00:01:04:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.10/24 => 00:00:00:02:04:00 3 diff --git a/P4D2_2018_East/exercises/ecn/send.py b/P4D2_2018_East/exercises/ecn/send.py deleted file mode 100755 index f279d41b2..000000000 --- a/P4D2_2018_East/exercises/ecn/send.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import Ether, IP, UDP -from scapy.all import IntField, FieldListField, FieldLenField, ShortField -from scapy.layers.inet import _IPOption_HDR - -from time import sleep - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - - if len(sys.argv)<4: - print 'pass 2 arguments: "" ' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(dst=addr, tos=1) / UDP(dport=4321, sport=1234) / sys.argv[2] - pkt.show2() - #hexdump(pkt) - try: - for i in range(int(sys.argv[3])): - sendp(pkt, iface=iface) - sleep(1) - except KeyboardInterrupt: - raise - - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/ecn/setup.png b/P4D2_2018_East/exercises/ecn/setup.png deleted file mode 100644 index aa568ecc6..000000000 Binary files a/P4D2_2018_East/exercises/ecn/setup.png and /dev/null differ diff --git a/P4D2_2018_East/exercises/ecn/solution/ecn.p4 b/P4D2_2018_East/exercises/ecn/solution/ecn.p4 deleted file mode 100644 index 2aebe20c8..000000000 --- a/P4D2_2018_East/exercises/ecn/solution/ecn.p4 +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> TCP_PROTOCOL = 0x06; -const bit<16> TYPE_IPV4 = 0x800; -const bit<19> ECN_THRESHOLD = 10; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<6> diffserv; - bit<2> ecn; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action mark_ecn() { - hdr.ipv4.ecn = 3; - } - apply { - if (hdr.ipv4.ecn == 1 || hdr.ipv4.ecn == 2){ - if (standard_metadata.enq_qdepth >= ECN_THRESHOLD){ - mark_ecn(); - } - } - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.ecn, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/load_balance/Makefile b/P4D2_2018_East/exercises/load_balance/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2018_East/exercises/load_balance/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2018_East/exercises/load_balance/README.md b/P4D2_2018_East/exercises/load_balance/README.md deleted file mode 100644 index 69ff63931..000000000 --- a/P4D2_2018_East/exercises/load_balance/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# Load Balancing - -In this exercise, you will implement a form of load balancing based on -a simple version of Equal-Cost Multipath Forwarding. The switch you -will implement will use two tables to forward packets to one of two -destination hosts at random. The first table will use a hash function -(applied to a 5-tuple consisting of the source and destination -IP addresses, IP protocol, and source and destination TCP ports) -to select one of two hosts. The second table will use the -computed hash value to forward the packet to the selected host. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`load_balance.p4`, which initially drops all packets. Your job (in -the next step) will be to extend it to properly forward packets. - -Before that, let's compile the incomplete `load_balance.p4` and bring -up a switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - make - ``` - This will: - * compile `load_balance.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle, each connected to one host (`h1`, `h2`, `h3`). - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc. - * We use the IP address 10.0.0.1 to indicate traffic that should be - load balanced between `h2` and `h3`. - -2. You should now see a Mininet command prompt. Open three terminals - for `h1`, `h2` and `h3`, respectively: - ```bash - mininet> xterm h1 h2 h3 - ``` -3. Each host includes a small Python-based messaging client and - server. In `h2` and `h3`'s XTerms, start the servers: - ```bash - ./receive.py - ``` -4. In `h1`'s XTerm, send a message from the client: - ```bash - ./send.py 10.0.0.1 "P4 is cool" - ``` - The message will not be received. -5. Type `exit` to leave each XTerm and the Mininet command line. - -The message was not received because each switch is programmed with -`load_balance.p4`, which drops all packets on arrival. Your job is to -extend this file. - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules -governing packet processing are inserted into the pipeline by the -control plane. When a rule matches a packet, its action is invoked -with parameters supplied by the control plane as part of the rule. - -In this exercise, the control plane logic has already been -implemented. As part of bringing up the Mininet instance, the -`make` script will install packet-processing rules in the tables of -each switch. These are defined in the `s1-commands.txt` file. - -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. The `s1-commands.txt` file contains -a list of commands for the BMv2 switch API. These commands refer to -specific tables, keys, and actions by name, and any changes in the P4 -program that add or rename tables, keys, or actions will need to be -reflected in these command files. - -## Step 2: Implement Load Balancing - -The `load_balance.p4` file contains a skeleton P4 program with key -pieces of logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the -missing piece. - -A complete `load_balance.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 (`ipv4_t`). -2. Parsers for Ethernet and IPv4 that populate `ethernet_t` and `ipv4_t` fields. -3. An action to drop a packet, using `mark_to_drop()`. -4. **TODO:** An action (called `set_ecmp_select`), which will: - 1. Hashes the 5-tuple specified above using the `hash` extern - 2. Stores the result in the `meta.ecmp_select` field -5. **TODO:** A control that: - 1. Applies the `ecmp_group` table. - 2. Applies the `ecmp_nhop` table. -6. A deparser that selects the order in which fields inserted into the outgoing - packet. -7. A `package` instantiation supplied with the parser, control, and deparser. - > In general, a package also requires instances of checksum verification - > and recomputation controls. These are not necessary for this tutorial - > and are replaced with instantiations of empty controls. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, your message from -`h1` should be delivered to `h2` or `h3`. If you send several -messages, some should be received by each server. - -### Food for thought - - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `load_balance.p4` fails to compile. In this case, `make` will -report the error emitted from the compiler and stop. - -2. `load_balance.p4` compiles but does not support the control plane -rules in the `sX-commands.txt` files that `make` tries to install -using the BMv2 CLI. In this case, `make` will log the CLI tool output -in the `logs` directory. Use these error messages to fix your `load_balance.p4` -implementation. - -3. `load_balance.p4` compiles, and the control plane rules are -installed, but the switch does not process packets in the desired way. -The `/tmp/p4s..log` files contain trace messages -describing how each switch processes each packet. The output is -detailed and can help pinpoint logic errors in your implementation. - -#### Cleaning up Mininet - -In the latter two cases above, `make` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! diff --git a/P4D2_2018_East/exercises/load_balance/load_balance.p4 b/P4D2_2018_East/exercises/load_balance/load_balance.p4 deleted file mode 100644 index 54dfed6bc..000000000 --- a/P4D2_2018_East/exercises/load_balance/load_balance.p4 +++ /dev/null @@ -1,218 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - bit<32> srcAddr; - bit<32> dstAddr; -} - -header tcp_t { - bit<16> srcPort; - bit<16> dstPort; - bit<32> seqNo; - bit<32> ackNo; - bit<4> dataOffset; - bit<3> res; - bit<3> ecn; - bit<6> ctrl; - bit<16> window; - bit<16> checksum; - bit<16> urgentPtr; -} - -struct metadata { - bit<14> ecmp_select; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - tcp_t tcp; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - state start { - transition parse_ethernet; - } - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - 0x800: parse_ipv4; - default: accept; - } - } - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition select(hdr.ipv4.protocol) { - 6: parse_tcp; - default: accept; - } - } - state parse_tcp { - packet.extract(hdr.tcp); - transition accept; - } -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action drop() { - mark_to_drop(); - } - action set_ecmp_select(bit<16> ecmp_base, bit<32> ecmp_count) { - /* TODO: hash on 5-tuple and save the hash result in meta.ecmp_select - so that the ecmp_nhop table can use it to make a forwarding decision accordingly */ - } - action set_nhop(bit<48> nhop_dmac, bit<32> nhop_ipv4, bit<9> port) { - hdr.ethernet.dstAddr = nhop_dmac; - hdr.ipv4.dstAddr = nhop_ipv4; - standard_metadata.egress_spec = port; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - table ecmp_group { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - drop; - set_ecmp_select; - } - size = 1024; - } - table ecmp_nhop { - key = { - meta.ecmp_select: exact; - } - actions = { - drop; - set_nhop; - } - size = 2; - } - apply { - if (hdr.ipv4.isValid() && hdr.ipv4.ttl > 0) { - ecmp_group.apply(); - ecmp_nhop.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action rewrite_mac(bit<48> smac) { - hdr.ethernet.srcAddr = smac; - } - action drop() { - mark_to_drop(); - } - table send_frame { - key = { - standard_metadata.egress_port: exact; - } - actions = { - rewrite_mac; - drop; - } - size = 256; - } - apply { - send_frame.apply(); - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.tcp); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/load_balance/receive.py b/P4D2_2018_East/exercises/load_balance/receive.py deleted file mode 100755 index c93182f89..000000000 --- a/P4D2_2018_East/exercises/load_balance/receive.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -import sys -import struct -import os - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/')) - iface = ifaces[0] - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="tcp", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/load_balance/s1-commands.txt b/P4D2_2018_East/exercises/load_balance/s1-commands.txt deleted file mode 100644 index a53e07c7d..000000000 --- a/P4D2_2018_East/exercises/load_balance/s1-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.0.1/32 => 0 2 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:01:02 10.0.2.2 2 -table_add ecmp_nhop set_nhop 1 => 00:00:00:00:01:03 10.0.3.3 3 -table_add send_frame rewrite_mac 2 => 00:00:00:01:02:00 -table_add send_frame rewrite_mac 3 => 00:00:00:01:03:00 diff --git a/P4D2_2018_East/exercises/load_balance/s2-commands.txt b/P4D2_2018_East/exercises/load_balance/s2-commands.txt deleted file mode 100644 index ca78c19a1..000000000 --- a/P4D2_2018_East/exercises/load_balance/s2-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.2.2/32 => 0 1 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:02:02 10.0.2.2 1 -table_add send_frame rewrite_mac 1 => 00:00:00:02:01:00 diff --git a/P4D2_2018_East/exercises/load_balance/s3-commands.txt b/P4D2_2018_East/exercises/load_balance/s3-commands.txt deleted file mode 100644 index 0f9e6b363..000000000 --- a/P4D2_2018_East/exercises/load_balance/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.3.3/32 => 0 1 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:03:03 10.0.3.3 1 -table_add send_frame rewrite_mac 1 => 00:00:00:03:01:00 diff --git a/P4D2_2018_East/exercises/load_balance/send.py b/P4D2_2018_East/exercises/load_balance/send.py deleted file mode 100755 index 00496d9de..000000000 --- a/P4D2_2018_East/exercises/load_balance/send.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr -from scapy.all import Packet -from scapy.all import Ether, IP, UDP, TCP - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - print "sending on interface %s to %s" % (iface, str(addr)) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2] - pkt.show2() - sendp(pkt, iface=iface, verbose=False) - - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/load_balance/solution/load_balance.p4 b/P4D2_2018_East/exercises/load_balance/solution/load_balance.p4 deleted file mode 100644 index 64d9bcbfe..000000000 --- a/P4D2_2018_East/exercises/load_balance/solution/load_balance.p4 +++ /dev/null @@ -1,225 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - bit<32> srcAddr; - bit<32> dstAddr; -} - -header tcp_t { - bit<16> srcPort; - bit<16> dstPort; - bit<32> seqNo; - bit<32> ackNo; - bit<4> dataOffset; - bit<3> res; - bit<3> ecn; - bit<6> ctrl; - bit<16> window; - bit<16> checksum; - bit<16> urgentPtr; -} - -struct metadata { - bit<14> ecmp_select; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - tcp_t tcp; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - 0x800: parse_ipv4; - default: accept; - } - } - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition select(hdr.ipv4.protocol) { - 6: parse_tcp; - default: accept; - } - } - state parse_tcp { - packet.extract(hdr.tcp); - transition accept; - } -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - action set_ecmp_select(bit<16> ecmp_base, bit<32> ecmp_count) { - hash(meta.ecmp_select, - HashAlgorithm.crc16, - ecmp_base, - { hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr, - hdr.ipv4.protocol, - hdr.tcp.srcPort, - hdr.tcp.dstPort }, - ecmp_count); - } - action set_nhop(bit<48> nhop_dmac, bit<32> nhop_ipv4, bit<9> port) { - hdr.ethernet.dstAddr = nhop_dmac; - hdr.ipv4.dstAddr = nhop_ipv4; - standard_metadata.egress_spec = port; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - table ecmp_group { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - drop; - set_ecmp_select; - } - size = 1024; - } - table ecmp_nhop { - key = { - meta.ecmp_select: exact; - } - actions = { - drop; - set_nhop; - } - size = 2; - } - apply { - if (hdr.ipv4.isValid() && hdr.ipv4.ttl > 0) { - ecmp_group.apply(); - ecmp_nhop.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action rewrite_mac(bit<48> smac) { - hdr.ethernet.srcAddr = smac; - } - action drop() { - mark_to_drop(); - } - table send_frame { - key = { - standard_metadata.egress_port: exact; - } - actions = { - rewrite_mac; - drop; - } - size = 256; - } - apply { - send_frame.apply(); - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.tcp); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/load_balance/topology.json b/P4D2_2018_East/exercises/load_balance/topology.json deleted file mode 100644 index e33477cc9..000000000 --- a/P4D2_2018_East/exercises/load_balance/topology.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "hosts": [ - "h1", - "h2", - "h3" - ], - "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } - }, - "links": [ - ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], - ["s3", "s2"], ["s2", "h2"], ["s3", "h3"] - ] -} diff --git a/P4D2_2018_East/exercises/mri/Makefile b/P4D2_2018_East/exercises/mri/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2018_East/exercises/mri/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2018_East/exercises/mri/README.md b/P4D2_2018_East/exercises/mri/README.md deleted file mode 100644 index c38911043..000000000 --- a/P4D2_2018_East/exercises/mri/README.md +++ /dev/null @@ -1,244 +0,0 @@ -# Implementing MRI - -## Introduction - -The objective of this tutorial is to extend basic L3 forwarding with a -scaled-down version of In-Band Network Telemetry (INT), which we call -Multi-Hop Route Inspection (MRI). - -MRI allows users to track the path and the length of queues that every -packet travels through. To support this functionality, you will need -to write a P4 program that appends an ID and queue length to the -header stack of every packet. At the destination, the sequence of -switch IDs correspond to the path, and each ID is followed by the -queue length of the port at switch. - -As before, we have already defined the control plane rules, so you -only need to implement the data plane logic of your P4 program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`mri.p4`, which initially implements L3 forwarding. Your job (in the -next step) will be to extend it to properly prepend the MRI custom -headers. - -Before that, let's compile the incomplete `mri.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - make - ``` - This will: - * compile `mri.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle. There are 5 hosts. `h1` and `h11` are connected to `s1`. - `h2` and `h22` are connected to `s2` and `h3` is connected to `s3`. - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc - (`10.0..`). - * The control plane programs the P4 tables in each switch based on - `sx-commands.txt` - -2. We want to send a low rate traffic from `h1` to `h2` and a high - rate iperf traffic from `h11` to `h22`. The link between `s1` and - `s2` is common between the flows and is a bottleneck because we - reduced its bandwidth to 512kbps in topology.json. Therefore, if we - capture packets at `h2`, we should see high queue size for that - link. - -![Setup](setup.png) - -3. You should now see a Mininet command prompt. Open four terminals - for `h1`, `h11`, `h2`, `h22`, respectively: - ```bash - mininet> xterm h1 h11 h2 h22 - ``` -3. In `h2`'s xterm, start the server that captures packets: - ```bash - ./receive.py - ``` -4. in `h22`'s xterm, start the iperf UDP server: - ```bash - iperf -s -u - ``` - -5. In `h1`'s xterm, send one packet per second to `h2` using send.py - say for 30 seconds: - ```bash - ./send.py 10.0.2.2 "P4 is cool" 30 - ``` - The message "P4 is cool" should be received in `h2`'s xterm, -6. In `h11`'s xterm, start iperf client sending for 15 seconds - ```bash - iperf -c 10.0.2.22 -t 15 -u - ``` -7. At `h2`, the MRI header has no hop info (`count=0`) -8. type `exit` to close each xterm window - -You should see the message received at host `h2`, but without any -information about the path the message took. Your job is to extend -the code in `mri.p4` to implement the MRI logic to record the path. - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules -governing packet processing are inserted into the pipeline by the -control plane. When a rule matches a packet, its action is invoked -with parameters supplied by the control plane as part of the rule. - -In this exercise, the control plane logic has already been -implemented. As part of bringing up the Mininet instance, the -`make` script will install packet-processing rules in the tables of -each switch. These are defined in the `sX-commands.txt` files, where -`X` corresponds to the switch number. - -## Step 2: Implement MRI - -The `mri.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the -missing piece. - -MRI will require two custom headers. The first header, `mri_t`, -contains a single field `count`, which indicates the number of switch -IDs that follow. The second header, `switch_t`, contains switch ID and -Queue depth fields of each switch hop the packet goes through. - -One of the biggest challenges in implementing MRI is handling the -recursive logic for parsing these two headers. We will use a -`parser_metadata` field, `remaining`, to keep track of how many -`switch_t` headers we need to parse. In the `parse_mri` state, this -field should be set to `hdr.mri.count`. In the `parse_swtrace` state, -this field should be decremented. The `parse_swtrace` state will -transition to itself until `remaining` is 0. - -The MRI custom headers will be carried inside an IP Options -header. The IP Options header contains a field, `option`, which -indicates the type of the option. We will use a special type 31 to -indicate the presence of the MRI headers. - -Beyond the parser logic, you will add a table in egress, `swtrace` to -store the switch ID and queue depth, and actions that increment the -`count` field, and append a `switch_t` header. - -A complete `mri.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`), IPv4 (`ipv4_t`), - IP Options (`ipv4_option_t`), MRI (`mri_t`), and Switch (`switch_t`). -2. Parsers for Ethernet, IPv4, IP Options, MRI, and Switch that will -populate `ethernet_t`, `ipv4_t`, `ipv4_option_t`, `mri_t`, and -`switch_t`. -3. An action to drop a packet, using `mark_to_drop()`. -4. An action (called `ipv4_forward`), which will: - 1. Set the egress port for the next hop. - 2. Update the ethernet destination address with the address of - the next hop. - 3. Update the ethernet source address with the address of the switch. - 4. Decrement the TTL. -5. An ingress control that: - 1. Defines a table that will read an IPv4 destination address, and - invoke either `drop` or `ipv4_forward`. - 2. An `apply` block that applies the table. -6. At egress, an action (called `add_swtrace`) that will add the - switch ID and queue depth. -8. An egress control that applies a table (`swtrace`) to store the - switch ID and queue depth, and calls `add_swtrace`. -9. A deparser that selects the order in which fields inserted into the outgoing - packet. -10. A `package` instantiation supplied with the parser, control, - checksum verification and recomputation and deparser. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, when your message - from `h1` is delivered to `h2`, you should see the sequence of - switches through which the packet traveled plus the corresponding - queue depths. The expected output will look like the following, - which shows the MRI header, with a `count` of 2, and switch ids - (`swids`) 2 and 1. The queue depth at the common link (from s1 to - s2) is high. - -``` -got a packet -###[ Ethernet ]### - dst = 00:04:00:02:00:02 - src = f2:ed:e6:df:4e:fa - type = 0x800 -###[ IP ]### - version = 4L - ihl = 10L - tos = 0x0 - len = 42 - id = 1 - flags = - frag = 0L - ttl = 62 - proto = udp - chksum = 0x60c0 - src = 10.0.1.1 - dst = 10.0.2.2 - \options \ - |###[ MRI ]### - | copy_flag = 0L - | optclass = control - | option = 31L - | length = 20 - | count = 2 - | \swtraces \ - | |###[ SwitchTrace ]### - | | swid = 2 - | | qdepth = 0 - | |###[ SwitchTrace ]### - | | swid = 1 - | | qdepth = 17 -###[ UDP ]### - sport = 1234 - dport = 4321 - len = 18 - chksum = 0x1c7b -###[ Raw ]### - load = 'P4 is cool' - -``` - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `mri.p4` fails to compile. In this case, `make` will report the -error emitted from the compiler and stop. -2. `mri.p4` compiles but does not support the control plane rules in -the `sX-commands.txt` files that `make` tries to install using the BMv2 CLI. -In this case, `make` will log the CLI tool output in the `logs` directory. -Use these error messages to fix your `mri.p4` implementation. -3. `mri.p4` compiles, and the control plane rules are installed, but -the switch does not process packets in the desired way. The -`/tmp/p4s..log` files contain trace messages describing -how each switch processes each packet. The output is detailed and can -help pinpoint logic errors in your implementation. The -`build/-.pcap` also contains the pcap of -packets on each interface. Use `tcpdump -r -xxx` to print -the hexdump of the packets. -4. `mri.p4` compiles and all rules are installed. Packets go through -and the logs show that the queue length is always 0. Then either -reduce the link bandwidth in `topology.json`. - -#### Cleaning up Mininet - -In the latter two cases above, `make` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -make stop -``` - -## Next Steps - -Congratulations, your implementation works! Move on to [Source -Routing](../source_routing). - diff --git a/P4D2_2018_East/exercises/mri/receive.py b/P4D2_2018_East/exercises/mri/receive.py deleted file mode 100755 index b3c3b3558..000000000 --- a/P4D2_2018_East/exercises/mri/receive.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -import sys -import struct - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import PacketListField, ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class SwitchTrace(Packet): - fields_desc = [ IntField("swid", 0), - IntField("qdepth", 0)] - def extract_padding(self, p): - return "", p - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swtraces", - adjust=lambda pkt,l:l*2+4), - ShortField("count", 0), - PacketListField("swtraces", - [], - SwitchTrace, - count_from=lambda pkt:(pkt.count*1)) ] - -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - iface = 'h2-eth0' - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/mri/s1-commands.txt b/P4D2_2018_East/exercises/mri/s1-commands.txt deleted file mode 100644 index 5f15633ba..000000000 --- a/P4D2_2018_East/exercises/mri/s1-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 1 -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 2 -table_add ipv4_lpm ipv4_forward 10.0.1.11/32 => 00:00:00:00:01:0b 1 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:02:00 4 diff --git a/P4D2_2018_East/exercises/mri/s2-commands.txt b/P4D2_2018_East/exercises/mri/s2-commands.txt deleted file mode 100644 index 8b9bd15a7..000000000 --- a/P4D2_2018_East/exercises/mri/s2-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 2 -table_add ipv4_lpm ipv4_forward 10.0.2.22/32 => 00:00:00:00:02:16 1 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:03:00 4 diff --git a/P4D2_2018_East/exercises/mri/s3-commands.txt b/P4D2_2018_East/exercises/mri/s3-commands.txt deleted file mode 100644 index d27b73c50..000000000 --- a/P4D2_2018_East/exercises/mri/s3-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 3 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:01 1 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:04:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:04:00 3 diff --git a/P4D2_2018_East/exercises/mri/send.py b/P4D2_2018_East/exercises/mri/send.py deleted file mode 100755 index 7b0aca7fc..000000000 --- a/P4D2_2018_East/exercises/mri/send.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import Ether, IP, UDP -from scapy.all import IntField, FieldListField, FieldLenField, ShortField, PacketListField -from scapy.layers.inet import _IPOption_HDR - -from time import sleep - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class SwitchTrace(Packet): - fields_desc = [ IntField("swid", 0), - IntField("qdepth", 0)] - def extract_padding(self, p): - return "", p - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swtraces", - adjust=lambda pkt,l:l*2+4), - ShortField("count", 0), - PacketListField("swtraces", - [], - SwitchTrace, - count_from=lambda pkt:(pkt.count*1)) ] - - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP( - dst=addr, options = IPOption_MRI(count=0, - swtraces=[])) / UDP( - dport=4321, sport=1234) / sys.argv[2] - - # pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP( - # dst=addr, options = IPOption_MRI(count=2, - # swtraces=[SwitchTrace(swid=0,qdepth=0), SwitchTrace(swid=1,qdepth=0)])) / UDP( - # dport=4321, sport=1234) / sys.argv[2] - pkt.show2() - #hexdump(pkt) - try: - for i in range(int(sys.argv[3])): - sendp(pkt, iface=iface) - sleep(1) - except KeyboardInterrupt: - raise - - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/mri/setup.png b/P4D2_2018_East/exercises/mri/setup.png deleted file mode 100644 index aa568ecc6..000000000 Binary files a/P4D2_2018_East/exercises/mri/setup.png and /dev/null differ diff --git a/P4D2_2018_East/exercises/mri/solution/mri.p4 b/P4D2_2018_East/exercises/mri/solution/mri.p4 deleted file mode 100644 index 9827ffbfc..000000000 --- a/P4D2_2018_East/exercises/mri/solution/mri.p4 +++ /dev/null @@ -1,273 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> UDP_PROTOCOL = 0x11; -const bit<16> TYPE_IPV4 = 0x800; -const bit<5> IPV4_OPTION_MRI = 31; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; -typedef bit<32> switchID_t; -typedef bit<32> qdepth_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -header ipv4_option_t { - bit<1> copyFlag; - bit<2> optClass; - bit<5> option; - bit<8> optionLength; -} - -header mri_t { - bit<16> count; -} - -header switch_t { - switchID_t swid; - qdepth_t qdepth; -} - -struct ingress_metadata_t { - bit<16> count; -} - -struct parser_metadata_t { - bit<16> remaining; -} - -struct metadata { - ingress_metadata_t ingress_metadata; - parser_metadata_t parser_metadata; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - ipv4_option_t ipv4_option; - mri_t mri; - switch_t[MAX_HOPS] swtraces; -} - -error { IPHeaderTooShort } - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - verify(hdr.ipv4.ihl >= 5, error.IPHeaderTooShort); - transition select(hdr.ipv4.ihl) { - 5 : accept; - default : parse_ipv4_option; - } - } - - state parse_ipv4_option { - packet.extract(hdr.ipv4_option); - transition select(hdr.ipv4_option.option) { - IPV4_OPTION_MRI: parse_mri; - default: accept; - } - } - - state parse_mri { - packet.extract(hdr.mri); - meta.parser_metadata.remaining = hdr.mri.count; - transition select(meta.parser_metadata.remaining) { - 0 : accept; - default: parse_swtrace; - } - } - - state parse_swtrace { - packet.extract(hdr.swtraces.next); - meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1; - transition select(meta.parser_metadata.remaining) { - 0 : accept; - default: parse_swtrace; - } - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action add_swtrace(switchID_t swid) { - hdr.mri.count = hdr.mri.count + 1; - hdr.swtraces.push_front(1); - // According to the P4_16 spec, pushed elements are invalid, so we need - // to call setValid(). Older bmv2 versions would mark the new header(s) - // valid automatically (P4_14 behavior), but starting with version 1.11, - // bmv2 conforms with the P4_16 spec. - hdr.swtraces[0].setValid(); - hdr.swtraces[0].swid = swid; - hdr.swtraces[0].qdepth = (qdepth_t)standard_metadata.deq_qdepth; - - hdr.ipv4.ihl = hdr.ipv4.ihl + 2; - hdr.ipv4_option.optionLength = hdr.ipv4_option.optionLength + 8; - hdr.ipv4.totalLen = hdr.ipv4.totalLen + 8; - } - - table swtrace { - actions = { - add_swtrace; - NoAction; - } - default_action = NoAction(); - } - - apply { - if (hdr.mri.isValid()) { - swtrace.apply(); - } - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.ipv4_option); - packet.emit(hdr.mri); - packet.emit(hdr.swtraces); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/mri/topology.json b/P4D2_2018_East/exercises/mri/topology.json deleted file mode 100644 index 2640a2b96..000000000 --- a/P4D2_2018_East/exercises/mri/topology.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "hosts": [ - "h1", - "h2", - "h3", - "h11", - "h22" - ], - "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } - }, - "links": [ - ["h1", "s1"], ["h11", "s1"], ["s1", "s2", "0", 0.5], ["s1", "s3"], - ["s3", "s2"], ["s2", "h2"], ["s2", "h22"], ["s3", "h3"] - ] -} diff --git a/P4D2_2018_East/exercises/p4runtime/README.md b/P4D2_2018_East/exercises/p4runtime/README.md deleted file mode 100644 index 9cc60e191..000000000 --- a/P4D2_2018_East/exercises/p4runtime/README.md +++ /dev/null @@ -1,188 +0,0 @@ -# Implementing a Control Plane using P4 Runtime - -## Introduction - -In this exercise, we will be using P4 Runtime to send flow entries to the -switch instead of using the switch's CLI. We will be building on the same P4 -program that you used in the [basic_tunnel](../basic_tunnel) exercise. The -P4 program has been renamed to `advanced_tunnel.py` and has been augmented -with two counters (`ingressTunnelCounter`, `egressTunnelCounter`) and -two new actions (`myTunnel_ingress`, `myTunnel_egress`). - -You will use the starter program, `mycontroller.py`, and a few helper -libraries in the `p4runtime_lib` directory to create the table entries -necessary to tunnel traffic between host 1 and 2. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The starter code for this assignment is in a file called `mycontroller.py`, -and it will install only some of the rules that you need to tunnel traffic between -two hosts. - -Let's first compile the new P4 program, start the network, use `mycontroller.py` -to install a few rules, and look at the `ingressTunnelCounter` to see that things -are working as expected. - -1. In your shell, run: - ```bash - make - ``` - This will: - * compile `advanced_tunnel.p4`, - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) - configured in a triangle, each connected to one host (`h1`, `h2`, `h3`), and - * assign IPs of `10.0.1.1`, `10.0.2.2`, `10.0.3.3` to the respective hosts. - -2. You should now see a Mininet command prompt. Start a ping between h1 and h2: - ```bash - mininet> h1 ping h2 - ``` - Because there are no rules on the switches, you should **not** receive any - replies yet. You should leave the ping running in this shell. - -3. Open another shell and run the starter code: - ```bash - cd ~/tutorials/P4D2_2017_Fall/exercises/p4runtime - ./mycontroller.py - ``` - This will install the `advanced_tunnel.p4` program on the switches and push the - tunnel ingress rules. - The program prints the tunnel ingress and egress counters every 2 seconds. - You should see the ingress tunnel counter for s1 increasing: - ``` - s1 ingressTunnelCounter 100: 2 packets - ``` - The other counters should remain at zero. - -4. Press `Ctrl-C` to the second shell to stop `mycontroller.py` - -Each switch is currently mapping traffic into tunnels based on the destination IP -address. Your job is to write the rules that forward the traffic between the switches -based on the tunnel ID. - -### Potential Issues - -If you see the following error message when running `mycontroller.py`, then -the gRPC server is not running on one or more switches. - -``` -p4@p4:~/tutorials/P4D2_2017_Fall/exercises/p4runtime$ ./mycontroller.py -... -grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with (StatusCode.UNAVAILABLE, Connect Failed)> -``` - -You can check to see which of gRPC ports are listening on the machine by running: -```bash -sudo netstat -lpnt -``` - -The easiest solution is to enter `Ctrl-D` or `exit` in the `mininet>` prompt, -and re-run `make`. - -### A note about the control plane - -A P4 program defines a packet-processing pipeline, but the rules -within each table are inserted by the control plane. In this case, -`mycontroller.py` implements our control plane, instead of installing static -table entries like we have in the previous exercises. - -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. This interface is defined in the -`advanced_tunnel.p4info` file. The table entries that you build in `mycontroller.py` -refer to specific tables, keys, and actions by name, and we use a P4Info helper -to convert the names into the IDs that are required for P4 Runtime. Any changes -in the P4 program that add or rename tables, keys, or actions will need to be -reflected in your table entries. - -## Step 2: Implement Tunnel Forwarding - -The `mycontroller.py` file is a basic controller plane that does the following: -1. Establishes a gRPC connection to the switches for the P4 Runtime service. -2. Pushes the P4 program to each switch. -3. Writes tunnel ingress and tunnel egress rules for two tunnels between h1 and h2. -4. Reads tunnel ingress and egress counters every 2 seconds. - -It also contains comments marked with `TODO` which indicate the functionality -that you need to implement. - -Your job will be to write the tunnel transit rule in the `writeTunnelRules` function -that will match on tunnel ID and forward packets to the next hop. - -![topology](../basic_tunnel/topo.png) - -In this exercise, you will be interacting with some of the classes and methods in -the `p4runtime_lib` directory. Here is a summary of each of the files in the directory: -- `helper.py` - - Contains the `P4InfoHelper` class which is used to parse the `p4info` files. - - Provides translation methods from entity name to and from ID number. - - Builds P4 program-dependent sections of P4 Runtime table entries. -- `switch.py` - - Contains the `SwitchConnection` class which grabs the gRPC client stub, and - establishes connections to the switches. - - Provides helper methods that construct the P4 Runtime protocol buffer messages - and makes the P4 Runtime gRPC service calls. -- `bmv2.py` - - Contains `Bmv2SwitchConnection` which extends `SwitchConnections` and provides - the BMv2-specific device payload to load the P4 program. -- `convert.py` - - Provides convenience methods to encode and decode from friendly strings and - numbers to the byte strings required for the protocol buffer messages. - - Used by `helper.py` - - -## Step 3: Run your solution - -Follow the instructions from Step 1. If your Mininet network is still running, -you will just need to run the following in your second shell: -```bash -./my_controller.py -``` - -You should start to see ICMP replies in your Mininet prompt, and you should start to -see the values for all counters start to increment. - -### Extra Credit and Food for Thought - -You might notice that the rules that are printed by `mycontroller.py` contain the entity -IDs rather than the table names. You can use the P4Info helper to translate these IDs -into entry names. - -Also, you may want to think about the following: -- What assumptions about the topology are baked into your implementation? How would you -need to change it for a more realistic network? - -- Why are the byte counters different between the ingress and egress counters? - -- What is the TTL in the ICMP replies? Why is it the value that it is? -Hint: The default TTL is 64 for packets sent by the hosts. - -If you are interested, you can find the protocol buffer and gRPC definitions here: -- [P4 Runtime](https://github.com/p4lang/PI/blob/master/proto/p4/p4runtime.proto) -- [P4 Info](https://github.com/p4lang/PI/blob/master/proto/p4/config/p4info.proto) - -#### Cleaning up Mininet - -If the Mininet shell crashes, it may leave a Mininet instance -running in the background. Use the following command to clean up: -```bash -make clean -``` - -#### Running the reference solution - -To run the reference solution, you should run the following command from the -`~/tutorials/P4D2_2017_Fall/exercises/p4runtime` directory: -```bash -solution/my_controller.py -``` - - -## Next Steps - -Congratulations, your implementation works! Move onto the next assignment -[ecn](../ecn)! - diff --git a/P4D2_2018_East/exercises/p4runtime/advanced_tunnel.p4 b/P4D2_2018_East/exercises/p4runtime/advanced_tunnel.p4 deleted file mode 100755 index 5ea8fc079..000000000 --- a/P4D2_2018_East/exercises/p4runtime/advanced_tunnel.p4 +++ /dev/null @@ -1,238 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_MYTUNNEL = 0x1212; -const bit<16> TYPE_IPV4 = 0x800; -const bit<32> MAX_TUNNEL_ID = 1 << 16; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header myTunnel_t { - bit<16> proto_id; - bit<16> dst_id; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - myTunnel_t myTunnel; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_MYTUNNEL: parse_myTunnel; - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_myTunnel { - packet.extract(hdr.myTunnel); - transition select(hdr.myTunnel.proto_id) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - counter(MAX_TUNNEL_ID, CounterType.packets_and_bytes) ingressTunnelCounter; - counter(MAX_TUNNEL_ID, CounterType.packets_and_bytes) egressTunnelCounter; - - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - action myTunnel_ingress(bit<16> dst_id) { - hdr.myTunnel.setValid(); - hdr.myTunnel.dst_id = dst_id; - hdr.myTunnel.proto_id = hdr.ethernet.etherType; - hdr.ethernet.etherType = TYPE_MYTUNNEL; - ingressTunnelCounter.count((bit<32>) hdr.myTunnel.dst_id); - } - - action myTunnel_forward(egressSpec_t port) { - standard_metadata.egress_spec = port; - } - - action myTunnel_egress(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.dstAddr = dstAddr; - hdr.ethernet.etherType = hdr.myTunnel.proto_id; - hdr.myTunnel.setInvalid(); - egressTunnelCounter.count((bit<32>) hdr.myTunnel.dst_id); - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - myTunnel_ingress; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - table myTunnel_exact { - key = { - hdr.myTunnel.dst_id: exact; - } - actions = { - myTunnel_forward; - myTunnel_egress; - drop; - } - size = 1024; - default_action = drop(); - } - - apply { - if (hdr.ipv4.isValid() && !hdr.myTunnel.isValid()) { - // Process only non-tunneled IPv4 packets. - ipv4_lpm.apply(); - } - - if (hdr.myTunnel.isValid()) { - // Process all tunneled packets. - myTunnel_exact.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.myTunnel); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/__init__.py b/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/bmv2.py b/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/bmv2.py deleted file mode 100644 index 7f483f476..000000000 --- a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/bmv2.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2017-present Open Networking Foundation -# -# 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. -# -from switch import SwitchConnection -from p4.tmp import p4config_pb2 - - -def buildDeviceConfig(bmv2_json_file_path=None): - "Builds the device config for BMv2" - device_config = p4config_pb2.P4DeviceConfig() - device_config.reassign = True - with open(bmv2_json_file_path) as f: - device_config.device_data = f.read() - return device_config - - -class Bmv2SwitchConnection(SwitchConnection): - def buildDeviceConfig(self, **kwargs): - return buildDeviceConfig(**kwargs) diff --git a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/convert.py b/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/convert.py deleted file mode 100644 index 0375e1747..000000000 --- a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/convert.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright 2017-present Open Networking Foundation -# -# 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. -# -import re -import socket - -import math - -''' -This package contains several helper functions for encoding to and decoding from byte strings: -- integers -- IPv4 address strings -- Ethernet address strings -''' - -mac_pattern = re.compile('^([\da-fA-F]{2}:){5}([\da-fA-F]{2})$') -def matchesMac(mac_addr_string): - return mac_pattern.match(mac_addr_string) is not None - -def encodeMac(mac_addr_string): - return mac_addr_string.replace(':', '').decode('hex') - -def decodeMac(encoded_mac_addr): - return ':'.join(s.encode('hex') for s in encoded_mac_addr) - -ip_pattern = re.compile('^(\d{1,3}\.){3}(\d{1,3})$') -def matchesIPv4(ip_addr_string): - return ip_pattern.match(ip_addr_string) is not None - -def encodeIPv4(ip_addr_string): - return socket.inet_aton(ip_addr_string) - -def decodeIPv4(encoded_ip_addr): - return socket.inet_ntoa(encoded_ip_addr) - -def bitwidthToBytes(bitwidth): - return int(math.ceil(bitwidth / 8.0)) - -def encodeNum(number, bitwidth): - byte_len = bitwidthToBytes(bitwidth) - num_str = '%x' % number - if number >= 2 ** bitwidth: - raise Exception("Number, %d, does not fit in %d bits" % (number, bitwidth)) - return ('0' * (byte_len * 2 - len(num_str)) + num_str).decode('hex') - -def decodeNum(encoded_number): - return int(encoded_number.encode('hex'), 16) - -def encode(x, bitwidth): - 'Tries to infer the type of `x` and encode it' - byte_len = bitwidthToBytes(bitwidth) - if (type(x) == list or type(x) == tuple) and len(x) == 1: - x = x[0] - encoded_bytes = None - if type(x) == str: - if matchesMac(x): - encoded_bytes = encodeMac(x) - elif matchesIPv4(x): - encoded_bytes = encodeIPv4(x) - else: - # Assume that the string is already encoded - encoded_bytes = x - elif type(x) == int: - encoded_bytes = encodeNum(x, bitwidth) - else: - raise Exception("Encoding objects of %r is not supported" % type(x)) - assert(len(encoded_bytes) == byte_len) - return encoded_bytes - -if __name__ == '__main__': - # TODO These tests should be moved out of main eventually - mac = "aa:bb:cc:dd:ee:ff" - enc_mac = encodeMac(mac) - assert(enc_mac == '\xaa\xbb\xcc\xdd\xee\xff') - dec_mac = decodeMac(enc_mac) - assert(mac == dec_mac) - - ip = "10.0.0.1" - enc_ip = encodeIPv4(ip) - assert(enc_ip == '\x0a\x00\x00\x01') - dec_ip = decodeIPv4(enc_ip) - assert(ip == dec_ip) - - num = 1337 - byte_len = 5 - enc_num = encodeNum(num, byte_len * 8) - assert(enc_num == '\x00\x00\x00\x05\x39') - dec_num = decodeNum(enc_num) - assert(num == dec_num) - - assert(matchesIPv4('10.0.0.1')) - assert(not matchesIPv4('10.0.0.1.5')) - assert(not matchesIPv4('1000.0.0.1')) - assert(not matchesIPv4('10001')) - - assert(encode(mac, 6 * 8) == enc_mac) - assert(encode(ip, 4 * 8) == enc_ip) - assert(encode(num, 5 * 8) == enc_num) - assert(encode((num,), 5 * 8) == enc_num) - assert(encode([num], 5 * 8) == enc_num) - - num = 256 - byte_len = 2 - try: - enc_num = encodeNum(num, 8) - raise Exception("expected exception") - except Exception as e: - print e diff --git a/P4D2_2018_East/exercises/p4runtime/topology.json b/P4D2_2018_East/exercises/p4runtime/topology.json deleted file mode 100755 index f8a5b48c5..000000000 --- a/P4D2_2018_East/exercises/p4runtime/topology.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "hosts": [ - "h1", - "h2", - "h3" - ], - "switches": { - "s1": {}, - "s2": {}, - "s3": {} - }, - "links": [ - ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], - ["s3", "s2"], ["s2", "h2"], ["s3", "h3"] - ] -} diff --git a/P4D2_2018_East/exercises/source_routing/Makefile b/P4D2_2018_East/exercises/source_routing/Makefile deleted file mode 100644 index f378756b2..000000000 --- a/P4D2_2018_East/exercises/source_routing/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../utils/Makefile diff --git a/P4D2_2018_East/exercises/source_routing/receive.py b/P4D2_2018_East/exercises/source_routing/receive.py deleted file mode 100755 index efb93d691..000000000 --- a/P4D2_2018_East/exercises/source_routing/receive.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -import sys -import struct - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr, bind_layers -from scapy.all import Packet, IPOption -from scapy.all import IP, UDP, Raw, Ether -from scapy.layers.inet import _IPOption_HDR -from scapy.fields import * - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - -class SourceRoute(Packet): - fields_desc = [ BitField("bos", 0, 1), - BitField("port", 0, 15)] -class SourceRoutingTail(Packet): - fields_desc = [ XShortField("etherType", 0x800)] - -bind_layers(Ether, SourceRoute, type=0x1234) -bind_layers(SourceRoute, SourceRoute, bos=0) -bind_layers(SourceRoute, SourceRoutingTail, bos=1) - -def main(): - iface = 'h2-eth0' - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/source_routing/s1-commands.txt b/P4D2_2018_East/exercises/source_routing/s1-commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2018_East/exercises/source_routing/s2-commands.txt b/P4D2_2018_East/exercises/source_routing/s2-commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2018_East/exercises/source_routing/s3-commands.txt b/P4D2_2018_East/exercises/source_routing/s3-commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/P4D2_2018_East/exercises/source_routing/send.py b/P4D2_2018_East/exercises/source_routing/send.py deleted file mode 100755 index 21331dba4..000000000 --- a/P4D2_2018_East/exercises/source_routing/send.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr, bind_layers -from scapy.all import Packet -from scapy.all import Ether, IP, UDP -from scapy.fields import * -import readline - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class SourceRoute(Packet): - fields_desc = [ BitField("bos", 0, 1), - BitField("port", 0, 15)] - -bind_layers(Ether, SourceRoute, type=0x1234) -bind_layers(SourceRoute, SourceRoute, bos=0) -bind_layers(SourceRoute, IP, bos=1) - -def main(): - - if len(sys.argv)<2: - print 'pass 2 arguments: ' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - print "sending on interface %s to %s" % (iface, str(addr)) - - while True: - print - s = str(raw_input('Type space separated port nums ' - '(example: "2 3 2 2 1") or "q" to quit: ')) - if s == "q": - break; - print - - i = 0 - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff'); - for p in s.split(" "): - try: - pkt = pkt / SourceRoute(bos=0, port=int(p)) - i = i+1 - except ValueError: - pass - if pkt.haslayer(SourceRoute): - pkt.getlayer(SourceRoute, i).bos = 1 - - pkt = pkt / IP(dst=addr) / UDP(dport=4321, sport=1234) - pkt.show2() - sendp(pkt, iface=iface, verbose=False) - - #pkt = pkt / SourceRoute(bos=0, port=2) / SourceRoute(bos=0, port=3); - #pkt = pkt / SourceRoute(bos=0, port=2) / SourceRoute(bos=0, port=2); - #pkt = pkt / SourceRoute(bos=1, port=1) - - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/exercises/source_routing/solution/source_routing.p4 b/P4D2_2018_East/exercises/source_routing/solution/source_routing.p4 deleted file mode 100644 index 30c497b59..000000000 --- a/P4D2_2018_East/exercises/source_routing/solution/source_routing.p4 +++ /dev/null @@ -1,181 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; -const bit<16> TYPE_SRCROUTING = 0x1234; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header srcRoute_t { - bit<1> bos; - bit<15> port; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - srcRoute_t[MAX_HOPS] srcRoutes; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_SRCROUTING: parse_srcRouting; - default: accept; - } - } - - state parse_srcRouting { - packet.extract(hdr.srcRoutes.next); - transition select(hdr.srcRoutes.last.bos) { - 1: parse_ipv4; - default: parse_srcRouting; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action drop() { - mark_to_drop(); - } - - action srcRoute_nhop() { - standard_metadata.egress_spec = (bit<9>)hdr.srcRoutes[0].port; - hdr.srcRoutes.pop_front(1); - } - - action srcRoute_finish() { - hdr.ethernet.etherType = TYPE_IPV4; - } - - action update_ttl(){ - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - apply { - if (hdr.srcRoutes[0].isValid()){ - if (hdr.srcRoutes[0].bos == 1){ - srcRoute_finish(); - } - srcRoute_nhop(); - if (hdr.ipv4.isValid()){ - update_ttl(); - } - }else{ - drop(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.srcRoutes); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/source_routing/source_routing.p4 b/P4D2_2018_East/exercises/source_routing/source_routing.p4 deleted file mode 100644 index a84479e87..000000000 --- a/P4D2_2018_East/exercises/source_routing/source_routing.p4 +++ /dev/null @@ -1,191 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; -const bit<16> TYPE_SRCROUTING = 0x1234; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header srcRoute_t { - bit<1> bos; - bit<15> port; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - srcRoute_t[MAX_HOPS] srcRoutes; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - /* - * TODO: Modify the next line to select on hdr.ethernet.etherType - * If the value is TYPE_SRCROUTING transition to parse_srcRouting - * otherwise transition to accept. - */ - transition accept; - } - - state parse_srcRouting { - /* - * TODO: extract the next entry of hdr.srcRoutes - * while hdr.srcRoutes.last.bos is 0 transition to this state - * otherwise parse ipv4 - */ - transition accept; - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action drop() { - mark_to_drop(); - } - - action srcRoute_nhop() { - /* - * TODO: set standard_metadata.egress_spec - * to the port in hdr.srcRoutes[0] and - * pop an entry from hdr.srcRoutes - */ - } - - action srcRoute_finish() { - hdr.ethernet.etherType = TYPE_IPV4; - } - - action update_ttl(){ - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - apply { - if (hdr.srcRoutes[0].isValid()){ - /* - * TODO: add logic to: - * - If final srcRoutes (top of stack has bos==1): - * - change etherType to IP - * - choose next hop and remove top of srcRoutes stack - */ - - if (hdr.ipv4.isValid()){ - update_ttl(); - } - }else{ - drop(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.srcRoutes); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/P4D2_2018_East/exercises/source_routing/topology.json b/P4D2_2018_East/exercises/source_routing/topology.json deleted file mode 100644 index e33477cc9..000000000 --- a/P4D2_2018_East/exercises/source_routing/topology.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "hosts": [ - "h1", - "h2", - "h3" - ], - "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } - }, - "links": [ - ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], - ["s3", "s2"], ["s2", "h2"], ["s3", "h3"] - ] -} diff --git a/P4D2_2018_East/utils/Makefile b/P4D2_2018_East/utils/Makefile deleted file mode 100644 index 99e44fb3a..000000000 --- a/P4D2_2018_East/utils/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -BUILD_DIR = build -PCAP_DIR = pcaps -LOG_DIR = logs - -TOPO = topology.json -P4C = p4c-bm2-ss -RUN_SCRIPT = ../../utils/run_exercise.py - -source := $(wildcard *.p4) -outfile := $(source:.p4=.json) - -compiled_json := $(BUILD_DIR)/$(outfile) - -# Define NO_P4 to start BMv2 without a program -ifndef NO_P4 -run_args += -j $(compiled_json) -endif - -# Set BMV2_SWITCH_EXE to override the BMv2 target -ifdef BMV2_SWITCH_EXE -run_args += -b $(BMV2_SWITCH_EXE) -endif - -all: run - -run: build - sudo python $(RUN_SCRIPT) -t $(TOPO) $(run_args) - -stop: - sudo mn -c - -build: dirs $(compiled_json) - -$(BUILD_DIR)/%.json: %.p4 - $(P4C) --p4v 16 $(P4C_ARGS) -o $@ $< - -dirs: - mkdir -p $(BUILD_DIR) $(PCAP_DIR) $(LOG_DIR) - -clean: stop - rm -f *.pcap - rm -rf $(BUILD_DIR) $(PCAP_DIR) $(LOG_DIR) diff --git a/P4D2_2018_East/utils/mininet/appcontroller.py b/P4D2_2018_East/utils/mininet/appcontroller.py deleted file mode 100644 index 2cf97323b..000000000 --- a/P4D2_2018_East/utils/mininet/appcontroller.py +++ /dev/null @@ -1,104 +0,0 @@ -import subprocess - -from shortest_path import ShortestPath - -class AppController: - - def __init__(self, manifest=None, target=None, topo=None, net=None, links=None): - self.manifest = manifest - self.target = target - self.conf = manifest['targets'][target] - self.topo = topo - self.net = net - self.links = links - - def read_entries(self, filename): - entries = [] - with open(filename, 'r') as f: - for line in f: - line = line.strip() - if line == '': continue - entries.append(line) - return entries - - def add_entries(self, thrift_port=9090, sw=None, entries=None): - assert entries - if sw: thrift_port = sw.thrift_port - - print '\n'.join(entries) - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE) - p.communicate(input='\n'.join(entries)) - - def read_register(self, register, idx, thrift_port=9090, sw=None): - if sw: thrift_port = sw.thrift_port - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate(input="register_read %s %d" % (register, idx)) - reg_val = filter(lambda l: ' %s[%d]' % (register, idx) in l, stdout.split('\n'))[0].split('= ', 1)[1] - return long(reg_val) - - def start(self): - shortestpath = ShortestPath(self.links) - entries = {} - for sw in self.topo.switches(): - entries[sw] = [] - if 'switches' in self.conf and sw in self.conf['switches'] and 'entries' in self.conf['switches'][sw]: - extra_entries = self.conf['switches'][sw]['entries'] - if type(extra_entries) == list: # array of entries - entries[sw] += extra_entries - else: # path to file that contains entries - entries[sw] += self.read_entries(extra_entries) - #entries[sw] += [ - # 'table_set_default send_frame _drop', - # 'table_set_default forward _drop', - # 'table_set_default ipv4_lpm _drop'] - - for host_name in self.topo._host_links: - h = self.net.get(host_name) - for link in self.topo._host_links[host_name].values(): - sw = link['sw'] - #entries[sw].append('table_add send_frame rewrite_mac %d => %s' % (link['sw_port'], link['sw_mac'])) - #entries[sw].append('table_add forward set_dmac %s => %s' % (link['host_ip'], link['host_mac'])) - #entries[sw].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (link['host_ip'], link['host_ip'], link['sw_port'])) - iface = h.intfNames()[link['idx']] - # use mininet to set ip and mac to let it know the change - h.setIP(link['host_ip'], 24) - h.setMAC(link['host_mac']) - #h.cmd('ifconfig %s %s hw ether %s' % (iface, link['host_ip'], link['host_mac'])) - h.cmd('arp -i %s -s %s %s' % (iface, link['sw_ip'], link['sw_mac'])) - h.cmd('ethtool --offload %s rx off tx off' % iface) - h.cmd('ip route add %s dev %s' % (link['sw_ip'], iface)) - h.setDefaultRoute("via %s" % link['sw_ip']) - - for h in self.net.hosts: - h_link = self.topo._host_links[h.name].values()[0] - for sw in self.net.switches: - path = shortestpath.get(sw.name, h.name, exclude=lambda n: n[0]=='h') - if not path: continue - if not path[1][0] == 's': continue # next hop is a switch - sw_link = self.topo._sw_links[sw.name][path[1]] - #entries[sw.name].append('table_add send_frame rewrite_mac %d => %s' % (sw_link[0]['port'], sw_link[0]['mac'])) - #entries[sw.name].append('table_add forward set_dmac %s => %s' % (h_link['host_ip'], sw_link[1]['mac'])) - #entries[sw.name].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (h_link['host_ip'], h_link['host_ip'], sw_link[0]['port'])) - - for h2 in self.net.hosts: - if h == h2: continue - path = shortestpath.get(h.name, h2.name, exclude=lambda n: n[0]=='h') - if not path: continue - h_link = self.topo._host_links[h.name][path[1]] - h2_link = self.topo._host_links[h2.name].values()[0] - h.cmd('ip route add %s via %s' % (h2_link['host_ip'], h_link['sw_ip'])) - - - print "**********" - print "Configuring entries in p4 tables" - for sw_name in entries: - print - print "Configuring switch... %s" % sw_name - sw = self.net.get(sw_name) - if entries[sw_name]: - self.add_entries(sw=sw, entries=entries[sw_name]) - print "Configuration complete." - print "**********" - - def stop(self): - pass diff --git a/P4D2_2018_East/utils/mininet/apptopo.py b/P4D2_2018_East/utils/mininet/apptopo.py deleted file mode 100644 index 3491a3d29..000000000 --- a/P4D2_2018_East/utils/mininet/apptopo.py +++ /dev/null @@ -1,70 +0,0 @@ -from mininet.topo import Topo - -class AppTopo(Topo): - - def __init__(self, links, latencies={}, manifest=None, target=None, - log_dir="/tmp", bws={}, **opts): - Topo.__init__(self, **opts) - - nodes = sum(map(list, zip(*links)), []) - host_names = sorted(list(set(filter(lambda n: n[0] == 'h', nodes)))) - sw_names = sorted(list(set(filter(lambda n: n[0] == 's', nodes)))) - sw_ports = dict([(sw, []) for sw in sw_names]) - - self._host_links = {} - self._sw_links = dict([(sw, {}) for sw in sw_names]) - - for sw_name in sw_names: - self.addSwitch(sw_name, log_file="%s/%s.log" %(log_dir, sw_name)) - - for host_name in host_names: - host_num = int(host_name[1:]) - - self.addHost(host_name) - - self._host_links[host_name] = {} - host_links = filter(lambda l: l[0]==host_name or l[1]==host_name, links) - - sw_idx = 0 - for link in host_links: - sw = link[0] if link[0] != host_name else link[1] - sw_num = int(sw[1:]) - assert sw[0]=='s', "Hosts should be connected to switches, not " + str(sw) - host_ip = "10.0.%d.%d" % (sw_num, host_num) - host_mac = '00:00:00:00:%02x:%02x' % (sw_num, host_num) - delay_key = ''.join([host_name, sw]) - delay = latencies[delay_key] if delay_key in latencies else '0ms' - bw = bws[delay_key] if delay_key in bws else None - sw_ports[sw].append(host_name) - self._host_links[host_name][sw] = dict( - idx=sw_idx, - host_mac = host_mac, - host_ip = host_ip, - sw = sw, - sw_mac = "00:00:00:00:%02x:%02x" % (sw_num, host_num), - sw_ip = "10.0.%d.%d" % (sw_num, 254), - sw_port = sw_ports[sw].index(host_name)+1 - ) - self.addLink(host_name, sw, delay=delay, bw=bw, - addr1=host_mac, addr2=self._host_links[host_name][sw]['sw_mac']) - sw_idx += 1 - - for link in links: # only check switch-switch links - sw1, sw2 = link - if sw1[0] != 's' or sw2[0] != 's': continue - - delay_key = ''.join(sorted([sw1, sw2])) - delay = latencies[delay_key] if delay_key in latencies else '0ms' - bw = bws[delay_key] if delay_key in bws else None - - self.addLink(sw1, sw2, delay=delay, bw=bw)#, max_queue_size=10) - sw_ports[sw1].append(sw2) - sw_ports[sw2].append(sw1) - - sw1_num, sw2_num = int(sw1[1:]), int(sw2[1:]) - sw1_port = dict(mac="00:00:00:%02x:%02x:00" % (sw1_num, sw2_num), port=sw_ports[sw1].index(sw2)+1) - sw2_port = dict(mac="00:00:00:%02x:%02x:00" % (sw2_num, sw1_num), port=sw_ports[sw2].index(sw1)+1) - - self._sw_links[sw1][sw2] = [sw1_port, sw2_port] - self._sw_links[sw2][sw1] = [sw2_port, sw1_port] - diff --git a/P4D2_2018_East/utils/mininet/multi_switch_mininet.py b/P4D2_2018_East/utils/mininet/multi_switch_mininet.py deleted file mode 100755 index 0bb406f61..000000000 --- a/P4D2_2018_East/utils/mininet/multi_switch_mininet.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/env python2 - -# 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. -# - -import signal -import os -import sys -import subprocess -import argparse -import json -import importlib -import re -from time import sleep - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.link import TCLink -from mininet.log import setLogLevel, info -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host -import apptopo -import appcontroller - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--bmv2-log', help='verbose messages in log file', action="store_true") -parser.add_argument('--cli', help="start the mininet cli", action="store_true") -parser.add_argument('--auto-control-plane', help='enable automatic control plane population', action="store_true") -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - action="store_true") -parser.add_argument('--manifest', '-m', help='Path to manifest file', - type=str, action="store", required=True) -parser.add_argument('--target', '-t', help='Target in manifest file to run', - type=str, action="store", required=True) -parser.add_argument('--log-dir', '-l', help='Location to save output to', - type=str, action="store", required=True) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) - - -args = parser.parse_args() - - -next_thrift_port = args.thrift_port - -def run_command(command): - return os.WEXITSTATUS(os.system(command)) - -def configureP4Switch(**switch_args): - class ConfiguredP4Switch(P4Switch): - def __init__(self, *opts, **kwargs): - global next_thrift_port - kwargs.update(switch_args) - kwargs['thrift_port'] = next_thrift_port - next_thrift_port += 1 - P4Switch.__init__(self, *opts, **kwargs) - return ConfiguredP4Switch - - -def main(): - - with open(args.manifest, 'r') as f: - manifest = json.load(f) - - conf = manifest['targets'][args.target] - params = conf['parameters'] if 'parameters' in conf else {} - - os.environ.update(dict(map(lambda (k,v): (k, str(v)), params.iteritems()))) - - def formatParams(s): - for param in params: - s = re.sub('\$'+param+'(\W|$)', str(params[param]) + r'\1', s) - s = s.replace('${'+param+'}', str(params[param])) - return s - - AppTopo = apptopo.AppTopo - AppController = appcontroller.AppController - - if 'topo_module' in conf: - sys.path.insert(0, os.path.dirname(args.manifest)) - topo_module = importlib.import_module(conf['topo_module']) - AppTopo = topo_module.CustomAppTopo - - if 'controller_module' in conf: - sys.path.insert(0, os.path.dirname(args.manifest)) - controller_module = importlib.import_module(conf['controller_module']) - AppController = controller_module.CustomAppController - - if not os.path.isdir(args.log_dir): - if os.path.exists(args.log_dir): raise Exception('Log dir exists and is not a dir') - os.mkdir(args.log_dir) - os.environ['P4APP_LOGDIR'] = args.log_dir - - - links = [l[:2] for l in conf['links']] - latencies = dict([(''.join(sorted(l[:2])), l[2]) for l in conf['links'] if len(l)>=3]) - bws = dict([(''.join(sorted(l[:2])), l[3]) for l in conf['links'] if len(l)>=4]) - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'latency' not in host: continue - for a, b in links: - if a != host_name and b != host_name: continue - other = a if a != host_name else b - latencies[host_name+other] = host['latency'] - - for l in latencies: - if isinstance(latencies[l], (str, unicode)): - latencies[l] = formatParams(latencies[l]) - else: - latencies[l] = str(latencies[l]) + "ms" - - bmv2_log = args.bmv2_log or ('bmv2_log' in conf and conf['bmv2_log']) - pcap_dump = args.pcap_dump or ('pcap_dump' in conf and conf['pcap_dump']) - - topo = AppTopo(links, latencies, manifest=manifest, target=args.target, - log_dir=args.log_dir, bws=bws) - switchClass = configureP4Switch( - sw_path=args.behavioral_exe, - json_path=args.json, - log_console=bmv2_log, - pcap_dump=pcap_dump) - net = Mininet(topo = topo, - link = TCLink, - host = P4Host, - switch = switchClass, - controller = None) - net.start() - - sleep(1) - - controller = None - if args.auto_control_plane or 'controller_module' in conf: - controller = AppController(manifest=manifest, target=args.target, - topo=topo, net=net, links=links) - controller.start() - - - for h in net.hosts: - h.describe() - - if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: - print message_file.read() - - if args.cli or ('cli' in conf and conf['cli']): - CLI(net) - - stdout_files = dict() - return_codes = [] - host_procs = [] - - - def formatCmd(cmd): - for h in net.hosts: - cmd = cmd.replace(h.name, h.defaultIntf().updateIP()) - return cmd - - def _wait_for_exit(p, host): - print p.communicate() - if p.returncode is None: - p.wait() - print p.communicate() - return_codes.append(p.returncode) - if host_name in stdout_files: - stdout_files[host_name].flush() - stdout_files[host_name].close() - - print '\n'.join(map(lambda (k,v): "%s: %s"%(k,v), params.iteritems())) + '\n' - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'cmd' not in host: continue - - h = net.get(host_name) - stdout_filename = os.path.join(args.log_dir, h.name + '.stdout') - stdout_files[h.name] = open(stdout_filename, 'w') - cmd = formatCmd(host['cmd']) - print h.name, cmd - p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, preexec_fn=os.setpgrp) - if 'startup_sleep' in host: sleep(host['startup_sleep']) - - if 'wait' in host and host['wait']: - _wait_for_exit(p, host_name) - else: - host_procs.append((p, host_name)) - - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: - _wait_for_exit(p, host_name) - - - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: - continue - if p.returncode is None: - run_command('pkill -INT -P %d' % p.pid) - sleep(0.2) - rc = run_command('pkill -0 -P %d' % p.pid) # check if it's still running - if rc == 0: # the process group is still running, send TERM - sleep(1) # give it a little more time to exit gracefully - run_command('pkill -TERM -P %d' % p.pid) - _wait_for_exit(p, host_name) - - if 'after' in conf and 'cmd' in conf['after']: - cmds = conf['after']['cmd'] if type(conf['after']['cmd']) == list else [conf['after']['cmd']] - for cmd in cmds: - os.system(cmd) - - if controller: controller.stop() - - net.stop() - -# if bmv2_log: -# os.system('bash -c "cp /tmp/p4s.s*.log \'%s\'"' % args.log_dir) -# if pcap_dump: -# os.system('bash -c "cp *.pcap \'%s\'"' % args.log_dir) - - bad_codes = [rc for rc in return_codes if rc != 0] - if len(bad_codes): sys.exit(1) - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/P4D2_2018_East/utils/mininet/p4_mininet.py b/P4D2_2018_East/utils/mininet/p4_mininet.py deleted file mode 100644 index 8abe79f3a..000000000 --- a/P4D2_2018_East/utils/mininet/p4_mininet.py +++ /dev/null @@ -1,161 +0,0 @@ -# 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. -# - -from mininet.net import Mininet -from mininet.node import Switch, Host -from mininet.log import setLogLevel, info, error, debug -from mininet.moduledeps import pathCheck -from sys import exit -from time import sleep -import os -import tempfile -import socket - -class P4Host(Host): - def config(self, **params): - r = super(P4Host, self).config(**params) - - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf().name, off) - self.cmd(cmd) - - # disable IPv6 - self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - - return r - - def describe(self, sw_addr=None, sw_mac=None): - print "**********" - print "Network configuration for: %s" % self.name - print "Default interface: %s\t%s\t%s" %( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC() - ) - if sw_addr is not None or sw_mac is not None: - print "Default route to switch: %s (%s)" % (sw_addr, sw_mac) - print "**********" - -class P4Switch(Switch): - """P4 virtual switch""" - device_id = 0 - - def __init__(self, name, sw_path = None, json_path = None, - log_file = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): - Switch.__init__(self, name, **kwargs) - assert(sw_path) - assert(json_path) - # make sure that the provided sw_path is valid - pathCheck(sw_path) - # make sure that the provided JSON file exists - if not os.path.isfile(json_path): - error("Invalid JSON file.\n") - exit(1) - self.sw_path = sw_path - self.json_path = json_path - self.verbose = verbose - self.log_file = log_file - if self.log_file is None: - self.log_file = "/tmp/p4s.{}.log".format(self.name) - self.output = open(self.log_file, 'w') - self.thrift_port = thrift_port - self.pcap_dump = pcap_dump - self.enable_debugger = enable_debugger - self.log_console = log_console - if device_id is not None: - self.device_id = device_id - P4Switch.device_id = max(P4Switch.device_id, device_id) - else: - self.device_id = P4Switch.device_id - P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) - - @classmethod - def setup(cls): - pass - - def check_switch_started(self, pid): - """While the process is running (pid exists), we check if the Thrift - server has been started. If the Thrift server is ready, we assume that - the switch was started successfully. This is only reliable if the Thrift - server is started at the end of the init process""" - while True: - if not os.path.exists(os.path.join("/proc", str(pid))): - return False - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(0.5) - result = sock.connect_ex(("localhost", self.thrift_port)) - if result == 0: - return True - - def start(self, controllers): - "Start up a new P4 switch" - info("Starting P4 switch {}.\n".format(self.name)) - args = [self.sw_path] - for port, intf in self.intfs.items(): - if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) - if self.pcap_dump: - args.append("--pcap") - # args.append("--useFiles") - if self.thrift_port: - args.extend(['--thrift-port', str(self.thrift_port)]) - if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) - P4Switch.device_id += 1 - args.append(self.json_path) - if self.enable_debugger: - args.append("--debugger") - if self.log_console: - args.append("--log-console") - info(' '.join(args) + "\n") - - pid = None - with tempfile.NamedTemporaryFile() as f: - # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &') - self.cmd(' '.join(args) + ' >' + self.log_file + ' 2>&1 & echo $! >> ' + f.name) - pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) - sleep(1) - if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly." - "Check the switch log file.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) - - def stop(self): - "Terminate P4 switch." - self.output.flush() - self.cmd('kill %' + self.sw_path) - self.cmd('wait') - self.deleteIntfs() - - def attach(self, intf): - "Connect a data port" - assert(0) - - def detach(self, intf): - "Disconnect a data port" - assert(0) diff --git a/P4D2_2018_East/utils/mininet/shortest_path.py b/P4D2_2018_East/utils/mininet/shortest_path.py deleted file mode 100644 index 971b1b446..000000000 --- a/P4D2_2018_East/utils/mininet/shortest_path.py +++ /dev/null @@ -1,78 +0,0 @@ -class ShortestPath: - - def __init__(self, edges=[]): - self.neighbors = {} - for edge in edges: - self.addEdge(*edge) - - def addEdge(self, a, b): - if a not in self.neighbors: self.neighbors[a] = [] - if b not in self.neighbors[a]: self.neighbors[a].append(b) - - if b not in self.neighbors: self.neighbors[b] = [] - if a not in self.neighbors[b]: self.neighbors[b].append(a) - - def get(self, a, b, exclude=lambda node: False): - # Shortest path from a to b - return self._recPath(a, b, [], exclude) - - def _recPath(self, a, b, visited, exclude): - if a == b: return [a] - new_visited = visited + [a] - paths = [] - for neighbor in self.neighbors[a]: - if neighbor in new_visited: continue - if exclude(neighbor) and neighbor != b: continue - path = self._recPath(neighbor, b, new_visited, exclude) - if path: paths.append(path) - - paths.sort(key=len) - return [a] + paths[0] if len(paths) else None - -if __name__ == '__main__': - - edges = [ - (1, 2), - (1, 3), - (1, 5), - (2, 4), - (3, 4), - (3, 5), - (3, 6), - (4, 6), - (5, 6), - (7, 8) - - ] - sp = ShortestPath(edges) - - assert sp.get(1, 1) == [1] - assert sp.get(2, 2) == [2] - - assert sp.get(1, 2) == [1, 2] - assert sp.get(2, 1) == [2, 1] - - assert sp.get(1, 3) == [1, 3] - assert sp.get(3, 1) == [3, 1] - - assert sp.get(4, 6) == [4, 6] - assert sp.get(6, 4) == [6, 4] - - assert sp.get(2, 6) == [2, 4, 6] - assert sp.get(6, 2) == [6, 4, 2] - - assert sp.get(1, 6) in [[1, 3, 6], [1, 5, 6]] - assert sp.get(6, 1) in [[6, 3, 1], [6, 5, 1]] - - assert sp.get(2, 5) == [2, 1, 5] - assert sp.get(5, 2) == [5, 1, 2] - - assert sp.get(4, 5) in [[4, 3, 5], [4, 6, 5]] - assert sp.get(5, 4) in [[5, 3, 4], [6, 6, 4]] - - assert sp.get(7, 8) == [7, 8] - assert sp.get(8, 7) == [8, 7] - - assert sp.get(1, 7) == None - assert sp.get(7, 2) == None - diff --git a/P4D2_2018_East/utils/mininet/single_switch_mininet.py b/P4D2_2018_East/utils/mininet/single_switch_mininet.py deleted file mode 100755 index e2e76366c..000000000 --- a/P4D2_2018_East/utils/mininet/single_switch_mininet.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python2 - -# 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. -# - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.log import setLogLevel, info -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host - -import argparse -from subprocess import PIPE, Popen -from time import sleep - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--num-hosts', help='Number of hosts to connect to switch', - type=int, action="store", default=2) -parser.add_argument('--mode', choices=['l2', 'l3'], type=str, default='l3') -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--log-file', help='Path to write the switch log file', - type=str, action="store", required=False) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - type=str, action="store", required=False, default=False) -parser.add_argument('--switch-config', help='simple_switch_CLI script to configure switch', - type=str, action="store", required=False, default=False) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) - -args = parser.parse_args() - - -class SingleSwitchTopo(Topo): - "Single switch connected to n (< 256) hosts." - def __init__(self, sw_path, json_path, log_file, - thrift_port, pcap_dump, n, **opts): - # Initialize topology and default options - Topo.__init__(self, **opts) - - switch = self.addSwitch('s1', - sw_path = sw_path, - json_path = json_path, - log_console = True, - log_file = log_file, - thrift_port = thrift_port, - enable_debugger = False, - pcap_dump = pcap_dump) - - for h in xrange(n): - host = self.addHost('h%d' % (h + 1), - ip = "10.0.%d.10/24" % h, - mac = '00:04:00:00:00:%02x' %h) - print "Adding host", str(host) - self.addLink(host, switch) - -def main(): - num_hosts = args.num_hosts - mode = args.mode - - topo = SingleSwitchTopo(args.behavioral_exe, - args.json, - args.log_file, - args.thrift_port, - args.pcap_dump, - num_hosts) - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None) - net.start() - - - sw_mac = ["00:aa:bb:00:00:%02x" % n for n in xrange(num_hosts)] - - sw_addr = ["10.0.%d.1" % n for n in xrange(num_hosts)] - - for n in xrange(num_hosts): - h = net.get('h%d' % (n + 1)) - if mode == "l2": - h.setDefaultRoute("dev %s" % h.defaultIntf().name) - else: - h.setARP(sw_addr[n], sw_mac[n]) - h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n])) - - for n in xrange(num_hosts): - h = net.get('h%d' % (n + 1)) - h.describe(sw_addr[n], sw_mac[n]) - - sleep(1) - - if args.switch_config is not None: - print - print "Reading switch configuration script:", args.switch_config - with open(args.switch_config, 'r') as config_file: - switch_config = config_file.read() - - print "Configuring switch..." - proc = Popen(["simple_switch_CLI"], stdin=PIPE) - proc.communicate(input=switch_config) - - print "Configuration complete." - print - - print "Ready !" - - if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: - print message_file.read() - - CLI( net ) - net.stop() - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/P4D2_2018_East/utils/netstat.py b/P4D2_2018_East/utils/netstat.py deleted file mode 100644 index bb12ffdf5..000000000 --- a/P4D2_2018_East/utils/netstat.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2017-present Open Networking Foundation -# -# 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. -# - -import psutil -def check_listening_on_port(port): - for c in psutil.net_connections(kind='inet'): - if c.status == 'LISTEN' and c.laddr[1] == port: - return True - return False diff --git a/P4D2_2018_East/utils/p4_mininet.py b/P4D2_2018_East/utils/p4_mininet.py deleted file mode 100644 index b7fbbcd80..000000000 --- a/P4D2_2018_East/utils/p4_mininet.py +++ /dev/null @@ -1,162 +0,0 @@ -# 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. -# - -from mininet.net import Mininet -from mininet.node import Switch, Host -from mininet.log import setLogLevel, info, error, debug -from mininet.moduledeps import pathCheck -from sys import exit -import os -import tempfile -import socket -from time import sleep - -from netstat import check_listening_on_port - -SWITCH_START_TIMEOUT = 10 # seconds - -class P4Host(Host): - def config(self, **params): - r = super(Host, self).config(**params) - - self.defaultIntf().rename("eth0") - - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload eth0 %s off" % off - self.cmd(cmd) - - # disable IPv6 - self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - - return r - - def describe(self): - print "**********" - print self.name - print "default interface: %s\t%s\t%s" %( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC() - ) - print "**********" - -class P4Switch(Switch): - """P4 virtual switch""" - device_id = 0 - - def __init__(self, name, sw_path = None, json_path = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): - Switch.__init__(self, name, **kwargs) - assert(sw_path) - assert(json_path) - # make sure that the provided sw_path is valid - pathCheck(sw_path) - # make sure that the provided JSON file exists - if not os.path.isfile(json_path): - error("Invalid JSON file.\n") - exit(1) - self.sw_path = sw_path - self.json_path = json_path - self.verbose = verbose - logfile = "/tmp/p4s.{}.log".format(self.name) - self.output = open(logfile, 'w') - self.thrift_port = thrift_port - if check_listening_on_port(self.thrift_port): - error('%s cannot bind port %d because it is bound by another process\n' % (self.name, self.grpc_port)) - exit(1) - self.pcap_dump = pcap_dump - self.enable_debugger = enable_debugger - self.log_console = log_console - if device_id is not None: - self.device_id = device_id - P4Switch.device_id = max(P4Switch.device_id, device_id) - else: - self.device_id = P4Switch.device_id - P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) - - @classmethod - def setup(cls): - pass - - def check_switch_started(self, pid): - """While the process is running (pid exists), we check if the Thrift - server has been started. If the Thrift server is ready, we assume that - the switch was started successfully. This is only reliable if the Thrift - server is started at the end of the init process""" - while True: - if not os.path.exists(os.path.join("/proc", str(pid))): - return False - if check_listening_on_port(self.thrift_port): - return True - sleep(0.5) - - def start(self, controllers): - "Start up a new P4 switch" - info("Starting P4 switch {}.\n".format(self.name)) - args = [self.sw_path] - for port, intf in self.intfs.items(): - if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) - if self.pcap_dump: - args.append("--pcap") - # args.append("--useFiles") - if self.thrift_port: - args.extend(['--thrift-port', str(self.thrift_port)]) - if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) - P4Switch.device_id += 1 - args.append(self.json_path) - if self.enable_debugger: - args.append("--debugger") - if self.log_console: - args.append("--log-console") - logfile = "/tmp/p4s.{}.log".format(self.name) - info(' '.join(args) + "\n") - - pid = None - with tempfile.NamedTemporaryFile() as f: - # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &') - self.cmd(' '.join(args) + ' >' + logfile + ' 2>&1 & echo $! >> ' + f.name) - pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) - if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) - - def stop(self): - "Terminate P4 switch." - self.output.flush() - self.cmd('kill %' + self.sw_path) - self.cmd('wait') - self.deleteIntfs() - - def attach(self, intf): - "Connect a data port" - assert(0) - - def detach(self, intf): - "Disconnect a data port" - assert(0) diff --git a/P4D2_2018_East/utils/p4apprunner.py b/P4D2_2018_East/utils/p4apprunner.py deleted file mode 100755 index 36b9eeab8..000000000 --- a/P4D2_2018_East/utils/p4apprunner.py +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env python2 -# 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. - -from __future__ import print_function - -import argparse -from collections import OrderedDict -import json -import os -import sys -import tarfile - -parser = argparse.ArgumentParser(description='p4apprunner') -parser.add_argument('--build-dir', help='Directory to build in.', - type=str, action='store', required=False, default='/tmp') -parser.add_argument('--quiet', help='Suppress log messages.', - action='store_true', required=False, default=False) -parser.add_argument('--manifest', help='Path to manifest file.', - type=str, action='store', required=False, default='./p4app.json') -parser.add_argument('app', help='.p4app package to run.', type=str) -parser.add_argument('target', help=('Target to run. Defaults to the first target ' - 'in the package.'), - nargs='?', type=str) - -args = parser.parse_args() - -def log(*items): - if args.quiet != True: - print(*items) - -def log_error(*items): - print(*items, file=sys.stderr) - -def run_command(command): - log('>', command) - return os.WEXITSTATUS(os.system(command)) - -class Manifest: - def __init__(self, program_file, language, target, target_config): - self.program_file = program_file - self.language = language - self.target = target - self.target_config = target_config - -def read_manifest(manifest_file): - manifest = json.load(manifest_file, object_pairs_hook=OrderedDict) - - if 'program' not in manifest: - log_error('No program defined in manifest.') - sys.exit(1) - program_file = manifest['program'] - - if 'language' not in manifest: - log_error('No language defined in manifest.') - sys.exit(1) - language = manifest['language'] - - if 'targets' not in manifest or len(manifest['targets']) < 1: - log_error('No targets defined in manifest.') - sys.exit(1) - - if args.target is not None: - chosen_target = args.target - elif 'default-target' in manifest: - chosen_target = manifest['default-target'] - else: - chosen_target = manifest['targets'].keys()[0] - - if chosen_target not in manifest['targets']: - log_error('Target not found in manifest:', chosen_target) - sys.exit(1) - - return Manifest(program_file, language, chosen_target, manifest['targets'][chosen_target]) - - -def run_compile_bmv2(manifest): - if 'run-before-compile' in manifest.target_config: - commands = manifest.target_config['run-before-compile'] - if not isinstance(commands, list): - log_error('run-before-compile should be a list:', commands) - sys.exit(1) - for command in commands: - run_command(command) - - compiler_args = [] - - if manifest.language == 'p4-14': - compiler_args.append('--p4v 14') - elif manifest.language == 'p4-16': - compiler_args.append('--p4v 16') - else: - log_error('Unknown language:', manifest.language) - sys.exit(1) - - if 'compiler-flags' in manifest.target_config: - flags = manifest.target_config['compiler-flags'] - if not isinstance(flags, list): - log_error('compiler-flags should be a list:', flags) - sys.exit(1) - compiler_args.extend(flags) - - # Compile the program. - output_file = manifest.program_file + '.json' - compiler_args.append('"%s"' % manifest.program_file) - compiler_args.append('-o "%s"' % output_file) - rv = run_command('p4c-bm2-ss %s' % ' '.join(compiler_args)) - - if 'run-after-compile' in manifest.target_config: - commands = manifest.target_config['run-after-compile'] - if not isinstance(commands, list): - log_error('run-after-compile should be a list:', commands) - sys.exit(1) - for command in commands: - run_command(command) - - if rv != 0: - log_error('Compile failed.') - sys.exit(1) - - return output_file - -def run_mininet(manifest): - output_file = run_compile_bmv2(manifest) - - # Run the program using the BMV2 Mininet simple switch. - switch_args = [] - - # We'll place the switch's log file in current (build) folder. - cwd = os.getcwd() - log_file = os.path.join(cwd, manifest.program_file + '.log') - print ("*** Log file %s" % log_file) - switch_args.append('--log-file "%s"' % log_file) - - pcap_dir = os.path.join(cwd) - print ("*** Pcap folder %s" % pcap_dir) - switch_args.append('--pcap-dump "%s" '% pcap_dir) - - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: - - print(file=message) - print('======================================================================', - file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', - file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) - print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI', file=message) - print(file=message) - print('To view the switch log, run this command from your host OS:', file=message) - print(' tail -f %s' % log_file, file=message) - print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) - print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) - - switch_args.append('--cli-message "%s"' % message_file) - - if 'num-hosts' in manifest.target_config: - switch_args.append('--num-hosts %s' % manifest.target_config['num-hosts']) - - if 'switch-config' in manifest.target_config: - switch_args.append('--switch-config "%s"' % manifest.target_config['switch-config']) - - switch_args.append('--behavioral-exe "%s"' % 'simple_switch') - switch_args.append('--json "%s"' % output_file) - - program = '"%s/mininet/single_switch_mininet.py"' % sys.path[0] - return run_command('python2 %s %s' % (program, ' '.join(switch_args))) - -def run_multiswitch(manifest): - output_file = run_compile_bmv2(manifest) - - script_args = [] - cwd = os.getcwd() - log_dir = os.path.join(cwd, cwd + '/logs') - print ("*** Log directory %s" % log_dir) - script_args.append('--log-dir "%s"' % log_dir) - pcap_dir = os.path.join(cwd) - print ("*** Pcap directory %s" % cwd) - script_args.append('--manifest "%s"' % args.manifest) - script_args.append('--target "%s"' % manifest.target) - if 'auto-control-plane' in manifest.target_config and manifest.target_config['auto-control-plane']: - script_args.append('--auto-control-plane' ) - script_args.append('--behavioral-exe "%s"' % 'simple_switch') - script_args.append('--json "%s"' % output_file) - #script_args.append('--cli') - - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: - - print(file=message) - print('======================================================================', - file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', - file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) - print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI --thrift-port ', file=message) - print(file=message) - print('To view a switch log, run this command from your host OS:', file=message) - print(' tail -f %s/.log' % log_dir, file=message) - print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) - print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) - - script_args.append('--cli-message "%s"' % message_file) - - program = '"%s/mininet/multi_switch_mininet.py"' % sys.path[0] - return run_command('python2 %s %s' % (program, ' '.join(script_args))) - -def run_stf(manifest): - output_file = run_compile_bmv2(manifest) - - if not 'test' in manifest.target_config: - log_error('No STF test file provided.') - sys.exit(1) - stf_file = manifest.target_config['test'] - - # Run the program using the BMV2 STF interpreter. - stf_args = [] - stf_args.append('-v') - stf_args.append(os.path.join(args.build_dir, output_file)) - stf_args.append(os.path.join(args.build_dir, stf_file)) - - program = '"%s/stf/bmv2stf.py"' % sys.path[0] - rv = run_command('python2 %s %s' % (program, ' '.join(stf_args))) - if rv != 0: - sys.exit(1) - return rv - -def run_custom(manifest): - output_file = run_compile_bmv2(manifest) - python_path = 'PYTHONPATH=$PYTHONPATH:/scripts/mininet/' - script_args = [] - script_args.append('--behavioral-exe "%s"' % 'simple_switch') - script_args.append('--json "%s"' % output_file) - script_args.append('--cli "%s"' % 'simple_switch_CLI') - if not 'program' in manifest.target_config: - log_error('No mininet program file provided.') - sys.exit(1) - program = manifest.target_config['program'] - rv = run_command('%s python2 %s %s' % (python_path, program, ' '.join(script_args))) - - if rv != 0: - sys.exit(1) - return rv - -def main(): - log('Entering build directory.') - os.chdir(args.build_dir) - - # A '.p4app' package is really just a '.tar.gz' archive. Extract it so we - # can process its contents. - log('Extracting package.') - tar = tarfile.open(args.app) - tar.extractall() - tar.close() - - log('Reading package manifest.') - with open(args.manifest, 'r') as manifest_file: - manifest = read_manifest(manifest_file) - - # Dispatch to the backend implementation for this target. - backend = manifest.target - if 'use' in manifest.target_config: - backend = manifest.target_config['use'] - - if backend == 'mininet': - rc = run_mininet(manifest) - elif backend == 'multiswitch': - rc = run_multiswitch(manifest) - elif backend == 'stf': - rc = run_stf(manifest) - elif backend == 'custom': - rc = run_custom(manifest) - elif backend == 'compile-bmv2': - run_compile_bmv2(manifest) - rc = 0 - else: - log_error('Target specifies unknown backend:', backend) - sys.exit(1) - - sys.exit(rc) - -if __name__ == '__main__': - main() diff --git a/P4D2_2018_East/utils/p4runtime_switch.py b/P4D2_2018_East/utils/p4runtime_switch.py deleted file mode 100644 index df919ed20..000000000 --- a/P4D2_2018_East/utils/p4runtime_switch.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright 2017-present Barefoot Networks, Inc. -# Copyright 2017-present Open Networking Foundation -# -# 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. -# - -import sys, os, tempfile, socket -from time import sleep - -from mininet.node import Switch -from mininet.moduledeps import pathCheck -from mininet.log import info, error, debug - -from p4_mininet import P4Switch, SWITCH_START_TIMEOUT -from netstat import check_listening_on_port - -class P4RuntimeSwitch(P4Switch): - "BMv2 switch with gRPC support" - next_grpc_port = 50051 - - def __init__(self, name, sw_path = None, json_path = None, - grpc_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): - Switch.__init__(self, name, **kwargs) - assert (sw_path) - self.sw_path = sw_path - # make sure that the provided sw_path is valid - pathCheck(sw_path) - - if json_path is not None: - # make sure that the provided JSON file exists - if not os.path.isfile(json_path): - error("Invalid JSON file.\n") - exit(1) - self.json_path = json_path - else: - self.json_path = None - - if grpc_port is not None: - self.grpc_port = grpc_port - else: - self.grpc_port = P4RuntimeSwitch.next_grpc_port - P4RuntimeSwitch.next_grpc_port += 1 - - if check_listening_on_port(self.grpc_port): - error('%s cannot bind port %d because it is bound by another process\n' % (self.name, self.grpc_port)) - exit(1) - - self.verbose = verbose - logfile = "/tmp/p4s.{}.log".format(self.name) - self.output = open(logfile, 'w') - self.pcap_dump = pcap_dump - self.enable_debugger = enable_debugger - self.log_console = log_console - if device_id is not None: - self.device_id = device_id - P4Switch.device_id = max(P4Switch.device_id, device_id) - else: - self.device_id = P4Switch.device_id - P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) - - - def check_switch_started(self, pid): - for _ in range(SWITCH_START_TIMEOUT * 2): - if not os.path.exists(os.path.join("/proc", str(pid))): - return False - if check_listening_on_port(self.grpc_port): - return True - sleep(0.5) - - def start(self, controllers): - info("Starting P4 switch {}.\n".format(self.name)) - args = [self.sw_path] - for port, intf in self.intfs.items(): - if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) - if self.pcap_dump: - args.append("--pcap") - if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) - P4Switch.device_id += 1 - if self.json_path: - args.append(self.json_path) - else: - args.append("--no-p4") - if self.enable_debugger: - args.append("--debugger") - if self.log_console: - args.append("--log-console") - if self.grpc_port: - args.append("-- --grpc-server-addr 0.0.0.0:" + str(self.grpc_port)) - cmd = ' '.join(args) - info(cmd + "\n") - - logfile = "/tmp/p4s.{}.log".format(self.name) - pid = None - with tempfile.NamedTemporaryFile() as f: - self.cmd(cmd + ' >' + logfile + ' 2>&1 & echo $! >> ' + f.name) - pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) - if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) - diff --git a/P4D2_2018_East/utils/run_exercise.py b/P4D2_2018_East/utils/run_exercise.py deleted file mode 100755 index 84997d702..000000000 --- a/P4D2_2018_East/utils/run_exercise.py +++ /dev/null @@ -1,382 +0,0 @@ -#!/usr/bin/env python2 -# 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. -# -# Adapted by Robert MacDavid (macdavid@cs.princeton.edu) from scripts found in -# the p4app repository (https://github.com/p4lang/p4app) -# -# We encourage you to dissect this script to better understand the BMv2/Mininet -# environment used by the P4 tutorial. -# -import os, sys, json, subprocess, re, argparse -from time import sleep - -from p4_mininet import P4Switch, P4Host - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.link import TCLink -from mininet.cli import CLI - -from p4runtime_switch import P4RuntimeSwitch - -def configureP4Switch(**switch_args): - """ Helper class that is called by mininet to initialize - the virtual P4 switches. The purpose is to ensure each - switch's thrift server is using a unique port. - """ - if "sw_path" in switch_args and 'grpc' in switch_args['sw_path']: - # If grpc appears in the BMv2 switch target, we assume will start P4 Runtime - class ConfiguredP4RuntimeSwitch(P4RuntimeSwitch): - def __init__(self, *opts, **kwargs): - kwargs.update(switch_args) - P4RuntimeSwitch.__init__(self, *opts, **kwargs) - - def describe(self): - print "%s -> gRPC port: %d" % (self.name, self.grpc_port) - - return ConfiguredP4RuntimeSwitch - else: - class ConfiguredP4Switch(P4Switch): - next_thrift_port = 9090 - def __init__(self, *opts, **kwargs): - global next_thrift_port - kwargs.update(switch_args) - kwargs['thrift_port'] = ConfiguredP4Switch.next_thrift_port - ConfiguredP4Switch.next_thrift_port += 1 - P4Switch.__init__(self, *opts, **kwargs) - - def describe(self): - print "%s -> Thrift port: %d" % (self.name, self.thrift_port) - - return ConfiguredP4Switch - - -class ExerciseTopo(Topo): - """ The mininet topology class for the P4 tutorial exercises. - A custom class is used because the exercises make a few topology - assumptions, mostly about the IP and MAC addresses. - """ - def __init__(self, hosts, switches, links, log_dir, **opts): - Topo.__init__(self, **opts) - host_links = [] - switch_links = [] - self.sw_port_mapping = {} - - for link in links: - if link['node1'][0] == 'h': - host_links.append(link) - else: - switch_links.append(link) - - link_sort_key = lambda x: x['node1'] + x['node2'] - # Links must be added in a sorted order so bmv2 port numbers are predictable - host_links.sort(key=link_sort_key) - switch_links.sort(key=link_sort_key) - - for sw in switches: - self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw)) - - for link in host_links: - host_name = link['node1'] - host_sw = link['node2'] - host_num = int(host_name[1:]) - sw_num = int(host_sw[1:]) - host_ip = "10.0.%d.%d" % (sw_num, host_num) - host_mac = '00:00:00:00:%02x:%02x' % (sw_num, host_num) - # Each host IP should be /24, so all exercise traffic will use the - # default gateway (the switch) without sending ARP requests. - self.addHost(host_name, ip=host_ip+'/24', mac=host_mac) - self.addLink(host_name, host_sw, - delay=link['latency'], bw=link['bandwidth'], - addr1=host_mac, addr2=host_mac) - self.addSwitchPort(host_sw, host_name) - - for link in switch_links: - self.addLink(link['node1'], link['node2'], - delay=link['latency'], bw=link['bandwidth']) - self.addSwitchPort(link['node1'], link['node2']) - self.addSwitchPort(link['node2'], link['node1']) - - self.printPortMapping() - - def addSwitchPort(self, sw, node2): - if sw not in self.sw_port_mapping: - self.sw_port_mapping[sw] = [] - portno = len(self.sw_port_mapping[sw])+1 - self.sw_port_mapping[sw].append((portno, node2)) - - def printPortMapping(self): - print "Switch port mapping:" - for sw in sorted(self.sw_port_mapping.keys()): - print "%s: " % sw, - for portno, node2 in self.sw_port_mapping[sw]: - print "%d:%s\t" % (portno, node2), - print - - -class ExerciseRunner: - """ - Attributes: - log_dir : string // directory for mininet log files - pcap_dir : string // directory for mininet switch pcap files - quiet : bool // determines if we print logger messages - - hosts : list // list of mininet host names - switches : dict // mininet host names and their associated properties - links : list // list of mininet link properties - - switch_json : string // json of the compiled p4 example - bmv2_exe : string // name or path of the p4 switch binary - - topo : Topo object // The mininet topology instance - net : Mininet object // The mininet instance - - """ - def logger(self, *items): - if not self.quiet: - print(' '.join(items)) - - def formatLatency(self, l): - """ Helper method for parsing link latencies from the topology json. """ - if isinstance(l, (str, unicode)): - return l - else: - return str(l) + "ms" - - - def __init__(self, topo_file, log_dir, pcap_dir, - switch_json, bmv2_exe='simple_switch', quiet=False): - """ Initializes some attributes and reads the topology json. Does not - actually run the exercise. Use run_exercise() for that. - - Arguments: - topo_file : string // A json file which describes the exercise's - mininet topology. - log_dir : string // Path to a directory for storing exercise logs - pcap_dir : string // Ditto, but for mininet switch pcap files - switch_json : string // Path to a compiled p4 json for bmv2 - bmv2_exe : string // Path to the p4 behavioral binary - quiet : bool // Enable/disable script debug messages - """ - - self.quiet = quiet - self.logger('Reading topology file.') - with open(topo_file, 'r') as f: - topo = json.load(f) - self.hosts = topo['hosts'] - self.switches = topo['switches'] - self.links = self.parse_links(topo['links']) - - # Ensure all the needed directories exist and are directories - for dir_name in [log_dir, pcap_dir]: - if not os.path.isdir(dir_name): - if os.path.exists(dir_name): - raise Exception("'%s' exists and is not a directory!" % dir_name) - os.mkdir(dir_name) - self.log_dir = log_dir - self.pcap_dir = pcap_dir - self.switch_json = switch_json - self.bmv2_exe = bmv2_exe - - - def run_exercise(self): - """ Sets up the mininet instance, programs the switches, - and starts the mininet CLI. This is the main method to run after - initializing the object. - """ - # Initialize mininet with the topology specified by the config - self.create_network() - self.net.start() - sleep(1) - - # some programming that must happen after the net has started - self.program_hosts() - self.program_switches() - - # wait for that to finish. Not sure how to do this better - sleep(1) - - self.do_net_cli() - # stop right after the CLI is exited - self.net.stop() - - - def parse_links(self, unparsed_links): - """ Given a list of links descriptions of the form [node1, node2, latency, bandwidth] - with the latency and bandwidth being optional, parses these descriptions - into dictionaries and store them as self.links - """ - links = [] - for link in unparsed_links: - # make sure each link's endpoints are ordered alphabetically - s, t, = link[0], link[1] - if s > t: - s,t = t,s - - link_dict = {'node1':s, - 'node2':t, - 'latency':'0ms', - 'bandwidth':None - } - if len(link) > 2: - link_dict['latency'] = self.formatLatency(link[2]) - if len(link) > 3: - link_dict['bandwidth'] = link[3] - - if link_dict['node1'][0] == 'h': - assert link_dict['node2'][0] == 's', 'Hosts should be connected to switches, not ' + str(link_dict['node2']) - links.append(link_dict) - return links - - - def create_network(self): - """ Create the mininet network object, and store it as self.net. - - Side effects: - - Mininet topology instance stored as self.topo - - Mininet instance stored as self.net - """ - self.logger("Building mininet topology.") - - self.topo = ExerciseTopo(self.hosts, self.switches.keys(), self.links, self.log_dir) - - switchClass = configureP4Switch( - sw_path=self.bmv2_exe, - json_path=self.switch_json, - log_console=True, - pcap_dump=self.pcap_dir) - - self.net = Mininet(topo = self.topo, - link = TCLink, - host = P4Host, - switch = switchClass, - controller = None) - - - def program_switches(self): - """ If any command files were provided for the switches, - this method will start up the CLI on each switch and use the - contents of the command files as input. - - Assumes: - - A mininet instance is stored as self.net and self.net.start() has - been called. - """ - cli = 'simple_switch_CLI' - for sw_name, sw_dict in self.switches.iteritems(): - if 'cli_input' not in sw_dict: continue - # get the port for this particular switch's thrift server - sw_obj = self.net.get(sw_name) - thrift_port = sw_obj.thrift_port - - cli_input_commands = sw_dict['cli_input'] - self.logger('Configuring switch %s with file %s' % (sw_name, cli_input_commands)) - with open(cli_input_commands, 'r') as fin: - cli_outfile = '%s/%s_cli_output.log'%(self.log_dir, sw_name) - with open(cli_outfile, 'w') as fout: - subprocess.Popen([cli, '--thrift-port', str(thrift_port)], - stdin=fin, stdout=fout) - - def program_hosts(self): - """ Adds static ARP entries and default routes to each mininet host. - - Assumes: - - A mininet instance is stored as self.net and self.net.start() has - been called. - """ - for host_name in self.topo.hosts(): - h = self.net.get(host_name) - h_iface = h.intfs.values()[0] - link = h_iface.link - - sw_iface = link.intf1 if link.intf1 != h_iface else link.intf2 - # phony IP to lie to the host about - host_id = int(host_name[1:]) - sw_ip = '10.0.%d.254' % host_id - - # Ensure each host's interface name is unique, or else - # mininet cannot shutdown gracefully - h.defaultIntf().rename('%s-eth0' % host_name) - # static arp entries and default routes - h.cmd('arp -i %s -s %s %s' % (h_iface.name, sw_ip, sw_iface.mac)) - h.cmd('ethtool --offload %s rx off tx off' % h_iface.name) - h.cmd('ip route add %s dev %s' % (sw_ip, h_iface.name)) - h.setDefaultRoute("via %s" % sw_ip) - - - def do_net_cli(self): - """ Starts up the mininet CLI and prints some helpful output. - - Assumes: - - A mininet instance is stored as self.net and self.net.start() has - been called. - """ - for s in self.net.switches: - s.describe() - for h in self.net.hosts: - h.describe() - self.logger("Starting mininet CLI") - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - print('') - print('======================================================================') - print('Welcome to the BMV2 Mininet CLI!') - print('======================================================================') - print('Your P4 program is installed into the BMV2 software switch') - print('and your initial configuration is loaded. You can interact') - print('with the network using the mininet CLI below.') - print('') - if self.switch_json: - print('To inspect or change the switch configuration, connect to') - print('its CLI from your host operating system using this command:') - print(' simple_switch_CLI --thrift-port ') - print('') - print('To view a switch log, run this command from your host OS:') - print(' tail -f %s/.log' % self.log_dir) - print('') - print('To view the switch output pcap, check the pcap files in %s:' % self.pcap_dir) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap') - print('') - - CLI(self.net) - - -def get_args(): - cwd = os.getcwd() - default_logs = os.path.join(cwd, 'logs') - default_pcaps = os.path.join(cwd, 'pcaps') - parser = argparse.ArgumentParser() - parser.add_argument('-q', '--quiet', help='Suppress log messages.', - action='store_true', required=False, default=False) - parser.add_argument('-t', '--topo', help='Path to topology json', - type=str, required=False, default='./topology.json') - parser.add_argument('-l', '--log-dir', type=str, required=False, default=default_logs) - parser.add_argument('-p', '--pcap-dir', type=str, required=False, default=default_pcaps) - parser.add_argument('-j', '--switch_json', type=str, required=False) - parser.add_argument('-b', '--behavioral-exe', help='Path to behavioral executable', - type=str, required=False, default='simple_switch') - return parser.parse_args() - - -if __name__ == '__main__': - # from mininet.log import setLogLevel - # setLogLevel("info") - - args = get_args() - exercise = ExerciseRunner(args.topo, args.log_dir, args.pcap_dir, - args.switch_json, args.behavioral_exe, args.quiet) - - exercise.run_exercise() - diff --git a/P4D2_2018_East/vm/p4-logo.png b/P4D2_2018_East/vm/p4-logo.png deleted file mode 100644 index cc36615ba..000000000 Binary files a/P4D2_2018_East/vm/p4-logo.png and /dev/null differ diff --git a/P4D2_2018_East/vm/p4.vim b/P4D2_2018_East/vm/p4.vim deleted file mode 100644 index a62d81552..000000000 --- a/P4D2_2018_East/vm/p4.vim +++ /dev/null @@ -1,133 +0,0 @@ -" Vim syntax file -" Language: P4_16 -" Maintainer: Antonin Bas, Barefoot Networks Inc -" Latest Revision: 5 August 2014 -" Updated By: Gyanesh Patra, Unicamp University -" Latest Revision: 12 April 2016 -" Updated Again By: Robert MacDavid, Princeton University -" Latest Revision: 12 June 2017 - -if version < 600 - syntax clear -elseif exists("b:current_syntax") - finish -endif - -" Use case sensitive matching of keywords -syn case match - -syn keyword p4ObjectKeyword action apply control default -syn keyword p4ObjectKeyword enum extern exit -syn keyword p4ObjectKeyword header header_union -syn keyword p4ObjectKeyword match_kind -syn keyword p4ObjectKeyword package parser -syn keyword p4ObjectKeyword state struct switch size -syn keyword p4ObjectKeyword table transition tuple typedef -syn keyword p4ObjectKeyword verify - -" Tables -syn keyword p4ObjectAttributeKeyword key actions default_action entries -syn keyword p4ObjectAttributeKeyword implementation -" Counters and meters -syn keyword p4ObjectAttributeKeyword counters meters -" Var Attributes -syn keyword p4ObjectKeyword const in out inout - - -syn keyword p4Annotation @name @tableonly @defaultonly -syn keyword p4Annotation @globalname @atomic @hidden - - -syn keyword p4MatchTypeKeyword exact ternary lpm range - -syn keyword p4TODO contained FIXME TODO -syn match p4Comment '\/\/.*' contains=p4TODO -syn region p4BlockComment start='\/\*' end='\*\/' contains=p4TODO keepend - -syn match p4Preprocessor '#(include|define|undef|if|ifdef) .*$' -syn match p4Preprocessor '#(if|ifdef|ifndef|elif|else) .*$' -syn match p4Preprocessor '#(endif|defined|line|file) .*$' -syn match p4Preprocessor '#(error|warning) .*$' - -syn keyword p4Type bit bool int varbit void error - -" Integer Literals - -syn match p4Int '[0-9][0-9_]*' -syn match p4Indentifier '[A-Za-z_][A-Za-z0-9_]*' -syn match p4HexadecimalInt '0[Xx][0-9a-fA-F]\+' -syn match p4DecimalInt '0[dD][0-9_]\+' -syn match p4OctalInt '0[oO][0-7_]\+' -syn match p4BinaryInt '0[bB][01_]\+' - - -syn region p4SizedType start='(bit|int|varbit)\<' end='\>' -syn match p4UserType '[A-Za-z_][A-Za-z0-9_]*[_][t]\W' -syn keyword p4Operators and or not &&& mask - - -" Header Methods -syn keyword p4Primitive isValid setValid setInvalid -" Table Methods -syn keyword p4Primitive hit action_run -" Packet_in methods -syn keyword p4Primitive extract lookahead advance length -" Packet_out methods -syn keyword p4Primitive emit -" Known parser states -syn keyword p4Primitive accept reject -" Misc -syn keyword p4Primitive NoAction - - -syn keyword p4Conditional if else select -syn keyword p4Statement return - -" Don't Care -syn keyword p4Constant _ -" Error -syn keyword p4Constant NoError PacketTooShort NoMatch StackOutOfBounds -syn keyword p4Constant OverwritingHeader HeaderTooShort ParserTiimeout -" Boolean -syn keyword p4Boolean false true - -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Apply highlight groups to syntax groups defined above -" For version 5.7 and earlier: only when not done already -" For version 5.8 and later: only when an item doesn't have highlighting yet -if version >= 508 || !exists("did_p4_syntax_inits") - if version <= 508 - let did_p4_syntax_inits = 1 - command -nargs=+ HiLink hi link - else - command -nargs=+ HiLink hi def link - endif - - HiLink p4ObjectKeyword Repeat - HiLink p4UserType Type - HiLink p4ObjectAttributeKeyword Keyword - HiLink p4TypeAttribute StorageClass - HiLink p4Annotation Special - HiLink p4MatchTypeKeyword Keyword - HiLink p4TODO Todo - HiLink p4Comment Comment - HiLink p4BlockComment Comment - HiLink p4Preprocessor PreProc - HiLink p4SizedType Type - HiLink p4Type Type - HiLink p4DecimalInt Number - HiLink p4HexadecimalInt Number - HiLink p4OctalInt Number - HiLink p4BinaryInt Number - HiLink p4Int Number - HiLink p4Operators Operator - HiLink p4Primitive Function - HiLink p4Conditional Conditional - HiLink p4Statement Statement - HiLink p4Constant Constant - HiLink p4Boolean Boolean - - delcommand HiLink -endif - -let b:current_syntax = "p4" diff --git a/P4D2_2018_East/vm/p4_16-mode.el b/P4D2_2018_East/vm/p4_16-mode.el deleted file mode 100644 index 7625d907e..000000000 --- a/P4D2_2018_East/vm/p4_16-mode.el +++ /dev/null @@ -1,222 +0,0 @@ -;;; p4_16-mode.el --- Support for the P4_16 programming language - -;; Copyright (C) 2016- Barefoot Networks -;; Author: Vladimir Gurevich -;; Maintainer: Vladimir Gurevich -;; Created: 15 April 2017 -;; Version: 0.2 -;; Keywords: languages p4_16 -;; Homepage: http://p4.org - -;; This file is not part of GNU Emacs. - -;; This file is free software… - -;; This mode has preliminary support for P4_16. It covers the core language, -;; but it is not clear yet, how we can highlight the indentifiers, defined -;; for a particular architecture. Core library definitions are included - -;; Placeholder for user customization code -(defvar p4_16-mode-hook nil) - -;; Define the keymap (for now it is pretty much default) -(defvar p4_16-mode-map - (let ((map (make-keymap))) - (define-key map "\C-j" 'newline-and-indent) - map) - "Keymap for P4_16 major mode") - -;; Syntactic HighLighting - -;; Main keywors (declarations and operators) -(setq p4_16-keywords - '("action" "apply" - "control" - "default" - "else" "enum" "extern" "exit" - "header" "header_union" - "if" - "match_kind" - "package" "parser" - "return" - "select" "state" "struct" "switch" - "table" "transition" "tuple" "typedef" - "verify" - )) - -(setq p4_16-annotations - '("@name" "@metadata" "@alias" - )) - -(setq p4_16-attributes - '("const" "in" "inout" "out" - ;; Tables - "key" "actions" "default_action" "entries" "implementation" - "counters" "meters" - )) - -(setq p4_16-variables - '("packet_in" "packet_out" - )) - -(setq p4_16-operations - '("&&&" ".." "++" "?" ":")) - -(setq p4_16-constants - '( - ;;; Don't care - "_" - ;;; bool - "false" "true" - ;;; error - "NoError" "PacketTooShort" "NoMatch" "StackOutOfBounds" - "OverwritingHeader" "HeaderTooShort" "ParserTiimeout" - ;;; match_kind - "exact" "ternary" "lpm" "range" - ;;; We can add constants for supported architectures here - )) - -(setq p4_16-types - '("bit" "bool" "int" "varbit" "void" "error" - )) - -(setq p4_16-primitives - '( - ;;; Header methods - "isValid" "setValid" "setInvalid" - ;;; Table Methods - "hit" "action_run" - ;;; packet_in methods - "extract" "lookahead" "advance" "length" - ;;; packet_out methods - "emit" - ;;; Known parser states - "accept" "reject" - ;;; misc - "NoAction" - )) - -(setq p4_16-cpp - '("#include" - "#define" "#undef" - "#if" "#ifdef" "#ifndef" - "#elif" "#else" - "#endif" - "defined" - "#line" "#file")) - -(setq p4_16-cppwarn - '("#error" "#warning")) - -;; Optimize the strings -(setq p4_16-keywords-regexp (regexp-opt p4_16-keywords 'words)) -(setq p4_16-annotations-regexp (regexp-opt p4_16-annotations 1)) -(setq p4_16-attributes-regexp (regexp-opt p4_16-attributes 'words)) -(setq p4_16-variables-regexp (regexp-opt p4_16-variables 'words)) -(setq p4_16-operations-regexp (regexp-opt p4_16-operations 'words)) -(setq p4_16-constants-regexp (regexp-opt p4_16-constants 'words)) -(setq p4_16-types-regexp (regexp-opt p4_16-types 'words)) -(setq p4_16-primitives-regexp (regexp-opt p4_16-primitives 'words)) -(setq p4_16-cpp-regexp (regexp-opt p4_16-cpp 1)) -(setq p4_16-cppwarn-regexp (regexp-opt p4_16-cppwarn 1)) - - -;; create the list for font-lock. -;; each category of keyword is given a particular face -(defconst p4_16-font-lock-keywords - (list - (cons p4_16-cpp-regexp font-lock-preprocessor-face) - (cons p4_16-cppwarn-regexp font-lock-warning-face) - (cons p4_16-types-regexp font-lock-type-face) - (cons p4_16-constants-regexp font-lock-constant-face) - (cons p4_16-attributes-regexp font-lock-builtin-face) - (cons p4_16-variables-regexp font-lock-variable-name-face) - ;;; This is a special case to distinguish the method from the keyword - (cons "\\.apply" font-lock-function-name-face) - (cons p4_16-primitives-regexp font-lock-function-name-face) - (cons p4_16-operations-regexp font-lock-builtin-face) - (cons p4_16-keywords-regexp font-lock-keyword-face) - (cons p4_16-annotations-regexp font-lock-keyword-face) - (cons "\\(\\w*_t +\\)" font-lock-type-face) - (cons "[^A-Z_][A-Z] " font-lock-type-face) ;; Total hack for templates - (cons "<[A-Z, ]*>" font-lock-type-face) - (cons "\\(<[^>]+>\\)" font-lock-string-face) - (cons "\\([^_A-Za-z]\\([0-9]+w\\)?0x[0-9A-Fa-f]+\\)" font-lock-constant-face) - (cons "\\([^_A-Za-z]\\([0-9]+w\\)?0b[01]+\\)" font-lock-constant-face) - (cons "\\([^_A-Za-z][+-]?\\([0-9]+w\\)?[0-9]+\\)" font-lock-constant-face) - ;;(cons "\\(\\w*\\)" font-lock-variable-name-face) - ) - "Default Highlighting Expressions for P4_16") - -(defvar p4_16-mode-syntax-table - (let ((st (make-syntax-table))) - (modify-syntax-entry ?_ "w" st) - (modify-syntax-entry ?/ ". 124b" st) - (modify-syntax-entry ?* ". 23" st) - (modify-syntax-entry ?\n "> b" st) - st) - "Syntax table for p4_16-mode") - -;;; Indentation -(defvar p4_16-indent-offset 4 - "Indentation offset for `p4_16-mode'.") - -(defun p4_16-indent-line () - "Indent current line for any balanced-paren-mode'." - (interactive) - (let ((indent-col 0) - (indentation-increasers "[{(]") - (indentation-decreasers "[})]") - ) - (save-excursion - (beginning-of-line) - (condition-case nil - (while t - (backward-up-list 1) - (when (looking-at indentation-increasers) - (setq indent-col (+ indent-col p4_16-indent-offset)))) - (error nil))) - (save-excursion - (back-to-indentation) - (when (and (looking-at indentation-decreasers) - (>= indent-col p4_16-indent-offset)) - (setq indent-col (- indent-col p4_16-indent-offset)))) - (indent-line-to indent-col))) - -;;; Imenu support -(require 'imenu) -(setq p4_16-imenu-generic-expression - '( - ("Controls" "^ *control +\\([A-Za-z0-9_]*\\)" 1) - ("Externs" "^ *extern +\\([A-Za-z0-9_]*\\) *\\([A-Za-z0-9_]*\\)" 2) - ("Tables" "^ *table +\\([A-Za-z0-9_]*\\)" 1) - ("Actions" "^ *action +\\([A-Za-z0-9_]*\\)" 1) - ("Parsers" "^ *parser +\\([A-Za-z0-9_]*\\)" 1) - ("Parser States" "^ *state +\\([A-Za-z0-9_]*\\)" 1) - ("Headers" "^ *header +\\([A-Za-z0-9_]*\\)" 1) - ("Header Unions" "^ *header_union +\\([A-Za-z0-9_]*\\)" 1) - ("Structs" "^ *struct +\\([A-Za-z0-9_]*\\)" 1) - )) - -;;; Cscope Support -(require 'xcscope) - -;; Put everything together -(defun p4_16-mode () - "Major mode for editing P4_16 programs" - (interactive) - (kill-all-local-variables) - (set-syntax-table p4_16-mode-syntax-table) - (use-local-map p4_16-mode-map) - (set (make-local-variable 'font-lock-defaults) '(p4_16-font-lock-keywords)) - (set (make-local-variable 'indent-line-function) 'p4_16-indent-line) - (setq major-mode 'p4_16-mode) - (setq mode-name "P4_16") - (setq imenu-generic-expression p4_16-imenu-generic-expression) - (imenu-add-to-menubar "P4_16") - (cscope-minor-mode) - (run-hooks 'p4_16-mode-hook) -) - -;; The most important line -(provide 'p4_16-mode) diff --git a/P4D2_2017_Fall/P4_tutorial_labs.pdf b/P4_tutorial_labs.pdf similarity index 100% rename from P4D2_2017_Fall/P4_tutorial_labs.pdf rename to P4_tutorial_labs.pdf diff --git a/README.md b/README.md index 7fae846cd..883b602f9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,47 @@ -# tutorials -P4 language tutorials - -**Make sure that you edit [env.sh](env.sh) to point to your local copy of - [bmv2](https://github.com/p4lang/behavioral-model) and - [p4c-bm](https://github.com/p4lang/p4c-bm). You may want to follow the - instructions - [here](https://github.com/p4lang/tutorials/tree/master/SIGCOMM_2015#obtaining-required-software) - to make sure that your environment is setup correctly.** +# P4 Tutorial + +## Introduction + +Welcome to the P4 Tutorial! We've prepared a set of exercises to help +you get started with P4 programming, organized into four modules: + +1. Introduction and Language Basics +* [Basic Forwarding](./exercises/basic) +* [Basic Tunneling](./exercises/basic_tunnel) + +2. P4 Runtime and the Control Plane +* [P4 Runtime](./exercises/p4runtime) + +3. Monitoring and Debugging +* [Explicit Congestion Notification](./exercises/ecn) +* [Multi-Hop Route Inspection](./exercises/mri) + +4. Advanced Data Structures +* [Source Routing](./exercises/source_routing) +* [Calculator](./exercises/calc) + +5. Dynamic Behavior +* [Load Balancing](./exercises/load_balance) + +## Obtaining required software + +If you are starting this tutorial at the Fall 2017 P4 Developer Day, +then we've already provided you with a virtual machine that has all of +the required software installed. + +Otherwise, to complete the exercises, you will need to either build a +virtual machine or install several dependencies. + +To build the virtual machine: +- Install [Vagrant](https://vagrantup.com) and [VirtualBox](https://virtualbox.org) +- `cd vm` +- `vagrant up` +- Log in with username `p4` and password `p4` and issue the command `sudo shutdown -r now` +- When the machine reboots, you should have a graphical desktop machine with the required +software pre-installed. + +To install dependencies by hand, please reference the [vm](../vm) installation scripts. +They contain the dependencies, versions, and installation procedure. +You can run them directly on an Ubuntu 16.04 machine: +- `sudo ./root-bootstrap.sh` +- `sudo ./user-bootstrap.sh` diff --git a/SIGCOMM_2015/README.md b/SIGCOMM_2015/README.md deleted file mode 100644 index 74d32d331..000000000 --- a/SIGCOMM_2015/README.md +++ /dev/null @@ -1,316 +0,0 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [P4 SIGCOMM 2015 Tutorial](#p4-sigcomm-2015-tutorial) - - [Introduction](#introduction) - - [Obtaining required software](#obtaining-required-software) - - [Before starting the exercises](#before-starting-the-exercises) - - [Exercise 1: Source Routing](#exercise-1-source-routing) - - [Description of the EasyRoute protocol](#description-of-the-easyroute-protocol) - - [A few hints](#a-few-hints) - - [Populating the tables](#populating-the-tables) - - [Testing your code](#testing-your-code) - - [Debugging your code](#debugging-your-code) - - [Exercise 2: Implementing TCP flowlet switching](#exercise-2-implementing-tcp-flowlet-switching) - - [What is flowlet switching?](#what-is-flowlet-switching) - - [Running the starter code](#running-the-starter-code) - - [What you need to do](#what-you-need-to-do) - - - -# P4 SIGCOMM 2015 Tutorial - -The original webpage for the tutorial can be found -[here](http://conferences.sigcomm.org/sigcomm/2015/tutorial-p4.php) - -## Introduction - -This repository include 2 exercises we presented at SIGCOMM: *Source Routing* -and *Flowlet Switching*. Both exercises assume that you possess basic networking -knowledge and some familiarity with the P4 language. Please take a look at the -[P4 language spec](http://p4.org/specs/) and at the example `simple_router` -target [on -p4lang](https://github.com/p4lang/p4factory/tree/master/targets/simple_router/p4src). -*Source Routing* asks you to write a P4 program from scratch to implement a -custom source routing protocol. *Flowlet Switching* is more difficult: you will -start from a simple P4 routing program (with ECMP) and implement a version of -flowlet switching, which yields better load balancing for bursty TCP flows. - -For both exercises, you will find a .tar.gz archive which contains the solution -files. - -## Obtaining required software - -To complete the exercises, you will need to clone 2 p4lang Github repositories -and install their dependencies. To clone 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` - -You will also need to install `mininet`, as well as the following Python -packages: `scapy`, `thrift` (>= 0.9.2) and `networkx`. On Ubuntu, it would look -like this: -- `sudo apt-get install mininet` -- `sudo pip install scapy thrift networkx` - -## 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 the root directory of this -repository. 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. - -That's all :) - -## Exercise 1: Source Routing - -Place yourself in the `source_routing` directory. - -In this problem, we will implement a very simple source routing protocol in -P4. We will call this protocol EasyRoute. You will be designing the P4 program -from scratch, although you are of course welcome to reuse code from other -targets in p4lang. To test your implementation, you will create a Mininet -network and send messages between hosts. We provide a skeleton program: -[source_routing/p4src/source_routing.p4](source_routing/p4src/source_routing.p4), -you need to implement the parser and the ingress control flow. - -### Description of the EasyRoute protocol - -The EasyRoute packets looks like this: - -``` -preamble (8 bytes) | num_valid (4 bytes) | port_1 (1 byte) | port_2 (1 byte) | -... | port_n (1 byte) | payload -``` - -The preamble is always set to 0. You can use this to distinguish the EasyRoute -packets from other packets (Ethernet frames) your switch may receive. We do not -guarantee that your P4 switch will exclusively receive EasyRoute packets. - -The num_valid field indicates the number of valid ports in the header. If your -EasyRoute packet is to traverse 3 switches, num_valid will initially be set to -3, and the port list will be 3 byte long. When a switch receives an EasyRoute -packet, the first port of the list is used to determine the outgoing port for -the packet. num_valid is then decremented by 1 and the first port is removed -from the list. - -We will use the EasyRoute protocol to send text messages. The payload will -therefore correspond to the text message we are sending. You do not have to -worry about the encoding of the text message. - -![Source Routing topology](resources/images/source_routing_topology.png) - -If I wish to send message "Hello" from h1 to h3, the EasyRoute packet will look -like this: - -- when it leaves h1: -`00000000 00000000 | 00000002 | 03 | 01 | Hello` - -- when it leaves sw1: -`00000000 00000000 | 00000001 | 01 | Hello` - -- when it leaves sw3: -`00000000 00000000 | 00000000 | Hello` - -Note that the last switch should not remove the EasyRoute header; otherwise the -application running in the end hosts won’t be able to handle incoming packets -properly. - -Your P4 implementation needs to adhere to the following requirements: - -1. **all non-EasyRoute packets should be dropped** -2. **if a switch receives an EasyRoute packet for which num_valid is 0, the -packet should be dropped** - -### A few hints - -1. in the start parse state, you can use `current()` to check if the packet is -an EasyRoute packet. A call to `current(0, 64)` will examine the first 64 bits -of the packet, **without shifting the packet pointer**. -2. do not forget that a table can match on the validity of a header. Furthermore -if a header is not valid, our software switch will set all its fields to 0. -3. a table can "match" on an empty key, which means the default action will -always be executed - if configured correctly by the runtime. Just omit the -"reads" attribute to achieve this. -4. you can remove a header with a call to `remove_header()` -5. when parsing the EasyRoute header, you do not have to parse the whole port -list. Actually P4 is currently missing language constructs needed to parse a -general Type-Length-Value style header[1](#myfootnote1), and hence -you’ll need to simply extract the first port of the list and ignore the rest -(including the payload). Also preamble, num_valid and the port number don't have -to all be placed in the same header type. -6. finally, we advise you to put all your logic in the ingress control flow and -leave the egress empty. You will not need more than 1 or 2 tables to implement -EasyRoute. - -1: Members of [P4.org](http://p4.org) are working -together to come up with language constructs needed to be able to parse -TLV-style headers soon. - -### Populating the tables - -Once your P4 code is ready (you can validate it easily by running `p4-validate` -on it), you need to think about populating the tables. We made it easy for you: -you just have to fill the commands.txt file with `bmv2` CLI commands. We think -that you only need to know 2 commands: - -- `table_set_default [action_data]`: this is used to -set the default action of a given table -- `table_add => [action_data]`: this -is used to add an entry to a table - -You can look at example commands in the `flowlet_switching` directory: -[flowlet_switching/commands.txt](flowlet_switching/commands.txt) and match them -with the corresponding P4 tables -[flowlet_switching/p4src/simple_router.p4](flowlet_switching/p4src/simple_router.p4). - -### Testing your code - -./run_demo.sh will compile your code and create the Mininet network described -above. It will also use commands.txt to configure each one of the switches. -Once the network is up and running, you should type the following in the Mininet -CLI: - -- `xterm h1` -- `xterm h3` - -This will open a terminal for you on h1 and h3. - -On h3 run: `./receive.py`. - -On h1 run: `./send.py h1 h3`. - -You should then be able to type messages on h1 and receive them on h3. The -`send.py` program finds the shortest path between h1 and h3 using Dijkstra, then -send correctly-formatted packets to h3 through s1 and s3. - -### Debugging your code - -.pcap files will be generated for every interface (9 files: 3 for each of the 3 -switches). You can look at the appropriate files and check that your packets are -being processed correctly. - -## Exercise 2: Implementing TCP flowlet switching - -Place yourself in the `flowlet_switching` directory and run -`sudo ./veth_setup.sh`. - -### What is flowlet switching? - -Flowlet switching leverages the burstiness of TCP flows to achieve better load -balancing of TCP traffic. In this exercise, you will start from a program -that load-balances based on layer 4 flows: this is generally considered -"classic" ECMP. To do this, we compute a hash over the 5-tuple and use this -value to choose from a set of possible next hops. This means that all packets -belonging to the same flow (i.e. with the same 5-tuple) will be routed to -the same nexthop. You need to enhance this P4 code with additional logic to -implement flowlet switching. - -We suggest implementing flowlet switching as follows: - -1. Compute a crc16 hash over the regular TCP 5-tuple, using the -`modify_field_with_hash_based_offset()` P4 primitive. We already use this -primitive in the ECMP starter code, so take a look. This hash will identify each -TCP flow (note: we do not care about collisions in this case). - -2. For each flow, you need to store 2 things: a) a timestamp for the last -observed packet belonging to this flow and b) a flowlet_id. Flowlet switching -is very simple: for each packet which belongs to the flow, you need to update -the timestamp. Then, if the time delta between the last observed packet and the -current packet exceeds a certain timeout value (in our case, we suggest using -50ms), then the flowlet_id needs to be incremented. Note that in data centers -with mostly short, high-speed links, this timeout value will typically be much -smaller. With flowlet switching, packets belonging to the same TCP burst will -have the same flowlet_id, but packets in 2 different bursts (i.e. separated by a -timeout) will have a different flowlet_id. This also implies that we must -maintain some state for each TCP flow. To maintain state in P4, you will need to -use 'register' objects (look them up in the spec). In this case, you will need -to use two separate registers for each packet (one for the timestamp and one for -the flowlet_id). The software switch will generate a timestamp for each new -packet and store it in the metadata field -`intrinsic_metadata.ingress_global_timestamp`. This is a 32 bit value, expressed -in microseconds. You can read it in the ingress pipeline, but don't try to write -to it. - -3. Once you have obtained the flowlet_id, you can compute a new hash. This -time, the hash will include the 5-tuple AND the flowlet_id. You will use this -hash exactly like we used our hash in the starter code, as an offset into a -nexthop table. This part of the exercise actually mostly reuses the starter -code. Your changes to tables `ecmp_group` and `ecmp_nhop` should be minimal. - -### Running the starter code - -To compile and run the starter code, simply use `./run_demo.sh`. This time we -will not be using Mininet, we will instead generate simple TCP test packets and -send them individually to the switch to observe how it behaves. `run_demo.sh` -will start the switch and populate the tables using the CLI commands from -[flowlet_switching/commands.txt](flowlet_switching/commands.txt). - -When the switch is running, you can send test packets with `sudo -./run_test.py`. Note that this script will take a few seconds to complete. The -tests sends a few hundred identical TCP packets through the switch, in bursts, -on port 3. If you take a look at commands.txt, you will see that each TCP packet -can either go out of port 1 or port 2, based on the result of the hash -computation. The script prints the list of outgoing ports. Since all packets are -identical and we are using "regular" ECMP, all the packets should come out of -the same port and you will see either a thousand "1"s or a thousand "2"s when -you run the test. If you were to alter the test script (example: modify the TTL -value of the input TCP packets), the output should randomly choose between port -1 and port 2. - -Note that the test script (and commands.txt) assume the following topology: - -``` - --------------------------------- nhop-0 10.0.1.1 - | 00:04:00:00:00:00 - 1 - 00:aa:bb:00:00:00 - | --------- 3--sw - | - 2 - 00:aa:bb:00:00:01 - | - --------------------------------- nhop-1 10.0.2.1 - 00:04:00:00:00:01 -``` - -Both `nhop-0` and `nhop-1` have a path to `10.0.0.1`, which is the final -destination of our test packet. - -### What you need to do - -1. Update the provided [P4 program](flowlet_switching/p4src/simple_router.p4) to -perform TCP flowlet switching. In our case, it requires adding 2 tables to the -ingress pipeline. Remember that you can omit the 'reads' attribute for a table. -In this case, providing you configure the default action of the table correctly, -the default action will always be performed. - -2. Update [commands.txt](flowlet_switching/commands.txt) to configure your new -tables. - -3. Run the above test again. Observe how the list of ports alternate between 1 -and 2. You will need to edit the test script if you chose not to use a 50ms -(50,000 microseconds!) timeout for the flowlet_id. diff --git a/SIGCOMM_2015/flowlet_switching/commands.txt b/SIGCOMM_2015/flowlet_switching/commands.txt deleted file mode 100644 index 7c0e3ae1f..000000000 --- a/SIGCOMM_2015/flowlet_switching/commands.txt +++ /dev/null @@ -1,11 +0,0 @@ -table_set_default ecmp_group _drop -table_set_default ecmp_nhop _drop -table_set_default forward _drop -table_set_default send_frame _drop -table_add ecmp_group set_ecmp_select 10.0.0.1/32 => 0 2 -table_add ecmp_nhop set_nhop 0 => 10.0.1.1 1 -table_add ecmp_nhop set_nhop 1 => 10.0.2.1 2 -table_add forward set_dmac 10.0.1.1 => 00:04:00:00:00:00 -table_add forward set_dmac 10.0.2.1 => 00:04:00:00:00:01 -table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00 -table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:01 diff --git a/SIGCOMM_2015/flowlet_switching/p4src/includes/headers.p4 b/SIGCOMM_2015/flowlet_switching/p4src/includes/headers.p4 deleted file mode 100644 index b9b0725fd..000000000 --- a/SIGCOMM_2015/flowlet_switching/p4src/includes/headers.p4 +++ /dev/null @@ -1,56 +0,0 @@ -/* -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_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; - } -} - -header_type tcp_t { - fields { - srcPort : 16; - dstPort : 16; - seqNo : 32; - ackNo : 32; - dataOffset : 4; - res : 3; - ecn : 3; - ctrl : 6; - window : 16; - checksum : 16; - urgentPtr : 16; - } -} diff --git a/SIGCOMM_2015/flowlet_switching/p4src/includes/intrinsic.p4 b/SIGCOMM_2015/flowlet_switching/p4src/includes/intrinsic.p4 deleted file mode 100644 index 1785cd098..000000000 --- a/SIGCOMM_2015/flowlet_switching/p4src/includes/intrinsic.p4 +++ /dev/null @@ -1,26 +0,0 @@ -/* -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 intrinsic_metadata_t { - fields { - ingress_global_timestamp : 48; - lf_field_list : 32; - mcast_grp : 16; - egress_rid : 16; - } -} - -metadata intrinsic_metadata_t intrinsic_metadata; diff --git a/SIGCOMM_2015/flowlet_switching/p4src/includes/parser.p4 b/SIGCOMM_2015/flowlet_switching/p4src/includes/parser.p4 deleted file mode 100644 index 6d927081c..000000000 --- a/SIGCOMM_2015/flowlet_switching/p4src/includes/parser.p4 +++ /dev/null @@ -1,77 +0,0 @@ -/* -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. -*/ - -parser start { - return parse_ethernet; -} - -#define ETHERTYPE_IPV4 0x0800 - -header ethernet_t ethernet; - -parser parse_ethernet { - extract(ethernet); - return select(latest.etherType) { - ETHERTYPE_IPV4 : parse_ipv4; - default: ingress; - } -} - -header ipv4_t ipv4; - -field_list ipv4_checksum_list { - ipv4.version; - ipv4.ihl; - ipv4.diffserv; - ipv4.totalLen; - ipv4.identification; - ipv4.flags; - ipv4.fragOffset; - ipv4.ttl; - ipv4.protocol; - ipv4.srcAddr; - ipv4.dstAddr; -} - -field_list_calculation ipv4_checksum { - input { - ipv4_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field ipv4.hdrChecksum { - verify ipv4_checksum; - update ipv4_checksum; -} - -#define IP_PROTOCOLS_TCP 6 - -parser parse_ipv4 { - extract(ipv4); - return select(latest.protocol) { - IP_PROTOCOLS_TCP : parse_tcp; - default: ingress; - } -} - -header tcp_t tcp; - -parser parse_tcp { - extract(tcp); - return ingress; -} diff --git a/SIGCOMM_2015/flowlet_switching/p4src/simple_router.p4 b/SIGCOMM_2015/flowlet_switching/p4src/simple_router.p4 deleted file mode 100644 index 2df930c7f..000000000 --- a/SIGCOMM_2015/flowlet_switching/p4src/simple_router.p4 +++ /dev/null @@ -1,132 +0,0 @@ -/* -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. -*/ - -#include "includes/headers.p4" -#include "includes/parser.p4" -#include "includes/intrinsic.p4" - -header_type ingress_metadata_t { - fields { - ecmp_offset : 14; // offset into the ecmp table - - nhop_ipv4 : 32; - - // TODO: add your flowlet metadata here - } -} - -metadata ingress_metadata_t ingress_metadata; - -action _drop() { - drop(); -} - -action set_nhop(nhop_ipv4, port) { - modify_field(ingress_metadata.nhop_ipv4, nhop_ipv4); - modify_field(standard_metadata.egress_spec, port); - add_to_field(ipv4.ttl, -1); -} - -#define ECMP_BIT_WIDTH 10 -#define ECMP_GROUP_TABLE_SIZE 1024 -#define ECMP_NHOP_TABLE_SIZE 16384 - -// TODO: add flowlet id to hash fields - -field_list l3_hash_fields { - ipv4.srcAddr; - ipv4.dstAddr; - ipv4.protocol; - tcp.srcPort; - tcp.dstPort; -} - -field_list_calculation ecmp_hash { - input { - l3_hash_fields; - } - algorithm : crc16; - output_width : ECMP_BIT_WIDTH; -} - - -action set_ecmp_select(ecmp_base, ecmp_count) { - modify_field_with_hash_based_offset(ingress_metadata.ecmp_offset, ecmp_base, - ecmp_hash, ecmp_count); -} - -table ecmp_group { - reads { - ipv4.dstAddr : lpm; - } - actions { - _drop; - set_ecmp_select; - } - size : ECMP_GROUP_TABLE_SIZE; -} - -table ecmp_nhop { - reads { - ingress_metadata.ecmp_offset : exact; - } - actions { - _drop; - set_nhop; - } - size : ECMP_NHOP_TABLE_SIZE; -} - -action set_dmac(dmac) { - modify_field(ethernet.dstAddr, dmac); -} - -table forward { - reads { - ingress_metadata.nhop_ipv4 : exact; - } - actions { - set_dmac; - _drop; - } - size: 512; -} - -action rewrite_mac(smac) { - modify_field(ethernet.srcAddr, smac); -} - -table send_frame { - reads { - standard_metadata.egress_port: exact; - } - actions { - rewrite_mac; - _drop; - } - size: 256; -} - -control ingress { - // TODO: flowlet switching - apply(ecmp_group); - apply(ecmp_nhop); - apply(forward); -} - -control egress { - apply(send_frame); -} diff --git a/SIGCOMM_2015/flowlet_switching/run_demo.sh b/SIGCOMM_2015/flowlet_switching/run_demo.sh deleted file mode 100755 index d386500d6..000000000 --- a/SIGCOMM_2015/flowlet_switching/run_demo.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/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/tools/runtime_CLI.py - -# 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/simple_router.p4 --json simple_router.json -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH simple_router.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 --json simple_router.json < commands.txt -echo "READY!!!" -fg diff --git a/SIGCOMM_2015/flowlet_switching/run_test.py b/SIGCOMM_2015/flowlet_switching/run_test.py deleted file mode 100755 index 5e09682c0..000000000 --- a/SIGCOMM_2015/flowlet_switching/run_test.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python - -# 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. - -import time - -NUM_PACKETS = 500 - -import random - -import threading -from scapy.all import sniff -from scapy.all import Ether, IP, IPv6 -from scapy.all import sendp - -class PacketQueue: - def __init__(self): - self.pkts = [] - self.lock = threading.Lock() - self.ifaces = set() - - def add_iface(self, iface): - self.ifaces.add(iface) - - def get(self): - self.lock.acquire() - if not self.pkts: - self.lock.release() - return None, None - pkt = self.pkts.pop(0) - self.lock.release() - return pkt - - def add(self, iface, pkt): - if iface not in self.ifaces: - return - self.lock.acquire() - self.pkts.append( (iface, pkt) ) - self.lock.release() - -queue = PacketQueue() - -def pkt_handler(pkt, iface): - if IPv6 in pkt: - return - queue.add(iface, pkt) - -class SnifferThread(threading.Thread): - def __init__(self, iface, handler = pkt_handler): - threading.Thread.__init__(self) - self.iface = iface - self.handler = handler - - def run(self): - sniff( - iface = self.iface, - prn = lambda x: self.handler(x, self.iface) - ) - -class PacketDelay: - def __init__(self, bsize, bdelay, imin, imax, num_pkts = 100): - self.bsize = bsize - self.bdelay = bdelay - self.imin = imin - self.imax = imax - self.num_pkts = num_pkts - self.current = 1 - - def __iter__(self): - return self - - def next(self): - if self.num_pkts <= 0: - raise StopIteration - self.num_pkts -= 1 - if self.current == self.bsize: - self.current = 1 - return random.randint(self.imin, self.imax) - else: - self.current += 1 - return self.bdelay - - -pkt = Ether()/IP(dst='10.0.0.1', ttl=64) - -port_map = { - 1: "veth3", - 2: "veth5", - 3: "veth7" -} - -iface_map = {} -for p, i in port_map.items(): - iface_map[i] = p - -queue.add_iface("veth3") -queue.add_iface("veth5") - -for p, iface in port_map.items(): - t = SnifferThread(iface) - t.daemon = True - t.start() - -import socket - -send_socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, - socket.htons(0x03)) -send_socket.bind((port_map[3], 0)) - -delays = PacketDelay(10, 5, 25, 100, NUM_PACKETS) -ports = [] -print "Sending", NUM_PACKETS, "packets ..." -for d in delays: - # sendp is too slow... - # sendp(pkt, iface=port_map[3], verbose=0) - send_socket.send(str(pkt)) - time.sleep(d / 1000.) -time.sleep(1) -iface, pkt = queue.get() -while pkt: - ports.append(iface_map[iface]) - iface, pkt = queue.get() -print ports diff --git a/SIGCOMM_2015/flowlet_switching/solution.tar.gz b/SIGCOMM_2015/flowlet_switching/solution.tar.gz deleted file mode 100644 index af5ee7d92..000000000 Binary files a/SIGCOMM_2015/flowlet_switching/solution.tar.gz and /dev/null differ diff --git a/SIGCOMM_2015/flowlet_switching/veth_setup.sh b/SIGCOMM_2015/flowlet_switching/veth_setup.sh deleted file mode 100755 index 40835412c..000000000 --- a/SIGCOMM_2015/flowlet_switching/veth_setup.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/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 diff --git a/SIGCOMM_2015/resources/images/source_routing_topology.png b/SIGCOMM_2015/resources/images/source_routing_topology.png deleted file mode 100644 index cc71fca79..000000000 Binary files a/SIGCOMM_2015/resources/images/source_routing_topology.png and /dev/null differ diff --git a/SIGCOMM_2015/source_routing/cleanup b/SIGCOMM_2015/source_routing/cleanup deleted file mode 100755 index a155edffd..000000000 --- a/SIGCOMM_2015/source_routing/cleanup +++ /dev/null @@ -1 +0,0 @@ -sudo killall lt-simple_switch diff --git a/SIGCOMM_2015/source_routing/commands.txt b/SIGCOMM_2015/source_routing/commands.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/SIGCOMM_2015/source_routing/p4src/source_routing.p4 b/SIGCOMM_2015/source_routing/p4src/source_routing.p4 deleted file mode 100644 index 804f83135..000000000 --- a/SIGCOMM_2015/source_routing/p4src/source_routing.p4 +++ /dev/null @@ -1,41 +0,0 @@ -/* -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. -*/ - -// TODO: define headers & header instances - -parser start { - // TODO - return ingress; -} - -// TODO: define parser states - -action _drop() { - drop(); -} - -action route() { - modify_field(standard_metadata.egress_spec, /* TODO: port field from your header */); - // TODO: update your header -} - -control ingress { - // TODO -} - -control egress { - // leave empty -} diff --git a/SIGCOMM_2015/source_routing/receive.py b/SIGCOMM_2015/source_routing/receive.py deleted file mode 100755 index 40fefac4d..000000000 --- a/SIGCOMM_2015/source_routing/receive.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python - -# 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. - -from scapy.all import sniff, sendp -from scapy.all import Packet -from scapy.all import ShortField, IntField, LongField, BitField - -import sys -import struct - -def handle_pkt(pkt): - pkt = str(pkt) - if len(pkt) < 12: return - preamble = pkt[:8] - preamble_exp = "\x00" * 8 - if preamble != preamble_exp: return - num_valid = struct.unpack("/dev/null 2>&1 -sudo PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \ - --behavioral-exe $SWITCH_PATH \ - --json source_routing.json \ - --cli $CLI_PATH diff --git a/SIGCOMM_2015/source_routing/send.py b/SIGCOMM_2015/source_routing/send.py deleted file mode 100755 index e1a250350..000000000 --- a/SIGCOMM_2015/source_routing/send.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/python - -# 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. - -from scapy.all import sniff, sendp -from scapy.all import Packet -from scapy.all import ShortField, IntField, LongField, BitField - -import networkx as nx - -import sys - -class SrcRoute(Packet): - name = "SrcRoute" - fields_desc = [ - LongField("preamble", 0), - IntField("num_valid", 0) - ] - -def read_topo(): - nb_hosts = 0 - nb_switches = 0 - links = [] - with open("topo.txt", "r") as f: - line = f.readline()[:-1] - w, nb_switches = line.split() - assert(w == "switches") - line = f.readline()[:-1] - w, nb_hosts = line.split() - assert(w == "hosts") - for line in f: - if not f: break - a, b = line.split() - links.append( (a, b) ) - return int(nb_hosts), int(nb_switches), links - -def main(): - if len(sys.argv) != 3: - print "Usage: send.py [this_host] [target_host]" - print "For example: send.py h1 h2" - sys.exit(1) - - src, dst = sys.argv[1:] - - nb_hosts, nb_switches, links = read_topo() - - port_map = {} - - for a, b in links: - if a not in port_map: - port_map[a] = {} - if b not in port_map: - port_map[b] = {} - - assert(b not in port_map[a]) - assert(a not in port_map[b]) - port_map[a][b] = len(port_map[a]) + 1 - port_map[b][a] = len(port_map[b]) + 1 - - - G = nx.Graph() - for a, b in links: - G.add_edge(a, b) - - shortest_paths = nx.shortest_path(G) - shortest_path = shortest_paths[src][dst] - - print "path is:", shortest_path - - port_list = [] - first = shortest_path[1] - for h in shortest_path[2:]: - port_list.append(port_map[first][h]) - first = h - - print "port list is:", port_list - - port_str = "" - for p in port_list: - port_str += chr(p) - - while(1): - msg = raw_input("What do you want to send: ") - - # finding the route - first = None - - p = SrcRoute(num_valid = len(port_list)) / port_str / msg - print p.show() - sendp(p, iface = "eth0") - # print msg - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2015/source_routing/solution.tar.gz b/SIGCOMM_2015/source_routing/solution.tar.gz deleted file mode 100644 index dd775a119..000000000 Binary files a/SIGCOMM_2015/source_routing/solution.tar.gz and /dev/null differ diff --git a/SIGCOMM_2015/source_routing/topo.py b/SIGCOMM_2015/source_routing/topo.py deleted file mode 100644 index abfa57c9d..000000000 --- a/SIGCOMM_2015/source_routing/topo.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/python - -# 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. - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.log import setLogLevel, info -from mininet.cli import CLI -from mininet.link import TCLink - -from p4_mininet import P4Switch, P4Host - -import argparse -from time import sleep -import os -import subprocess - -_THIS_DIR = os.path.dirname(os.path.realpath(__file__)) -_THRIFT_BASE_PORT = 22222 - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--cli', help='Path to BM CLI', - type=str, action="store", required=True) - -args = parser.parse_args() - -class MyTopo(Topo): - def __init__(self, sw_path, json_path, nb_hosts, nb_switches, links, **opts): - # Initialize topology and default options - Topo.__init__(self, **opts) - - for i in xrange(nb_switches): - switch = self.addSwitch('s%d' % (i + 1), - sw_path = sw_path, - json_path = json_path, - thrift_port = _THRIFT_BASE_PORT + i, - pcap_dump = True, - device_id = i) - - for h in xrange(nb_hosts): - host = self.addHost('h%d' % (h + 1)) - - for a, b in links: - self.addLink(a, b) - -def read_topo(): - nb_hosts = 0 - nb_switches = 0 - links = [] - with open("topo.txt", "r") as f: - line = f.readline()[:-1] - w, nb_switches = line.split() - assert(w == "switches") - line = f.readline()[:-1] - w, nb_hosts = line.split() - assert(w == "hosts") - for line in f: - if not f: break - a, b = line.split() - links.append( (a, b) ) - return int(nb_hosts), int(nb_switches), links - - -def main(): - nb_hosts, nb_switches, links = read_topo() - - topo = MyTopo(args.behavioral_exe, - args.json, - nb_hosts, nb_switches, links) - - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None ) - net.start() - - for n in xrange(nb_hosts): - h = net.get('h%d' % (n + 1)) - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload eth0 %s off" % off - print cmd - h.cmd(cmd) - print "disable ipv6" - h.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - h.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - h.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - h.cmd("sysctl -w net.ipv4.tcp_congestion_control=reno") - h.cmd("iptables -I OUTPUT -p icmp --icmp-type destination-unreachable -j DROP") - - sleep(1) - - for i in xrange(nb_switches): - cmd = [args.cli, "--json", args.json, - "--thrift-port", str(_THRIFT_BASE_PORT + i)] - with open("commands.txt", "r") as f: - print " ".join(cmd) - try: - output = subprocess.check_output(cmd, stdin = f) - print output - except subprocess.CalledProcessError as e: - print e - print e.output - - sleep(1) - - print "Ready !" - - CLI( net ) - net.stop() - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/SIGCOMM_2015/source_routing/topo.txt b/SIGCOMM_2015/source_routing/topo.txt deleted file mode 100644 index 08f8387b1..000000000 --- a/SIGCOMM_2015/source_routing/topo.txt +++ /dev/null @@ -1,8 +0,0 @@ -switches 3 -hosts 3 -h1 s1 -h2 s2 -h3 s3 -s1 s2 -s1 s3 -s2 s3 diff --git a/SIGCOMM_2016/heavy_hitter/README.md b/SIGCOMM_2016/heavy_hitter/README.md deleted file mode 100644 index e2ab3e585..000000000 --- a/SIGCOMM_2016/heavy_hitter/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Implementing Heavy Hitter Dectection - -## Introduction - -The objective of this tutorial is to detect the heavy hitters on the network. -Heavy hitters can simply be defined as the traffic sources who send unusually large traffic. This can be categorized solely by source IP address or can be classified to each application, or application session that sends the traffic. -There are various ways to detect and determine the host of the heavy hitter and we will explore two different methods using P4 in this tutorial. - -First method is to use the counters in P4. Counters allow you to keep a measurement of every table match value that occurs in the data plane. -Thus, for every entry in the table, P4 counter keeps a cell to store the counted value in. - -The main advantage of using a counter is that it is fairly easy to use and it can give an accurate count of the table entry that we want to count. You can simply specify the counter indices of where the table match should be stored and the counting happens automatically. - -However, it comes with disadvantages. Unless we involve the control-plane to add proper entries to the counter table in advance, it is not possible to count for new entries (i.e. unknown flow entries) for the table. Learning a new flow arrival and inserting a matching flow entry through the control plane can be slow. - -Therefore, we propose that the attendees of this tutorial to come up a solution that utilizes P4 registers and hashes to perform flexible heavy hitter detection solely on the data plane. - -The main idea of the solution is to implement counting bloom filter. The idea of the counting bloom filter is to compute multiple hashes of some values and increment the corresponding hash indices of a data structure. In the case of P4, we will use register as the data structure to store the counter values indexed by the hash values. After incrementing the values of the given hash indices, -we can check to see if the new packet belongs to a heavy hitter by looking at the minimum of the values in the multiple indices. The image below shows a general idea of how counting bloom filter looks like. - -![Alt text](images/counting_bloom_filter.png?raw=true "Counting Bloom Filter") - -Given this, we can see that we gain the flexibility of counting on new flows without having to add table entry rules, because the hashes can be computed regardless of the match values. - -This method, however, can suffer from hash collisions when there are too many heavy hitters for the filter to track, falsely increasing the count value computed by the filter. We ignore this issue in our tutorial by creating only small number of connections. - -In this tutorial, we simply react to detected heavy hitters by dropping the packets from the heavy hitters. In real world scenario, there are multitude of possible reactions that you can take on queuing policy, traffic enginnering, etc. - -## Running the starter code - -The starter code named `heavy_hitter.p4` is located under the `p4src` directory. The starter code contains the logic for simple L2 forwarding. In addition to that, it contains the logic to increment the P4 counter when a known flow rule is detected. The flow rule that is installed by the user (or by a control plane) is located in `commands.txt`. It contains the list of CLI commands to set the default table action, as well as actions for a particular match. Please make sure to understand both files before moving on. - -To compile the p4 code under `p4src` and run the switch and the mininet instance, simply run `./run_demo.sh`, which will fire up a mininet instance of 3 hosts (`h1, h2, h3`) and 1 switch (`s1`). -Once the p4 source compiles without error and after all the flow rules has been added, run `xterm h1 h2` on the mininet prompt. It will look something like the below. (If there are any errors at this stage, something is not right. Please let the organizer know as soon as possible.) - -``` -mininet> xterm h1 h2 -``` - -This will bring up two terminals for two hosts. To test the workflow, run `./receive.py` on `h2`'s window first and `./send.py h2` on `h1`'s window. This will send random flows from `h1` to `h2` with varying sizes and ports. (If interested, please refer to `send.py` to see what kinds of packets are being sent.) If the packet transfer is successful, we can see that packet counts are being made in `h2`'s window for each of the 5 tuples (src ip, dst ip, protocol id, src port, dst port). - -After running the flows, run `./read_counter.sh` in another shell to view the counter for each host. Note that this script will also reset all counters. We can see that it records the total number of packets from `h1` to `h2`, but lacks any other information. At this stage, you have successfully completed the example program. -Type `exit' in the mininet terminal to exit. - -## What to do? - -Now, we are going to implement the heavy hitter detection based on the counting bloom filter method. For this, there are two files to modify which are the p4 file and `commands.txt`. - -First let's discuss what to add in the p4 file. Under `p4src` there is a file called `heavy_hitter_template.p4`. Under this file, places to modify are marked by `TODO`. - -The overview of things to complete are as follows. - -1. Define constants for determining heavy hitters. For simplicity, let's define a five tuple who sends 100 or more packets to be the heavy hitter. -2. Update the metadata to contain the hash values and the counter values. -3. Define field_list to compute the hash for the flow. This should basically be the five tuple that we discussed above. -4. Define two separate hashes to generate the hash indices - * You don't have to worry about writing the hash functions. You can simply use csum16 and crc16. Also, refer to how ipv4_checksum is computed to generate the hash value -5. Define registers with enough space to store the counts -6. Define action to compute the hash, read the current value of the register and update the register as packets come in - * Hint: You will have to use these primitives. `modify_field_with_hash_based_offset`, `register_read`, `register_write`, `add_to_field`. You can find more about the primitives at the following [link.](https://github.com/p4lang/p4-hlir/blob/master/p4_hlir/frontend/primitives.json "List of P4 Primitives") - * You can choose to write two separate actions or a single action that updates both hashes. -7. Define tables to run the action(s) as defined above. -8. Define tables to drop the table when you detect the heavy hitter. -9. Modify the control flow to apply one table or the other. - -After completing this, you must change the name of the file to `heavy_hitter.p4` and overwrite it in `p4src` directory. - -Now, we must modify `commands.txt`: remove the CLI commands for count_table and add commands to set the default actions for the new tables. - -After all of this is done. First run `./cleanup` to remove any remnants of the prior run. Then we can again run `./run_demo.sh` to compile the new p4 file, install the new flow rule and open the mininet prompt. - -(Note: Mininet can still be running with errors in compilation or switch initalization. Please see the logs that are generated to see if all of the p4 code has been successfully compiled and/or the switch has been configured with the correct table rules). - -We can then generate random traffic as before using the terminals of the two hosts. One thing to notice now is that the traffic will start dropping after the sender reaches more than 1000 packets per five tuple instances. Also note that the traffic is organized by the five tuples rather than a single host, which makes heavy hitter detection much more fine tuned for some specific application. - -If all of this works well. Congratulations! You have finished this tutorial. - -There are also reference solution in soltion.tar.gz. Feel free to compare your solution to the reference solutions and provie comments and/or updates that can be made to the solution. diff --git a/SIGCOMM_2016/heavy_hitter/cleanup b/SIGCOMM_2016/heavy_hitter/cleanup deleted file mode 100755 index 66158dfec..000000000 --- a/SIGCOMM_2016/heavy_hitter/cleanup +++ /dev/null @@ -1,4 +0,0 @@ -sudo mn -c -sudo killall lt-simple_switch -sudo rm -f *.pcap -sudo rm -f *.json diff --git a/SIGCOMM_2016/heavy_hitter/commands.txt b/SIGCOMM_2016/heavy_hitter/commands.txt deleted file mode 100644 index 34b6f226a..000000000 --- a/SIGCOMM_2016/heavy_hitter/commands.txt +++ /dev/null @@ -1,16 +0,0 @@ -table_set_default send_frame _drop -table_set_default forward _drop -table_set_default ipv4_lpm _drop -table_set_default count_table _drop -table_add count_table count_action 10.0.0.1/32 => 0 -table_add count_table count_action 10.0.0.2/32 => 1 -table_add count_table count_action 10.0.0.3/32 => 2 -table_add send_frame rewrite_mac 1 => 00:00:00:00:00:01 -table_add send_frame rewrite_mac 2 => 00:00:00:00:00:02 -table_add send_frame rewrite_mac 3 => 00:00:00:00:00:03 -table_add forward set_dmac 10.0.0.1 => 00:00:00:00:00:01 -table_add forward set_dmac 10.0.0.2 => 00:00:00:00:00:02 -table_add forward set_dmac 10.0.0.3 => 00:00:00:00:00:03 -table_add ipv4_lpm set_nhop 10.0.0.1/32 => 10.0.0.1 1 -table_add ipv4_lpm set_nhop 10.0.0.2/32 => 10.0.0.2 2 -table_add ipv4_lpm set_nhop 10.0.0.3/32 => 10.0.0.3 3 diff --git a/SIGCOMM_2016/heavy_hitter/counter_example/commands.txt b/SIGCOMM_2016/heavy_hitter/counter_example/commands.txt deleted file mode 100644 index 34b6f226a..000000000 --- a/SIGCOMM_2016/heavy_hitter/counter_example/commands.txt +++ /dev/null @@ -1,16 +0,0 @@ -table_set_default send_frame _drop -table_set_default forward _drop -table_set_default ipv4_lpm _drop -table_set_default count_table _drop -table_add count_table count_action 10.0.0.1/32 => 0 -table_add count_table count_action 10.0.0.2/32 => 1 -table_add count_table count_action 10.0.0.3/32 => 2 -table_add send_frame rewrite_mac 1 => 00:00:00:00:00:01 -table_add send_frame rewrite_mac 2 => 00:00:00:00:00:02 -table_add send_frame rewrite_mac 3 => 00:00:00:00:00:03 -table_add forward set_dmac 10.0.0.1 => 00:00:00:00:00:01 -table_add forward set_dmac 10.0.0.2 => 00:00:00:00:00:02 -table_add forward set_dmac 10.0.0.3 => 00:00:00:00:00:03 -table_add ipv4_lpm set_nhop 10.0.0.1/32 => 10.0.0.1 1 -table_add ipv4_lpm set_nhop 10.0.0.2/32 => 10.0.0.2 2 -table_add ipv4_lpm set_nhop 10.0.0.3/32 => 10.0.0.3 3 diff --git a/SIGCOMM_2016/heavy_hitter/counter_example/heavy_hitter.p4 b/SIGCOMM_2016/heavy_hitter/counter_example/heavy_hitter.p4 deleted file mode 100644 index eaae17eda..000000000 --- a/SIGCOMM_2016/heavy_hitter/counter_example/heavy_hitter.p4 +++ /dev/null @@ -1,134 +0,0 @@ -/* 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. - */ - -#include "includes/headers.p4" -#include "includes/parser.p4" - -field_list ipv4_checksum_list { - ipv4.version; - ipv4.ihl; - ipv4.diffserv; - ipv4.totalLen; - ipv4.identification; - ipv4.flags; - ipv4.fragOffset; - ipv4.ttl; - ipv4.protocol; - ipv4.srcAddr; - ipv4.dstAddr; -} - -field_list_calculation ipv4_checksum { - input { - ipv4_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field ipv4.hdrChecksum { - verify ipv4_checksum; - update ipv4_checksum; -} - -action _drop() { - drop(); -} - -header_type custom_metadata_t { - fields { - nhop_ipv4: 32; - } -} - -metadata custom_metadata_t custom_metadata; - -action set_nhop(nhop_ipv4, port) { - modify_field(custom_metadata.nhop_ipv4, nhop_ipv4); - modify_field(standard_metadata.egress_spec, port); - add_to_field(ipv4.ttl, -1); -} - -action set_dmac(dmac) { - modify_field(ethernet.dstAddr, dmac); -} - -counter ip_src_counter { - type: packets; - static: count_table; - instance_count: 1024; -} - -action count_action(idx) { - count(ip_src_counter, idx); -} - -table count_table { - reads { - ipv4.srcAddr : lpm; - } - actions { - count_action; - _drop; - } - size : 1024; -} - -table ipv4_lpm { - reads { - ipv4.dstAddr : lpm; - } - actions { - set_nhop; - _drop; - } - size: 1024; -} - -table forward { - reads { - custom_metadata.nhop_ipv4 : exact; - } - actions { - set_dmac; - _drop; - } - size: 512; -} - -action rewrite_mac(smac) { - modify_field(ethernet.srcAddr, smac); -} - -table send_frame { - reads { - standard_metadata.egress_port: exact; - } - actions { - rewrite_mac; - _drop; - } - size: 256; -} - -control ingress { - apply(count_table); - apply(ipv4_lpm); - apply(forward); -} - -control egress { - apply(send_frame); -} diff --git a/SIGCOMM_2016/heavy_hitter/images/counting_bloom_filter.png b/SIGCOMM_2016/heavy_hitter/images/counting_bloom_filter.png deleted file mode 100644 index 2a47d8556..000000000 Binary files a/SIGCOMM_2016/heavy_hitter/images/counting_bloom_filter.png and /dev/null differ diff --git a/SIGCOMM_2016/heavy_hitter/p4src/heavy_hitter.p4 b/SIGCOMM_2016/heavy_hitter/p4src/heavy_hitter.p4 deleted file mode 100644 index eaae17eda..000000000 --- a/SIGCOMM_2016/heavy_hitter/p4src/heavy_hitter.p4 +++ /dev/null @@ -1,134 +0,0 @@ -/* 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. - */ - -#include "includes/headers.p4" -#include "includes/parser.p4" - -field_list ipv4_checksum_list { - ipv4.version; - ipv4.ihl; - ipv4.diffserv; - ipv4.totalLen; - ipv4.identification; - ipv4.flags; - ipv4.fragOffset; - ipv4.ttl; - ipv4.protocol; - ipv4.srcAddr; - ipv4.dstAddr; -} - -field_list_calculation ipv4_checksum { - input { - ipv4_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field ipv4.hdrChecksum { - verify ipv4_checksum; - update ipv4_checksum; -} - -action _drop() { - drop(); -} - -header_type custom_metadata_t { - fields { - nhop_ipv4: 32; - } -} - -metadata custom_metadata_t custom_metadata; - -action set_nhop(nhop_ipv4, port) { - modify_field(custom_metadata.nhop_ipv4, nhop_ipv4); - modify_field(standard_metadata.egress_spec, port); - add_to_field(ipv4.ttl, -1); -} - -action set_dmac(dmac) { - modify_field(ethernet.dstAddr, dmac); -} - -counter ip_src_counter { - type: packets; - static: count_table; - instance_count: 1024; -} - -action count_action(idx) { - count(ip_src_counter, idx); -} - -table count_table { - reads { - ipv4.srcAddr : lpm; - } - actions { - count_action; - _drop; - } - size : 1024; -} - -table ipv4_lpm { - reads { - ipv4.dstAddr : lpm; - } - actions { - set_nhop; - _drop; - } - size: 1024; -} - -table forward { - reads { - custom_metadata.nhop_ipv4 : exact; - } - actions { - set_dmac; - _drop; - } - size: 512; -} - -action rewrite_mac(smac) { - modify_field(ethernet.srcAddr, smac); -} - -table send_frame { - reads { - standard_metadata.egress_port: exact; - } - actions { - rewrite_mac; - _drop; - } - size: 256; -} - -control ingress { - apply(count_table); - apply(ipv4_lpm); - apply(forward); -} - -control egress { - apply(send_frame); -} diff --git a/SIGCOMM_2016/heavy_hitter/p4src/heavy_hitter_template.p4 b/SIGCOMM_2016/heavy_hitter/p4src/heavy_hitter_template.p4 deleted file mode 100644 index 19655e521..000000000 --- a/SIGCOMM_2016/heavy_hitter/p4src/heavy_hitter_template.p4 +++ /dev/null @@ -1,139 +0,0 @@ -/* 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. - */ - -#include "includes/headers.p4" -#include "includes/parser.p4" - -// TODO: Define the threshold value - -field_list ipv4_checksum_list { - ipv4.version; - ipv4.ihl; - ipv4.diffserv; - ipv4.totalLen; - ipv4.identification; - ipv4.flags; - ipv4.fragOffset; - ipv4.ttl; - ipv4.protocol; - ipv4.srcAddr; - ipv4.dstAddr; -} - -field_list_calculation ipv4_checksum { - input { - ipv4_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field ipv4.hdrChecksum { - verify ipv4_checksum; - update ipv4_checksum; -} - -action _drop() { - drop(); -} - -header_type custom_metadata_t { - fields { - nhop_ipv4: 32; - // TODO: Add the metadata for hash indices and count values - } -} - -metadata custom_metadata_t custom_metadata; - -action set_nhop(nhop_ipv4, port) { - modify_field(custom_metadata.nhop_ipv4, nhop_ipv4); - modify_field(standard_metadata.egress_spec, port); - add_to_field(ipv4.ttl, -1); -} - -action set_dmac(dmac) { - modify_field(ethernet.dstAddr, dmac); -} - -// TODO: Define the field list to compute the hash on -// Use the 5 tuple of -// (src ip, dst ip, src port, dst port, ip protocol) - -// TODO: Define two different hash functions to store the counts -// Note: Please use csum16 and crc16 for the hash functions - -// TODO: Define the registers to store the counts - - -// TODO: Actions to set heavy hitter filter - - -// TODO: Define the tables to run actions - - -// TODO: Define table to drop the heavy hitter traffic - - -table ipv4_lpm { - reads { - ipv4.dstAddr : lpm; - } - actions { - set_nhop; - _drop; - } - size: 1024; -} - -table forward { - reads { - custom_metadata.nhop_ipv4 : exact; - } - actions { - set_dmac; - _drop; - } - size: 512; -} - -action rewrite_mac(smac) { - modify_field(ethernet.srcAddr, smac); -} - -table send_frame { - reads { - standard_metadata.egress_port: exact; - } - actions { - rewrite_mac; - _drop; - } - size: 256; -} - -control ingress { - // TODO: Add table control here - if (// TODO: Add conditional here) { - // TODO: apply table here - } else { - apply(ipv4_lpm); - apply(forward); - } -} - -control egress { - apply(send_frame); -} diff --git a/SIGCOMM_2016/heavy_hitter/p4src/includes/headers.p4 b/SIGCOMM_2016/heavy_hitter/p4src/includes/headers.p4 deleted file mode 100644 index 90343674f..000000000 --- a/SIGCOMM_2016/heavy_hitter/p4src/includes/headers.p4 +++ /dev/null @@ -1,57 +0,0 @@ -/* 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_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; - } -} - -header_type tcp_t { - fields { - srcPort : 16; - dstPort : 16; - seqNo : 32; - ackNo : 32; - dataOffset : 4; - res : 3; - ecn : 3; - ctrl : 6; - window : 16; - checksum : 16; - urgentPtr : 16; - } -} - - diff --git a/SIGCOMM_2016/heavy_hitter/p4src/includes/parser.p4 b/SIGCOMM_2016/heavy_hitter/p4src/includes/parser.p4 deleted file mode 100644 index 66d453e84..000000000 --- a/SIGCOMM_2016/heavy_hitter/p4src/includes/parser.p4 +++ /dev/null @@ -1,49 +0,0 @@ -/* 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. - */ - -parser start { - return parse_ethernet; -} - -#define ETHERTYPE_IPV4 0x0800 - -header ethernet_t ethernet; - -parser parse_ethernet { - extract(ethernet); - return select(latest.etherType) { - ETHERTYPE_IPV4 : parse_ipv4; - default: ingress; - } -} - -header ipv4_t ipv4; - -#define IP_PROTOCOLS_TCP 6 - -parser parse_ipv4 { - extract(ipv4); - return select(latest.protocol) { - IP_PROTOCOLS_TCP : parse_tcp; - default: ingress; - } -} - -header tcp_t tcp; - -parser parse_tcp { - extract(tcp); - return ingress; -} diff --git a/SIGCOMM_2016/heavy_hitter/read_counters.sh b/SIGCOMM_2016/heavy_hitter/read_counters.sh deleted file mode 100755 index bbce420c5..000000000 --- a/SIGCOMM_2016/heavy_hitter/read_counters.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/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 - -CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI - -echo "displaying counters for h1" -echo "counter_read ip_src_counter 0" | $CLI_PATH heavy_hitter.json 22222 -echo -echo "displaying counters for h2" -echo "counter_read ip_src_counter 1" | $CLI_PATH heavy_hitter.json 22222 -echo -echo "displaying counters for h3" -echo "counter_read ip_src_counter 2" | $CLI_PATH heavy_hitter.json 22222 -echo -echo "resetting counters" -echo "counter_reset ip_src_counter" | $CLI_PATH heavy_hitter.json 22222 -echo - - diff --git a/SIGCOMM_2016/heavy_hitter/receive.py b/SIGCOMM_2016/heavy_hitter/receive.py deleted file mode 100755 index 39ef4e999..000000000 --- a/SIGCOMM_2016/heavy_hitter/receive.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/python - -# 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. - -from scapy.all import sniff -from scapy.all import IP, TCP - -VALID_IPS = ("10.0.0.1", "10.0.0.2", "10.0.0.3") -totals = {} - -def handle_pkt(pkt): - if IP in pkt and TCP in pkt: - src_ip = pkt[IP].src - dst_ip = pkt[IP].dst - proto = pkt[IP].proto - sport = pkt[TCP].sport - dport = pkt[TCP].dport - id_tup = (src_ip, dst_ip, proto, sport, dport) - if src_ip in VALID_IPS: - if id_tup not in totals: - totals[id_tup] = 0 - totals[id_tup] += 1 - print ("Received from %s total: %s" % - (id_tup, totals[id_tup])) - -def main(): - sniff(iface = "eth0", - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2016/heavy_hitter/run_demo.sh b/SIGCOMM_2016/heavy_hitter/run_demo.sh deleted file mode 100755 index 53e006200..000000000 --- a/SIGCOMM_2016/heavy_hitter/run_demo.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/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/tools/runtime_CLI.py - -$P4C_BM_SCRIPT p4src/heavy_hitter.p4 --json heavy_hitter.json -# This gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \ - --behavioral-exe $SWITCH_PATH \ - --json heavy_hitter.json \ - --cli $CLI_PATH diff --git a/SIGCOMM_2016/heavy_hitter/send.py b/SIGCOMM_2016/heavy_hitter/send.py deleted file mode 100755 index 5c7f971ad..000000000 --- a/SIGCOMM_2016/heavy_hitter/send.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/python - -# 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. - -from scapy.all import Ether, IP, sendp, get_if_hwaddr, get_if_list, TCP, Raw -import sys -import random, string - -def randomword(max_length): - length = random.randint(1, max_length) - return ''.join(random.choice(string.lowercase) for i in range(length)) - -def read_topo(): - nb_hosts = 0 - nb_switches = 0 - links = [] - with open("topo.txt", "r") as f: - line = f.readline()[:-1] - w, nb_switches = line.split() - assert(w == "switches") - line = f.readline()[:-1] - w, nb_hosts = line.split() - assert(w == "hosts") - for line in f: - if not f: break - a, b = line.split() - links.append( (a, b) ) - return int(nb_hosts), int(nb_switches), links - -def send_random_traffic(dst): - dst_mac = None - dst_ip = None - src_mac = [get_if_hwaddr(i) for i in get_if_list() if i == 'eth0'] - if len(src_mac) < 1: - print ("No interface for output") - sys.exit(1) - src_mac = src_mac[0] - src_ip = None - if src_mac =="00:00:00:00:00:01": - src_ip = "10.0.0.1" - elif src_mac =="00:00:00:00:00:02": - src_ip = "10.0.0.2" - elif src_mac =="00:00:00:00:00:03": - src_ip = "10.0.0.3" - else: - print ("Invalid source host") - sys.exit(1) - - if dst == 'h1': - dst_mac = "00:00:00:00:00:01" - dst_ip = "10.0.0.1" - elif dst == 'h2': - dst_mac = "00:00:00:00:00:02" - dst_ip = "10.0.0.2" - elif dst == 'h3': - dst_mac = "00:00:00:00:00:03" - dst_ip = "10.0.0.3" - else: - print ("Invalid host to send to") - sys.exit(1) - - total_pkts = 0 - random_ports = random.sample(xrange(1024, 65535), 10) - for port in random_ports: - num_packets = random.randint(50, 250) - for i in range(num_packets): - data = randomword(100) - p = Ether(dst=dst_mac,src=src_mac)/IP(dst=dst_ip,src=src_ip) - p = p/TCP(dport=port)/Raw(load=data) - print p.show() - sendp(p, iface = "eth0") - total_pkts += 1 - print "Sent %s packets in total" % total_pkts - -if __name__ == '__main__': - if len(sys.argv) < 2: - print("Usage: python send.py dst_host_name") - sys.exit(1) - else: - dst_name = sys.argv[1] - send_random_traffic(dst_name) diff --git a/SIGCOMM_2016/heavy_hitter/solution.tar.gz b/SIGCOMM_2016/heavy_hitter/solution.tar.gz deleted file mode 100644 index cd87a8ddb..000000000 Binary files a/SIGCOMM_2016/heavy_hitter/solution.tar.gz and /dev/null differ diff --git a/SIGCOMM_2016/heavy_hitter/topo.py b/SIGCOMM_2016/heavy_hitter/topo.py deleted file mode 100644 index 1bff8f46d..000000000 --- a/SIGCOMM_2016/heavy_hitter/topo.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/python - -# 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. - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.log import setLogLevel -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host - -import argparse -from time import sleep -import os -import subprocess - -_THIS_DIR = os.path.dirname(os.path.realpath(__file__)) -_THRIFT_BASE_PORT = 22222 - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--cli', help='Path to BM CLI', - type=str, action="store", required=True) - -args = parser.parse_args() - -class MyTopo(Topo): - def __init__(self, sw_path, json_path, nb_hosts, nb_switches, links, **opts): - # Initialize topology and default options - Topo.__init__(self, **opts) - - for i in xrange(nb_switches): - self.addSwitch('s%d' % (i + 1), - sw_path = sw_path, - json_path = json_path, - thrift_port = _THRIFT_BASE_PORT + i, - pcap_dump = True, - device_id = i) - - for h in xrange(nb_hosts): - self.addHost('h%d' % (h + 1), ip="10.0.0.%d" % (h + 1), - mac="00:00:00:00:00:0%d" % (h+1)) - - for a, b in links: - self.addLink(a, b) - -def read_topo(): - nb_hosts = 0 - nb_switches = 0 - links = [] - with open("topo.txt", "r") as f: - line = f.readline()[:-1] - w, nb_switches = line.split() - assert(w == "switches") - line = f.readline()[:-1] - w, nb_hosts = line.split() - assert(w == "hosts") - for line in f: - if not f: break - a, b = line.split() - links.append( (a, b) ) - return int(nb_hosts), int(nb_switches), links - - -def main(): - nb_hosts, nb_switches, links = read_topo() - - topo = MyTopo(args.behavioral_exe, - args.json, - nb_hosts, nb_switches, links) - - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None ) - net.start() - - for n in xrange(nb_hosts): - h = net.get('h%d' % (n + 1)) - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload eth0 %s off" % off - print cmd - h.cmd(cmd) - print "disable ipv6" - h.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - h.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - h.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - h.cmd("sysctl -w net.ipv4.tcp_congestion_control=reno") - h.cmd("iptables -I OUTPUT -p icmp --icmp-type destination-unreachable -j DROP") - - sleep(1) - - for i in xrange(nb_switches): - cmd = [args.cli, "--json", args.json, - "--thrift-port", str(_THRIFT_BASE_PORT + i)] - with open("commands.txt", "r") as f: - print " ".join(cmd) - try: - output = subprocess.check_output(cmd, stdin = f) - print output - except subprocess.CalledProcessError as e: - print e - print e.output - - sleep(1) - - print "Ready !" - - CLI( net ) - net.stop() - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/SIGCOMM_2016/heavy_hitter/topo.txt b/SIGCOMM_2016/heavy_hitter/topo.txt deleted file mode 100644 index 42b6aa26d..000000000 --- a/SIGCOMM_2016/heavy_hitter/topo.txt +++ /dev/null @@ -1,5 +0,0 @@ -switches 1 -hosts 3 -h1 s1 -h2 s1 -h3 s1 diff --git a/SIGCOMM_2016/p4-tutorial-slides.pdf b/SIGCOMM_2016/p4-tutorial-slides.pdf deleted file mode 100644 index 23d177421..000000000 Binary files a/SIGCOMM_2016/p4-tutorial-slides.pdf and /dev/null differ diff --git a/SIGCOMM_2017/P4_tutorial_labs.pdf b/SIGCOMM_2017/P4_tutorial_labs.pdf deleted file mode 100644 index e59950574..000000000 Binary files a/SIGCOMM_2017/P4_tutorial_labs.pdf and /dev/null differ diff --git a/SIGCOMM_2017/exercises/README.md b/SIGCOMM_2017/exercises/README.md deleted file mode 100644 index 256d2b72e..000000000 --- a/SIGCOMM_2017/exercises/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# P4 Tutorial - -## Introduction - -Welcome to the P4 Tutorial! - -We've prepared a set of exercises to help you get started with P4 -programming, organized into four modules: - -1. Introduction -* [Basic Forwarding](./basic) -* [Scrambler](./scrambler) - -2. Monitoring and Debugging -* [Explicit Congestion Notification](./ecn) -* [Multi-Hop Route Inspection](./mri) - -3. Advanced Data Structures -* [Source Routing](./source_routing) -* [Calculator](./calc) - -4. Dynamic Behavior -* [Load Balancing](./load_balance) -* [HULA](./hula) - -## Obtaining required software - -If you are starting this tutorial at SIGCOMM 2017, then we've already -provided you with a virtual machine that has all of the required -software installed. - -Otherwise, to complete the exercises, you will need to either build a -virtual machine or install several dependencies. - -To build the virtual machine: -- Install [Vagrant](https://vagrantup.com) and [VirtualBox](https://virtualbox.org) -- `cd vm` -- `vagrant up` -- Log in with username `p4` and password `p4` and issue the command `sudo shutdown -r now` -- When the machine reboots, you should have a graphical desktop machine with the required software pre-installed. - -To install dependences by hand: -- `git clone https://github.com/p4lang/behavioral-model.git` -- `git clone https://github.com/p4lang/p4c` -- `git clone https://github.com/p4lang/tutorials` -Then follow the instructions for how to build each package. Each of -these repositories come with dependencies, which can be installed -using the supplied instructions. The first repository -([behavioral-model](https://github.com/p4lang/behavioral-model)) -contains the P4 behavioral model. It is a C++ software switch that -will implement the functionality specified in your P4 program. The -second repository ([p4c](https://github.com/p4lang/p4c-bm)) is the -compiler for the behavioral model. It takes P4 program and produces a -JSON file which can be loaded by the behavioral model. The third -repository ([tutorial](https://github.com/p4lang/tutorial)) is the P4 -Tutorial itself. You will also need to install `mininet`. On Ubuntu, -it would look like this: - -``` -$ sudo apt-get install mininet -``` diff --git a/SIGCOMM_2017/exercises/basic/README.md b/SIGCOMM_2017/exercises/basic/README.md deleted file mode 100644 index e5203e809..000000000 --- a/SIGCOMM_2017/exercises/basic/README.md +++ /dev/null @@ -1,163 +0,0 @@ -# Implementing Basic Forwarding - -## Introduction - -The objective of this exercise is to write a P4 program that -implements basic forwarding. To keep things simple, we will just -implement forwarding for IPv4. - -With IPv4 forwarding, the switch must perform the following actions -for every packet: (i) update the source and destination MAC addresses, -(ii) decrement the time-to-live (TTL) in the IP header, and (iii) -forward the packet out the appropriate port. - -Your switch will have a single table, which the control plane will -populate with static rules. Each rule will map an IP address to the -MAC address and output port for the next hop. We have already defined -the control plane rules, so you only need to implement the data plane -logic of your P4 program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`basic.p4`, which initially drops all packets. Your job will be to -extend this skeleton program to properly forward IPv4 packets. - -Before that, let's compile the incomplete `basic.p4` and bring -up a switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `basic.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) - configured in a triangle, each connected to one host (`h1`, `h2`, - and `h3`). - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc. - -2. You should now see a Mininet command prompt. Open two terminals -for `h1` and `h2`, respectively: - ```bash - mininet> xterm h1 h2 - ``` -3. Each host includes a small Python-based messaging client and -server. In `h2`'s xterm, start the server: - ```bash - ./receive.py - ``` -4. In `h1`'s xterm, send a message to `h2`: - ```bash - ./send.py 10.0.2.2 "P4 is cool" - ``` - The message will not be received. -5. Type `exit` to leave each xterm and the Mininet command line. - -The message was not received because each switch is programmed -according to `basic.p4`, which drops all packets on arrival. -Your job is to extend this file so it forwards packets. - -### A note about the control plane - -A P4 program defines a packet-processing pipeline, but the rules -within each table are inserted by the control plane. When a rule -matches a packet, its action is invoked with parameters supplied by -the control plane as part of the rule. - -In this exercise, we have already implemented the the control plane -logic for you. As part of bringing up the Mininet instance, the -`run.sh` script will install packet-processing rules in the tables of -each switch. These are defined in the `sX-commands.txt` files, where -`X` corresponds to the switch number. - -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. The commands in the files -`sX-commands.txt` refer to specific tables, keys, and actions by name, -and any changes in the P4 program that add or rename tables, keys, or -actions will need to be reflected in these command files. - -## Step 2: Implement L3 forwarding - -The `basic.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. Your implementation should follow -the structure given in this file---replace each `TODO` with logic -implementing the missing piece. - -A complete `basic.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 (`ipv4_t`). -2. **TODO:** Parsers for Ethernet and IPv4 that populate `ethernet_t` and `ipv4_t` fields. -3. An action to drop a packet, using `mark_to_drop()`. -4. **TODO:** An action (called `ipv4_forward`) that: - 1. Sets the egress port for the next hop. - 2. Updates the ethernet destination address with the address of the next hop. - 3. Updates the ethernet source address with the address of the switch. - 4. Decrements the TTL. -5. **TODO:** A control that: - 1. Defines a table that will read an IPv4 destination address, and - invoke either `drop` or `ipv4_forward`. - 2. An `apply` block that applies the table. -6. **TODO:** A deparser that selects the order - in which fields inserted into the outgoing packet. -7. A `package` instantiation supplied with the parser, control, and deparser. - > In general, a package also requires instances of checksum verification - > and recomputation controls. These are not necessary for this tutorial - > and are replaced with instantiations of empty controls. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, your message from -`h1` should be delivered to `h2`. - -### Food for thought - -The "test suite" for your solution---sending a message from `h1` to -`h2`---is not very robust. What else should you test to be confident -of your implementation? - -> Although the Python `scapy` library is outside the scope of this tutorial, -> it can be used to generate packets for testing. The `send.py` file shows how -> to use it. - -Other questions to consider: - - How would you enhance your program to support next hops? - - Is this program enough to replace a router? What's missing? - -### Troubleshooting - -There are several problems that might manifest as you develop your program: - -1. `basic.p4` might fails to compile. In this case, `run.sh` will -report the error emitted from the compiler and halt. - -2. `basic.p4` might compile but fail to support the control plane -rules in the `s1-commands.txt` through `s3-command.txt` files that -`run.sh` tries to install using the Bmv2 CLI. In this case, `run.sh` -will report these errors to `stderr`. Use these error messages to fix -your `basic.p4` implementation. - -3. `basic.p4` might compile, and the control plane rules might be -installed, but the switch might not process packets in the desired -way. The `build/logs/.log` files contain detailed logs -that describing how each switch processes each packet. The output is -detailed and can help pinpoint logic errors in your implementation. - -#### Cleaning up Mininet - -In the latter two cases above, `run.sh` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to the next -exercise: implementing the [scrambler](../scrambler)! diff --git a/SIGCOMM_2017/exercises/basic/basic.p4 b/SIGCOMM_2017/exercises/basic/basic.p4 deleted file mode 100644 index c0e7d9395..000000000 --- a/SIGCOMM_2017/exercises/basic/basic.p4 +++ /dev/null @@ -1,160 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - /* TODO: add parser logic */ - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - /* TODO: fill out code in action body */ - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - /* TODO: fix ingress control logic */ - ipv4_lpm.apply(); - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - /* TODO: add deparser logic */ - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/basic/p4app.json b/SIGCOMM_2017/exercises/basic/p4app.json deleted file mode 100644 index 92df55667..000000000 --- a/SIGCOMM_2017/exercises/basic/p4app.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "program": "basic.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["s1", "s2"], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s3", "h3"]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - } - }, - "switches": { - "s1": { - "entries": "s1-commands.txt" - }, - "s2": { - "entries": "s2-commands.txt" - }, - "s3": { - "entries": "s3-commands.txt" - } - } - } - } -} diff --git a/SIGCOMM_2017/exercises/basic/receive.py b/SIGCOMM_2017/exercises/basic/receive.py deleted file mode 100755 index c93182f89..000000000 --- a/SIGCOMM_2017/exercises/basic/receive.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -import sys -import struct -import os - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/')) - iface = ifaces[0] - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="tcp", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/basic/run.sh b/SIGCOMM_2017/exercises/basic/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/SIGCOMM_2017/exercises/basic/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/SIGCOMM_2017/exercises/basic/s1-commands.txt b/SIGCOMM_2017/exercises/basic/s1-commands.txt deleted file mode 100644 index 3bfdf6156..000000000 --- a/SIGCOMM_2017/exercises/basic/s1-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 diff --git a/SIGCOMM_2017/exercises/basic/s2-commands.txt b/SIGCOMM_2017/exercises/basic/s2-commands.txt deleted file mode 100644 index 35a49d7a0..000000000 --- a/SIGCOMM_2017/exercises/basic/s2-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 diff --git a/SIGCOMM_2017/exercises/basic/s3-commands.txt b/SIGCOMM_2017/exercises/basic/s3-commands.txt deleted file mode 100644 index c111108a2..000000000 --- a/SIGCOMM_2017/exercises/basic/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:03:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 diff --git a/SIGCOMM_2017/exercises/basic/send.py b/SIGCOMM_2017/exercises/basic/send.py deleted file mode 100755 index 00496d9de..000000000 --- a/SIGCOMM_2017/exercises/basic/send.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr -from scapy.all import Packet -from scapy.all import Ether, IP, UDP, TCP - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - print "sending on interface %s to %s" % (iface, str(addr)) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2] - pkt.show2() - sendp(pkt, iface=iface, verbose=False) - - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/basic/solution/basic.p4 b/SIGCOMM_2017/exercises/basic/solution/basic.p4 deleted file mode 100644 index 7d491ea43..000000000 --- a/SIGCOMM_2017/exercises/basic/solution/basic.p4 +++ /dev/null @@ -1,176 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/calc/README.md b/SIGCOMM_2017/exercises/calc/README.md deleted file mode 100644 index 1e63bbc73..000000000 --- a/SIGCOMM_2017/exercises/calc/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# Implementing a P4 Calculator - -## Introduction - -The objective of this tutorial is to implement a basic calculator -using a custom protocol header written in P4. The header will contain -an operation to perform and two operands. When a switch receives a -calculator packet header, it will execute the operation on the -operands, and return the result to the sender. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`calc.p4`, which initially drops all packets. Your job will be to -extend it to properly implement the calculator logic. - -As a first step, compile the incomplete `calc.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `calc.p4`, and - - * start a Mininet instance with one switches (`s1`) connected to - two hosts (`h1`, `h2`). - * The hosts are assigned IPs of `10.0.1.1` and `10.0.2.2`. - -2. We've written a small Python-based driver program that will allow -you to test your calculator. You can run the driver program directly -from the Mininet command prompt: - -``` -mininet> h1 python calc.py -> -``` - -3. The driver program will provide a new prompt, at which you can type -basic expressions. The test harness will parse your expression, and -prepare a packet with the corresponding operator and operands. It will -then send a packet to the switch for evaluation. When the switch -returns the result of the computation, the test program will print the -result. However, because the calculator program is not implemented, -you should see an error message. - -``` -> 1+1 -Didn't receive response -> -``` - -## Step 2: Implement Calculator - -To implement the calculator, you will need to define a custom -calculator header, and implement the switch logic to parse header, -perform the requested operation, write the result in the header, and -return the packet to the sender. - -We will use the following header format: - - 0 1 2 3 - +----------------+----------------+----------------+---------------+ - | P | 4 | Version | Op | - +----------------+----------------+----------------+---------------+ - | Operand A | - +----------------+----------------+----------------+---------------+ - | Operand B | - +----------------+----------------+----------------+---------------+ - | Result | - +----------------+----------------+----------------+---------------+ - - -- P is an ASCII Letter 'P' (0x50) -- 4 is an ASCII Letter '4' (0x34) -- Version is currently 0.1 (0x01) -- Op is an operation to Perform: - - '+' (0x2b) Result = OperandA + OperandB - - '-' (0x2d) Result = OperandA - OperandB - - '&' (0x26) Result = OperandA & OperandB - - '|' (0x7c) Result = OperandA | OperandB - - '^' (0x5e) Result = OperandA ^ OperandB - - -We will assume that the calculator header is carried over Ethernet, -and we will use the Ethernet type 0x1234 to indicate the presence of -the header. - -Given what you have learned so far, your task is to implement the P4 -calculator program. There is no control plane logic, so you need only -worry about the data plane implementation. - -A working calculator implementation will parse the custom headers, -execute the mathematical operation, write the result in the result -field, and return the packet to the sender. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, you should see the -correct result: - -``` -> 1+1 -2 -> -``` - -## Next Steps - -Congratulations, your implementation works! Move on to -[Load Balancer](../load_balance). diff --git a/SIGCOMM_2017/exercises/calc/calc.config b/SIGCOMM_2017/exercises/calc/calc.config deleted file mode 100644 index e69de29bb..000000000 diff --git a/SIGCOMM_2017/exercises/calc/calc.p4 b/SIGCOMM_2017/exercises/calc/calc.p4 deleted file mode 100644 index b4ed4b40f..000000000 --- a/SIGCOMM_2017/exercises/calc/calc.p4 +++ /dev/null @@ -1,250 +0,0 @@ -/* -*- P4_16 -*- */ - -/* - * P4 Calculator - * - * This program implements a simple protocol. It can be carried over Ethernet - * (Ethertype 0x1234). - * - * The Protocol header looks like this: - * - * 0 1 2 3 - * +----------------+----------------+----------------+---------------+ - * | P | 4 | Version | Op | - * +----------------+----------------+----------------+---------------+ - * | Operand A | - * +----------------+----------------+----------------+---------------+ - * | Operand B | - * +----------------+----------------+----------------+---------------+ - * | Result | - * +----------------+----------------+----------------+---------------+ - * - * P is an ASCII Letter 'P' (0x50) - * 4 is an ASCII Letter '4' (0x34) - * Version is currently 0.1 (0x01) - * Op is an operation to Perform: - * '+' (0x2b) Result = OperandA + OperandB - * '-' (0x2d) Result = OperandA - OperandB - * '&' (0x26) Result = OperandA & OperandB - * '|' (0x7c) Result = OperandA | OperandB - * '^' (0x5e) Result = OperandA ^ OperandB - * - * The device receives a packet, performs the requested operation, fills in the - * result and sends the packet back out of the same port it came in on, while - * swapping the source and destination addresses. - * - * If an unknown operation is specified or the header is not valid, the packet - * is dropped - */ - -#include -#include - -/* - * Define the headers the program will recognize - */ - -/* - * Standard ethernet header - */ -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -/* - * This is a custom protocol header for the calculator. We'll use - * ethertype 0x1234 for is (see parser) - */ -const bit<16> P4CALC_ETYPE = 0x1234; -const bit<8> P4CALC_P = 0x50; // 'P' -const bit<8> P4CALC_4 = 0x34; // '4' -const bit<8> P4CALC_VER = 0x01; // v0.1 -const bit<8> P4CALC_PLUS = 0x2b; // '+' -const bit<8> P4CALC_MINUS = 0x2d; // '-' -const bit<8> P4CALC_AND = 0x26; // '&' -const bit<8> P4CALC_OR = 0x7c; // '|' -const bit<8> P4CALC_CARET = 0x5e; // '^' - -header p4calc_t { - bit<8> op; -/* TODO - * fill p4calc_t header with P, four, ver, op, operand_a, operand_b, and res - entries based on above protocol header definition. - */ -} - -/* - * All headers, used in the program needs to be assembed into a single struct. - * We only need to declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ -struct headers { - ethernet_t ethernet; - p4calc_t p4calc; -} - -/* - * All metadata, globally used in the program, also needs to be assembed - * into a single struct. As in the case of the headers, we only need to - * declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ - -struct metadata { - /* In our case it is empty */ -} - -/************************************************************************* - *********************** P A R S E R *********************************** - *************************************************************************/ -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - state start { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - P4CALC_ETYPE : check_p4calc; - default : accept; - } - } - - state check_p4calc { - /* TODO: just uncomment the following parse block */ - /* - transition select(packet.lookahead().p, - packet.lookahead().four, - packet.lookahead().ver) { - (P4CALC_P, P4CALC_4, P4CALC_VER) : parse_p4calc; - default : accept; - } - */ - } - - state parse_p4calc { - packet.extract(hdr.p4calc); - transition accept; - } -} - -/************************************************************************* - ************ C H E C K S U M V E R I F I C A T I O N ************* - *************************************************************************/ -control MyVerifyChecksum(inout headers hdr, - inout metadata meta) { - apply { } -} - -/************************************************************************* - ************** I N G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action send_back(bit<32> result) { - /* TODO - * - put the result back in hdr.p4calc.res - * - swap MAC addresses in hdr.ethernet.dstAddr and - * hdr.ethernet.srcAddr using a temp variable - * - Send the packet back to the port it came from - by saving standard_metadata.ingress_port into - standard_metadata.egress_spec - */ - } - - action operation_add() { - /* TODO call send_back with operand_a + operand_b */ - } - - action operation_sub() { - /* TODO call send_back with operand_a - operand_b */ - } - - action operation_and() { - /* TODO call send_back with operand_a & operand_b */ - } - - action operation_or() { - /* TODO call send_back with operand_a | operand_b */ - } - - action operation_xor() { - /* TODO call send_back with operand_a ^ operand_b */ - } - - action operation_drop() { - mark_to_drop(); - } - - table calculate { - key = { - hdr.p4calc.op : exact; - } - actions = { - operation_add; - operation_sub; - operation_and; - operation_or; - operation_xor; - operation_drop; - } - const default_action = operation_drop(); - const entries = { - P4CALC_PLUS : operation_add(); - P4CALC_MINUS: operation_sub(); - P4CALC_AND : operation_and(); - P4CALC_OR : operation_or(); - P4CALC_CARET: operation_xor(); - } - } - - apply { - if (hdr.p4calc.isValid()) { - calculate.apply(); - } else { - operation_drop(); - } - } -} - -/************************************************************************* - **************** E G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* - ************* C H E C K S U M C O M P U T A T I O N ************** - *************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* - *********************** D E P A R S E R ******************************* - *************************************************************************/ -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.p4calc); - } -} - -/************************************************************************* - *********************** S W I T T C H ********************************** - *************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/calc/calc.py b/SIGCOMM_2017/exercises/calc/calc.py deleted file mode 100755 index d2fb035a5..000000000 --- a/SIGCOMM_2017/exercises/calc/calc.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys -import socket -import random -import struct -import re - -from scapy.all import sendp, send, srp1 -from scapy.all import Packet, hexdump -from scapy.all import Ether, StrFixedLenField, XByteField, IntField -from scapy.all import bind_layers -import readline - -class P4calc(Packet): - name = "P4calc" - fields_desc = [ StrFixedLenField("P", "P", length=1), - StrFixedLenField("Four", "4", length=1), - XByteField("version", 0x01), - StrFixedLenField("op", "+", length=1), - IntField("operand_a", 0), - IntField("operand_b", 0), - IntField("result", 0xDEADBABE)] - -bind_layers(Ether, P4calc, type=0x1234) - -class NumParseError(Exception): - pass - -class OpParseError(Exception): - pass - -class Token: - def __init__(self,type,value = None): - self.type = type - self.value = value - -def num_parser(s, i, ts): - pattern = "^\s*([0-9]+)\s*" - match = re.match(pattern,s[i:]) - if match: - ts.append(Token('num', match.group(1))) - return i + match.end(), ts - raise NumParseError('Expected number literal.') - - -def op_parser(s, i, ts): - pattern = "^\s*([-+&|^])\s*" - match = re.match(pattern,s[i:]) - if match: - ts.append(Token('num', match.group(1))) - return i + match.end(), ts - raise NumParseError("Expected binary operator '-', '+', '&', '|', or '^'.") - - -def make_seq(p1, p2): - def parse(s, i, ts): - i,ts2 = p1(s,i,ts) - return p2(s,i,ts2) - return parse - - -def main(): - - p = make_seq(num_parser, make_seq(op_parser,num_parser)) - s = '' - iface = 'h1-eth0' - - while True: - s = str(raw_input('> ')) - if s == "quit": - break - print s - try: - i,ts = p(s,0,[]) - pkt = Ether(dst='00:04:00:00:00:00', type=0x1234) / P4calc(op=ts[1].value, - operand_a=int(ts[0].value), - operand_b=int(ts[2].value)) - pkt = pkt/' ' - -# pkt.show() - resp = srp1(pkt, iface=iface, timeout=1, verbose=False) - if resp: - p4calc=resp[P4calc] - if p4calc: - print p4calc.result - else: - print "cannot find P4calc header in the packet" - else: - print "Didn't receive response" - except Exception as error: - print error - - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/calc/p4app.json b/SIGCOMM_2017/exercises/calc/p4app.json deleted file mode 100644 index 5c92c8061..000000000 --- a/SIGCOMM_2017/exercises/calc/p4app.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "program": "calc.p4", - "language": "p4-16", - "targets": { - "mininet": { - "num-hosts": 2, - "switch-config": "calc.config" - } - } -} diff --git a/SIGCOMM_2017/exercises/calc/run.sh b/SIGCOMM_2017/exercises/calc/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/SIGCOMM_2017/exercises/calc/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/SIGCOMM_2017/exercises/calc/solution/calc.p4 b/SIGCOMM_2017/exercises/calc/solution/calc.p4 deleted file mode 100644 index 3c7e86c8a..000000000 --- a/SIGCOMM_2017/exercises/calc/solution/calc.p4 +++ /dev/null @@ -1,256 +0,0 @@ -/* -*- P4_16 -*- */ - -/* - * P4 Calculator - * - * This program implements a simple protocol. It can be carried over Ethernet - * (Ethertype 0x1234). - * - * The Protocol header looks like this: - * - * 0 1 2 3 - * +----------------+----------------+----------------+---------------+ - * | P | 4 | Version | Op | - * +----------------+----------------+----------------+---------------+ - * | Operand A | - * +----------------+----------------+----------------+---------------+ - * | Operand B | - * +----------------+----------------+----------------+---------------+ - * | Result | - * +----------------+----------------+----------------+---------------+ - * - * P is an ASCII Letter 'P' (0x50) - * 4 is an ASCII Letter '4' (0x34) - * Version is currently 0.1 (0x01) - * Op is an operation to Perform: - * '+' (0x2b) Result = OperandA + OperandB - * '-' (0x2d) Result = OperandA - OperandB - * '&' (0x26) Result = OperandA & OperandB - * '|' (0x7c) Result = OperandA | OperandB - * '^' (0x5e) Result = OperandA ^ OperandB - * - * The device receives a packet, performs the requested operation, fills in the - * result and sends the packet back out of the same port it came in on, while - * swapping the source and destination addresses. - * - * If an unknown operation is specified or the header is not valid, the packet - * is dropped - */ - -#include -#include - -/* - * Define the headers the program will recognize - */ - -/* - * Standard ethernet header - */ -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -/* - * This is a custom protocol header for the calculator. We'll use - * ethertype 0x1234 for is (see parser) - */ -const bit<16> P4CALC_ETYPE = 0x1234; -const bit<8> P4CALC_P = 0x50; // 'P' -const bit<8> P4CALC_4 = 0x34; // '4' -const bit<8> P4CALC_VER = 0x01; // v0.1 -const bit<8> P4CALC_PLUS = 0x2b; // '+' -const bit<8> P4CALC_MINUS = 0x2d; // '-' -const bit<8> P4CALC_AND = 0x26; // '&' -const bit<8> P4CALC_OR = 0x7c; // '|' -const bit<8> P4CALC_CARET = 0x5e; // '^' - -header p4calc_t { - bit<8> p; - bit<8> four; - bit<8> ver; - bit<8> op; - bit<32> operand_a; - bit<32> operand_b; - bit<32> res; -} - -/* - * All headers, used in the program needs to be assembed into a single struct. - * We only need to declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ -struct headers { - ethernet_t ethernet; - p4calc_t p4calc; -} - -/* - * All metadata, globally used in the program, also needs to be assembed - * into a single struct. As in the case of the headers, we only need to - * declare the type, but there is no need to instantiate it, - * because it is done "by the architecture", i.e. outside of P4 functions - */ - -struct metadata { - /* In our case it is empty */ -} - -/************************************************************************* - *********************** P A R S E R *********************************** - *************************************************************************/ -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - P4CALC_ETYPE : check_p4calc; - default : accept; - } - } - - state check_p4calc { - transition select(packet.lookahead().p, - packet.lookahead().four, - packet.lookahead().ver) { - (P4CALC_P, P4CALC_4, P4CALC_VER) : parse_p4calc; - default : accept; - } - } - - state parse_p4calc { - packet.extract(hdr.p4calc); - transition accept; - } -} - -/************************************************************************* - ************ C H E C K S U M V E R I F I C A T I O N ************* - *************************************************************************/ -control MyVerifyChecksum(inout headers hdr, - inout metadata meta) { - apply { } -} - -/************************************************************************* - ************** I N G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action send_back(bit<32> result) { - bit<48> tmp; - - /* Put the result back in */ - hdr.p4calc.res = result; - - /* Swap the MAC addresses */ - tmp = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = hdr.ethernet.srcAddr; - hdr.ethernet.srcAddr = tmp; - - /* Send the packet back to the port it came from */ - standard_metadata.egress_spec = standard_metadata.ingress_port; - } - - action operation_add() { - send_back(hdr.p4calc.operand_a + hdr.p4calc.operand_b); - } - - action operation_sub() { - send_back(hdr.p4calc.operand_a - hdr.p4calc.operand_b); - } - - action operation_and() { - send_back(hdr.p4calc.operand_a & hdr.p4calc.operand_b); - } - - action operation_or() { - send_back(hdr.p4calc.operand_a | hdr.p4calc.operand_b); - } - - action operation_xor() { - send_back(hdr.p4calc.operand_a ^ hdr.p4calc.operand_b); - } - - action operation_drop() { - mark_to_drop(); - } - - table calculate { - key = { - hdr.p4calc.op : exact; - } - actions = { - operation_add; - operation_sub; - operation_and; - operation_or; - operation_xor; - operation_drop; - } - const default_action = operation_drop(); - const entries = { - P4CALC_PLUS : operation_add(); - P4CALC_MINUS: operation_sub(); - P4CALC_AND : operation_and(); - P4CALC_OR : operation_or(); - P4CALC_CARET: operation_xor(); - } - } - - - apply { - if (hdr.p4calc.isValid()) { - calculate.apply(); - } else { - operation_drop(); - } - } -} - -/************************************************************************* - **************** E G R E S S P R O C E S S I N G ******************* - *************************************************************************/ -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* - ************* C H E C K S U M C O M P U T A T I O N ************** - *************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* - *********************** D E P A R S E R ******************************* - *************************************************************************/ -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.p4calc); - } -} - -/************************************************************************* - *********************** S W I T T C H ********************************** - *************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/ecn/README.md b/SIGCOMM_2017/exercises/ecn/README.md deleted file mode 100644 index 709692c45..000000000 --- a/SIGCOMM_2017/exercises/ecn/README.md +++ /dev/null @@ -1,195 +0,0 @@ -# Implementing ECN - -## Introduction - -The objective of this tutorial is to extend basic L3 forwarding with -an implementation of Explict Congestion Notification (ECN). - -ECN allows end-to-end notification of network congestion without -dropping packets. If an end-host supports ECN, it puts the value of 1 -or 2 in the `ipv4.ecn` field. For such packets, each switch may -change the value to 3 if the queue size is larger than a threshold. -The receiver copies the value to sender, and the sender can lower the -rate. - -As before, we have already defined the control plane rules for -routing, so you only need to implement the data plane logic of your P4 -program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`ecn.p4`, which initially implements L3 forwarding. Your job (in the -next step) will be to extend it to properly append set the ECN bits - -Before that, let's compile the incomplete `ecn.p4` and bring up a -network in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `ecn.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle. There are 5 hosts. `h1` and `h11` are connected to `s1`. - `h2` and `h22` are connected to `s2` and `h3` is connected to `s3`. - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc - (`10.0..`). - * The control plane programs the P4 tables in each switch based on - `sx-commands.txt` -2. We want to send a low rate traffic from `h1` to `h2` and a high -rate iperf traffic from `h11` to `h22`. The link between `s1` and -`s2` is common between the flows and is a bottleneck because we -reduced its bandwidth to 512kbps in p4app.json. Therefore, if we -capture packets at `h2`, we should see the right ECN value. - -3. You should now see a Mininet command prompt. Open four terminals -for `h1`, `h11`, `h2`, `h22`, respectively: - ```bash - mininet> xterm h1 h11 h2 h22 - ``` -3. In `h2`'s XTerm, start the server that captures packets: - ```bash - ./receive.py - ``` -4. in `h22`'s XTerm, start the iperf UDP server: - ```bash - iperf -s -u - ``` -5. In `h1`'s XTerm, send one packet per second to `h2` using send.py -say for 30 seconds: - ```bash - ./send.py 10.0.2.2 "P4 is cool" 30 - ``` - The message "P4 is cool" should be received in `h2`'s xterm, -6. In `h11`'s XTerm, start iperf client sending for 15 seconds - ```bash - iperf -c 10.0.2.22 -t 15 -u - ``` -7. At `h2`, the `ipv4.tos` field (DiffServ+ECN) is always 1 -8. type `exit` to close each XTerm window - -Your job is to extend the code in `ecn.p4` to implement the ECN logic -for setting the ECN flag. - -## Step 2: Implement ECN - -The `ecn.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the -missing piece. - -First we have to change the ipv4_t header by splitting the TOS field -into DiffServ and ECN fields. Remember to update the checksum block -accordingly. Then, in the egress control block we must compare the -queue length with ECN_THRESHOLD. If the queue length is larger than -the threshold, the ECN flag will be set. Note that this logic should -happen only if the end-host declared supporting ECN by setting the -original ECN to 1 or 2. - -A complete `ecn.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 (`ipv4_t`). -2. Parsers for Ethernet, IPv4, -3. An action to drop a packet, using `mark_to_drop()`. -4. An action (called `ipv4_forward`), which will: - 1. Set the egress port for the next hop. - 2. Update the ethernet destination address with the address of - the next hop. - 3. Update the ethernet source address with the address of the switch. - 4. Decrement the TTL. -5. An egress control block that checks the ECN and -`standard_metadata.enq_qdepth` and sets the ipv4.ecn. -6. A deparser that selects the order in which fields inserted into the outgoing - packet. -7. A `package` instantiation supplied with the parser, control, - checksum verfiication and recomputation and deparser. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, when your message from -`h1` is delivered to `h2`, you should see `tos` values change from 1 -to 3 as the queue builds up. `tos` may change back to 1 when iperf -finishes and the queue depletes. - -To easily track the `tos` values you may want to redirect the output -of `h2` to a file by running the following for `h2` - ```bash - ./receive.py > h2.log - ``` -and just print the `tos` values `grep tos build/h2.log` in a separate window -``` - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x3 - tos = 0x3 - tos = 0x3 - tos = 0x3 - tos = 0x3 - tos = 0x3 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 - tos = 0x1 -``` - -### Food for thought - -How can we let the user configure the threshold? - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `ecn.p4` fails to compile. In this case, `run.sh` will report the - error emitted from the compiler and stop. -2. `ecn.p4` compiles but does not support the control plane rules in - the `sX-commands.txt` files that `run.sh` tries to install using - the BMv2 CLI. In this case, `run.sh` will report these errors to - `stderr`. Use these error messages to fix your `ecn.p4` - implementation. -3. `ecn.p4` compiles, and the control plane rules are installed, but - the switch does not process packets in the desired way. The - `build/logs/.log` files contain trace messages - describing how each switch processes each packet. The output is - detailed and can help pinpoint logic errors in your implementation. - The `build/-.pcap` also contains the - pcap of packets on each interface. Use `tcpdump -r -xxx` - to print the hexdump of the packets. -4. `ecn.p4` compiles and all rules are installed. Packets go through - and the logs show that the queue length was not high enough to set - the ECN bit. Then either lower the threshold in the p4 code or - reduce the link bandwidth in `p4app.json` - -#### Cleaning up Mininet - -In the latter two cases above, `run.sh` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to the next -exercise: [Multi-Hop Route Inspection](../mri), which identifies which -link is the source of congestion. diff --git a/SIGCOMM_2017/exercises/ecn/p4app.json b/SIGCOMM_2017/exercises/ecn/p4app.json deleted file mode 100644 index e54eef274..000000000 --- a/SIGCOMM_2017/exercises/ecn/p4app.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "program": "ecn.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["h11", "s1"], ["s1", "s2", "0", 0.5], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s2", "h22"], ["s3", "h3"]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - }, - "h11": { - }, - "h22": { - } - - }, - "switches": { - "s1": { - "entries": "s1-commands.txt" - }, - "s2": { - "entries": "s2-commands.txt" - }, - "s3": { - "entries": "s3-commands.txt" - } - } - } - } -} diff --git a/SIGCOMM_2017/exercises/ecn/receive.py b/SIGCOMM_2017/exercises/ecn/receive.py deleted file mode 100755 index ca5f11903..000000000 --- a/SIGCOMM_2017/exercises/ecn/receive.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -import sys -import struct - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - iface = 'h2-eth0' - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/ecn/run.sh b/SIGCOMM_2017/exercises/ecn/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/SIGCOMM_2017/exercises/ecn/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/SIGCOMM_2017/exercises/ecn/s1-commands.txt b/SIGCOMM_2017/exercises/ecn/s1-commands.txt deleted file mode 100644 index 67e4f741c..000000000 --- a/SIGCOMM_2017/exercises/ecn/s1-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1 -table_add ipv4_lpm ipv4_forward 10.0.1.11/32 => 00:00:00:00:01:0b 2 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:02:00 4 diff --git a/SIGCOMM_2017/exercises/ecn/s2-commands.txt b/SIGCOMM_2017/exercises/ecn/s2-commands.txt deleted file mode 100644 index dcbf77863..000000000 --- a/SIGCOMM_2017/exercises/ecn/s2-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1 -table_add ipv4_lpm ipv4_forward 10.0.2.22/32 => 00:00:00:00:02:16 2 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:03:00 4 diff --git a/SIGCOMM_2017/exercises/ecn/s3-commands.txt b/SIGCOMM_2017/exercises/ecn/s3-commands.txt deleted file mode 100644 index c7285fbaa..000000000 --- a/SIGCOMM_2017/exercises/ecn/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 -table_add ipv4_lpm ipv4_forward 10.0.1.10/24 => 00:00:00:01:04:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.10/24 => 00:00:00:02:04:00 3 diff --git a/SIGCOMM_2017/exercises/ecn/send.py b/SIGCOMM_2017/exercises/ecn/send.py deleted file mode 100755 index f279d41b2..000000000 --- a/SIGCOMM_2017/exercises/ecn/send.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import Ether, IP, UDP -from scapy.all import IntField, FieldListField, FieldLenField, ShortField -from scapy.layers.inet import _IPOption_HDR - -from time import sleep - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - - if len(sys.argv)<4: - print 'pass 2 arguments: "" ' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(dst=addr, tos=1) / UDP(dport=4321, sport=1234) / sys.argv[2] - pkt.show2() - #hexdump(pkt) - try: - for i in range(int(sys.argv[3])): - sendp(pkt, iface=iface) - sleep(1) - except KeyboardInterrupt: - raise - - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/ecn/solution/ecn.p4 b/SIGCOMM_2017/exercises/ecn/solution/ecn.p4 deleted file mode 100644 index 2aebe20c8..000000000 --- a/SIGCOMM_2017/exercises/ecn/solution/ecn.p4 +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> TCP_PROTOCOL = 0x06; -const bit<16> TYPE_IPV4 = 0x800; -const bit<19> ECN_THRESHOLD = 10; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<6> diffserv; - bit<2> ecn; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action mark_ecn() { - hdr.ipv4.ecn = 3; - } - apply { - if (hdr.ipv4.ecn == 1 || hdr.ipv4.ecn == 2){ - if (standard_metadata.enq_qdepth >= ECN_THRESHOLD){ - mark_ecn(); - } - } - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.ecn, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/hula/README.md b/SIGCOMM_2017/exercises/hula/README.md deleted file mode 100644 index b4e5c3e30..000000000 --- a/SIGCOMM_2017/exercises/hula/README.md +++ /dev/null @@ -1,300 +0,0 @@ - -# Implementing HULA - -## Introduction - -The objective of this exercise is to implement a simplified version of -[HULA](http://web.mit.edu/anirudh/www/hula-sosr16.pdf). -In contrast to ECMP, which selects the next hop randomly, HULA load balances -the flows over multiple paths to a destination ToR based on queue occupancy -of switches in each path. Thus, it can use the whole bisection bandwidth. -To keep the example simple, we implement it on top of source routing exercise. - -Here is how HULA works: -- Each ToR switch generates a HULA packet to each other ToR switch - to probe the condition of every path between the source and the destination ToR. - Each HULA packet is forwarded to the destination ToR (forward path), collects the maximum - queue length it observes while being forwarded, and finally delivers that information - to the destination ToR. Based on the congestion information collected via probes, - each destination ToR then can maintain the current best path (i.e., least congested path) - from each source ToR. To share the best path information with the source ToRs so that - the sources can use that information for new flows, the destination ToRs notify - source ToRs of the current best path by returning the HULA probe back to the source - ToR (reverse path) only if the current best path changes. The probe packets include - a HULA header and a list of ports for source routing. We describe the elements of HULA header later. -- In the forward path: - - Each hop updates the queue length field in the hula header if the local queue depth observed by - the HULA packet is larger than maximum queue depth recorded in the probe packet. Thus when - the packet reaches the destination ToR, queue length field will be the maximum observed queue length - on the forward path. - - At destination ToR, - 1. find the queue length of current best path from the source ToR. - 2. if the new path is better, update the queue length and best path and return - the HULA probe to the source path. This is done by setting the direction field - in the HULA header and returning the packet to the ingress port. - 3. if the probe came through the current best path, the destination ToR just updates - the existing value. This is needed to know if the best path got worse and hence allow - other paths to replace it later. It is inefficient to save the whole path ID - (i.e., sequence of switch IDs) and compare it in the data plane; - note, P4 doesn't have a loop construct. Instead, we keep a 32 bit digest of a - path in the HULA header. Each destination ToR only saves and compares the - digest of the best path along with its queue length. - The `hula.digest` field is set by source ToR upon creating the HULA packet - and does not change along the path. -- In the reverse path: - - Each hop will update the "routing next hop" to the destination ToR based on the port - it received the HULA packet on (as it was the best path). Then it forwards the packet - to the next hop in reverse path based on source routing. - - Source ToR also drops the packet. -- Now for each data packet, - - Each hop hashes the flow header fields and looks into a "flow table". - - If it doesn't find the next hop for the flow, looks into "routing next hop" to - find the next hop for destination ToR. We assume each ToR serves a /24 IP address. - The switch also updates the "flow table". "flow table" prevents the path of a flow to change - in order to avoid packet re-ordering and path oscilation during updating next hops. - - Otherwise, each hop just uses the next hop. - -Your switch will have multiple tables, which the control plane will -populate with static rules. We have already defined -the control plane rules, so you only need to implement the data plane -logic of your P4 program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the reference. - - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`hula.p4`, which initially drops all packets. Your job (in the next -step) will be to extend it to properly update HULA packets and forward data packets. - -Before that, let's compile the incomplete `hula.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `hula.p4`, and - * start a Mininet instance with three ToR switches (`s1`, `s2`, `s3`) - and two spine switches ( `s11`, `s22`). - * The hosts (`h1`, `h2`, `h3`) are assigned IPs of `10.0.1.1`, `10.0.2.2`, and `10.0.3.3`. - -2. You should now see a Mininet command prompt. Just ping `h2` from `h1`: - ```bash - mininet> h1 ping h2 - ``` -It doesn't work as no path is set. - -3. Type `exit` to close the Mininet command line. - -The message was not received because each switch is programmed with -`hula.p4`, which drops all data packets. Your job is to extend -this file. - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules governing packet -processing are inserted into the pipeline by the control plane. When a rule -matches a packet, its action is invoked with parameters supplied by the control -plane as part of the rule. - -In this exercise, the control plane logic has already been implemented. As -part of bringing up the Mininet instance, the `run.sh` script will install -packet-processing rules in the tables of each switch. These are defined in the -`sX-commands.txt` files, where `X` corresponds to the switch number. - -**Important:** A P4 program also defines the interface between the switch -pipeline and control plane. The `sX-commands.txt` files contain lists of -commands for the BMv2 switch API. These commands refer to specific tables, -keys, and actions by name, and any changes in the P4 program that add or rename -tables, keys, or actions will need to be reflected in these command files. - -## Step 2: Implement Hula - -The `hula.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the missing piece. - -A complete `hula.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`), Hula (`hula_t`), - Source Routing (`srcRoute_t`), IPv4 (`ipv4_t`), UDP(`udp_t`). -2. Parsers for the above headers. -3. Registers: - - `srcindex_qdepth_reg`: At destination ToR saves queue length of the best path - from each Source ToR - - `srcindex_digest_reg`: At destination ToR saves the digest of the best path - from each Source ToR - - `dstindex_nhop_reg`: At each hop, saves the next hop to reach each destination ToR - - `flow_port_reg`: At each hop saves the next hop for each flow -4. `hula_fwd table`: looks at the destination IP of a HULA packet. If it is the destination ToR, - it runs `hula_dst` action to set `meta.index` field based on source IP (source ToR). - The index is used later to find queue depth and digest of current best path from that source ToR. - Otherwise, this table just runs `srcRoute_nhop` to perform source routing. -5. `hula_bwd` table: at revere path, updates next hop to the destination ToR using `hula_set_nhop` -action. The action updates `dstindex_nhop_reg` register. -6. `hula_src` table checks the source IP address of a HULA packet in reverse path. - if this switch is the source, this is the end of reverse path, thus drop the packet. - Otherwise use `srcRoute_nhop` action to continue source routing in the reverse path. -7. `hula_nhop` table for data packets, reads destination IP/24 to get an index. - It uses the index to read `dstindex_nhop_reg` register and get best next hop to the - destination ToR. -8. dmac table just updates ethernet destination address based on next hop. -9. An apply block that has the following logic: - * If the packet has a HULA header - * In forward path (`hdr.hula.dir==0`): - * Apply `hula_fwd` table to check if it is the destination ToR or not - * If this switch is the destination ToR (`hula_dst` action ran and - set the `meta.index` based on the source IP address): - * read `srcindex_qdepth_reg` for the queue length of - the current best path from the source ToR - * If the new queue length is better, update the entry in `srcindex_qdepth_reg` and - save the path digest in `srcindex_digest_reg`. Then return the HULA packet to the source ToR - by sending to its ingress port and setting `hula.dir=1` (reverse path) - * else, if this HULA packet came through current best path (`hula.digest` is equal to - the value in `srcindex_digest_reg`), update its queue length in `srcindex_qdepth_reg`. - In this case we don't need to send the HULA packet back, thus drop the packet. - * in reverse path (`hdr.hula.dir==1`): - * apply `hula_bwd` to update the HULA next hop to the destination ToR - * apply `hula_src` table to drop the packet if it is the source ToR of the HULA packet - * If it is a data packet - * compute the hash of flow - * **TODO** read nexthop port from `flow_port_reg` into a temporary variable, say `port`. - * **TODO** If no entry found (`port==0`), read next hop by applying `hula_nhop` table. - Then save the value into `flow_port_reg` for later packets. - * **TODO** if it is found, save `port` into `standard_metadata.egress_spec` to finish routing. - * apply `dmac` table to update `ethernet.dstAddr`. This is necessary for the links that send packets - to hosts. Otherwise their NIC will drop packets. - * udpate TTL -5. **TODO:** An egress control that for HULA packets that are in forward path (`hdr.hula.dir==0`) - compares `standard_metadata.deq_qdepth` to `hdr.hula.qdepth` - in order to save the maximum in `hdr.hula.qdepth` -7. A deparser that selects the order in which fields inserted into the outgoing - packet. -8. A `package` instantiation supplied with the parser, control, checksum verification and - recomputation and deparser. - -## Step 3: Run your solution - -1. Run Mininet same as Step 1 - -2. Open a separate terminal, go to `exercises/hula`, and run `sudo ./generatehula.py`. - This python script makes each ToR switch generate one HULA probe for each other ToR and - through each separate forward path. For example, `s1` first probes `s2` via `s11` and then via `s22`. - Then `s1` probes `s3` again first via `s11` and then via `s22`. `s2` does the same thing to probe - paths to `s1` and `s3`, and so does `s3`. - -3. Now run `h1 ping h2`. The ping should work if you have completed the ingress control block in `hula.p4`. -Note at this point, every ToR considers all paths are equal because there isn't any congestion in the network. - -Now we are going to test a more complex scenario. - -We first create two iperf sessions: one from `h1` to `h3`, and the other from `h2` to `h3`. -Since both `s1` and `s2` currently think their best paths to `s3` should go through `s11`, -the two connections will use the same spine switch (`s11`). Note we throttled the -links from the spine switches to `s3` down to 1Mbps. Hence, each of the two connections -achieves only ~512Kbps. Let's confirm this by taking the following steps. - -1. open a terminal window on `h1`, `h2` and `h3`: -```bash -xterm h1 h2 h3 -``` -2. start iperf server at `h3` -```bash -iperf -s -u -i 1 -``` -3. run iperf client at `h1` -```bash -iperf -c 10.0.3.3 -t 30 -u -b 2m -``` -4. run iperf client in `h2`. try to do step 3 and 4 simultaneously. -```bash -iperf -c 10.0.3.3 -t 30 -u -b 2m -``` -While the connections are running, watch the iperf server's output at `h3`. -Although there are two completely non-overlapping paths for `h1` and `h2` to reach `h3`, -both `h1` and `h2` end up using the same spine, and hence the aggregate -throughput of the two connections is capped to 1Mbps. -You can confirm this by watching the performance of each connection. - - -Our goal is allowing the two connections to use two different spine switches and hence achieve -1Mbps each. We can do this by first causing congestion on one of the spines. More specifically -we'll create congestion at the queue in `s11` facing the link `s11-to-s3` by running a -long-running connection (an elephant flow) from `s1` to `s3` through `s11`. -Once the queue builds up due to the elephant, then we'll let `s2` generate HULA probes -several times so that it can learn to avoid forwarding new flows destined to `s3` through `s11`. -The following steps achieve this. - -1. open a terminal window on `h1`, `h2` and `h3`. (By the way, if you have already closed mininet, -you need to re-run the mininet test and run `generatehula.py` first, to setup initial routes) -```bash -xterm h1 h2 h3 -``` -2. start iperf server at `h3` -```bash -iperf -s -u -i 1 -``` -3. create a long-running full-demand connection from `h1` to `h3` through `s11`. -you can do this by running the following at `h1` -```bash -iperf -c 10.0.3.3 -t 3000 -u -b 2m -``` -4. outside mininet (in a separate terminal), go to `exercises/hula`, and run the following several (5 to 10) times -```bash -sudo ./generatehula.py -``` -This should let `s2` know that the path through `s11` to `s3` is congested and -the best path is now through the uncongested spine, `s22`. -5. Now, run iperf client at `h2` -```bash -iperf -c 10.0.3.3 -t 30 -u -b 2m -``` -You will be able to confirm both iperf sessions achieve 1Mbps because they go through two different spines. - -### Food for thought -* how can we implement flowlet routing (as opposed to flow routing) say based on the timestamp of packets -* in the ingress control logic, the destination ToR always sends a HULA packet -back on the reverse path if the queue length is better. But this is not necessary -if it came from the best path. Can you improve the code? -* the hula packets on the congested path may get dropped or extremely delayed, -thus the destination ToR would not be aware of the worsened condition of the current best path. -A solution could be that the destination ToR uses a timeout mechanism to ignore the current best path -if it doesn't receive a hula packet through it for a long time. -How can you implement this inside dataplane? - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `hula.p4` fails to compile. In this case, `run.sh` will report the -error emitted from the compiler and stop. - -2. `hula.p4` compiles but does not support the control plane rules in -the `sX-commands.txt` files that `run.sh` tries to install using the BMv2 CLI. -In this case, `run.sh` will report these errors to `stderr`. Use these error -messages to fix your `hula.p4` implementation. - -3. `hula.p4` compiles, and the control plane rules are installed, but -the switch does not process packets in the desired way. The -`build/logs/.log` files contain trace messages describing how each -switch processes each packet. The output is detailed and can help pinpoint -logic errors in your implementation. -The `build/-.pcap` also contains the pcap of packets on each -interface. Use `tcpdump -r -xxx` to print the hexdump of the packets. - -#### Cleaning up Mininet - -In the latter two cases above, `run.sh` may leave a Mininet instance running in -the background. Use the following command to clean up these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! diff --git a/SIGCOMM_2017/exercises/hula/generatehula.py b/SIGCOMM_2017/exercises/hula/generatehula.py deleted file mode 100755 index 0acacf5dc..000000000 --- a/SIGCOMM_2017/exercises/hula/generatehula.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr, bind_layers -from scapy.all import Packet -from scapy.all import Ether, IP, UDP -from scapy.fields import * -from time import sleep -import crcmod - -class Hula(Packet): - fields_desc = [ BitField("dir", 0, 1), - BitField("qdepth", 0, 15), - XIntField("digest", None)] - def post_build(self, p, pay): - p += pay - if self.digest is None: - crc32 = crcmod.Crc(0x104c11db7, initCrc=0, xorOut=0xFFFFFFFF) - crc32.update(str(p)); - c = bytes(bytearray.fromhex("%08x" % crc32.crcValue)) - p = p[:2]+ c +p[6:] - #ck = checksum(p) - #p = p[:2]+"\x00\x00"+chr(ck>>8)+chr(ck&0xff)+p[6:] - return p - -class SourceRoute(Packet): - fields_desc = [ BitField("bos", 0, 1), - BitField("port", 0, 15)] - -bind_layers(Ether, Hula, type=0x2345) -bind_layers(Hula, SourceRoute) -bind_layers(SourceRoute, SourceRoute, bos=0) -bind_layers(SourceRoute, IP, bos=1) - -def main(): - period = 0 - if len(sys.argv) > 1: - period = int(sys.argv[1]) - - # src, dst , src routing , interface - info = [ - ("10.0.1.0", "10.0.2.0", (2, 2, 1, 1), "s1-eth1"), - ("10.0.1.0", "10.0.2.0", (3, 2, 1, 1), "s1-eth1"), - ("10.0.1.0", "10.0.3.0", (2, 3, 1, 1), "s1-eth1"), - ("10.0.1.0", "10.0.3.0", (3, 3, 1, 1), "s1-eth1"), - ("10.0.2.0", "10.0.1.0", (2, 1, 2, 1), "s2-eth1"), - ("10.0.2.0", "10.0.1.0", (3, 1, 2, 1), "s2-eth1"), - ("10.0.2.0", "10.0.3.0", (2, 3, 2, 1), "s2-eth1"), - ("10.0.2.0", "10.0.3.0", (3, 3, 2, 1), "s2-eth1"), - ("10.0.3.0", "10.0.1.0", (2, 1, 3, 1), "s3-eth1"), - ("10.0.3.0", "10.0.1.0", (3, 1, 3, 1), "s3-eth1"), - ("10.0.3.0", "10.0.2.0", (2, 2, 3, 1), "s3-eth1"), - ("10.0.3.0", "10.0.2.0", (3, 2, 3, 1), "s3-eth1")] - - - try: - while True: - for e in info: - ports = e[2] - pkt = Ether(src=get_if_hwaddr(e[3]), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt / Hula(dir=0, qdepth=0) - pkt = pkt / SourceRoute(bos=0, port=ports[0]) - pkt = pkt / SourceRoute(bos=0, port=ports[1]) - pkt = pkt / SourceRoute(bos=0, port=ports[2]) - pkt = pkt / SourceRoute(bos=1, port=ports[3]) - pkt = pkt / IP(dst=e[1], src=e[0]) / UDP(dport=4321, sport=1234) - #pkt.show2() - sendp(pkt, iface=e[3], verbose=False) - if period == 0: - break; - else: - sleep(period) - except KeyboardInterrupt: - raise - - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/hula/hula.p4 b/SIGCOMM_2017/exercises/hula/hula.p4 deleted file mode 100644 index 8faa0ed46..000000000 --- a/SIGCOMM_2017/exercises/hula/hula.p4 +++ /dev/null @@ -1,433 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; -const bit<16> TYPE_HULA = 0x2345; - -#define MAX_HOPS 9 -#define TOR_NUM 32 -#define TOR_NUM_1 33 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; -typedef bit<15> qdepth_t; -typedef bit<32> digest_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header srcRoute_t { - bit<1> bos; - bit<15> port; -} - -header hula_t { - /* 0 is forward path, 1 is the backward path */ - bit<1> dir; - /* max qdepth seen so far in the forward path */ - qdepth_t qdepth; - /* digest of the source routing list to uniquely identify each path */ - digest_t digest; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -header udp_t { - bit<16> srcPort; - bit<16> dstPort; - bit<16> length_; - bit<16> checksum; -} - -struct metadata { - /* At destination ToR, this is the index of register - that saves qdepth for the best path from each source ToR */ - bit<32> index; -} - -struct headers { - ethernet_t ethernet; - srcRoute_t[MAX_HOPS] srcRoutes; - ipv4_t ipv4; - udp_t udp; - hula_t hula; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_HULA : parse_hula; - TYPE_IPV4 : parse_ipv4; - default : accept; - } - } - - state parse_hula { - packet.extract(hdr.hula); - transition parse_srcRouting; - } - - state parse_srcRouting { - packet.extract(hdr.srcRoutes.next); - transition select(hdr.srcRoutes.last.bos) { - 1 : parse_ipv4; - default : parse_srcRouting; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition select(hdr.ipv4.protocol) { - 8w17: parse_udp; - default: accept; - } - } - - state parse_udp { - packet.extract(hdr.udp); - transition accept; - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - /* At destination ToR, saves the queue depth of the best path from - * each source ToR - */ - register(TOR_NUM) srcindex_qdepth_reg; - - /* At destination ToR, saves the digest of the best path from - * each source ToR - */ - register(TOR_NUM) srcindex_digest_reg; - - /* At each hop, saves the next hop to reach each destination ToR */ - register>(TOR_NUM) dstindex_nhop_reg; - - /* At each hop saves the next hop for each flow */ - register>(65536) flow_port_reg; - - /* This action will drop packets */ - action drop() { - mark_to_drop(); - } - - action nop() { - } - - action update_ttl(){ - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - action set_dmac(macAddr_t dstAddr){ - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - } - - /* This action just applies source routing */ - action srcRoute_nhop() { - standard_metadata.egress_spec = (bit<9>)hdr.srcRoutes[0].port; - hdr.srcRoutes.pop_front(1); - } - - /* Runs if it is the destination ToR. - * Control plane Gives the index of register for best path from a source ToR - */ - action hula_dst(bit<32> index) { - meta.index = index; - } - - /* On reverse path, update nexthop to a destination ToR to the ingress port - * where we receive hula packet - */ - action hula_set_nhop(bit<32> index) { - dstindex_nhop_reg.write(index, (bit<16>)standard_metadata.ingress_port); - } - - /* Read next hop that is saved in hula_set_nhop action for data packets */ - action hula_get_nhop(bit<32> index){ - bit<16> tmp; - dstindex_nhop_reg.read(tmp, index); - standard_metadata.egress_spec = (bit<9>)tmp; - } - - /* Record best path at destination ToR */ - action change_best_path_at_dst(){ - srcindex_qdepth_reg.write(meta.index, hdr.hula.qdepth); - srcindex_digest_reg.write(meta.index, hdr.hula.digest); - } - - /* At destination ToR, return packet to source by - * - changing its hula direction - * - send it to the port it came from - */ - action return_hula_to_src(){ - hdr.hula.dir = 1; - standard_metadata.egress_spec = standard_metadata.ingress_port; - } - - /* On forward path: - * - if destination ToR: run hula_dst to set the index based on srcAddr - * - otherwise run srcRoute_nhop to perform source routing - */ - table hula_fwd { - key = { - hdr.ipv4.dstAddr: exact; - hdr.ipv4.srcAddr: exact; - } - actions = { - hula_dst; - srcRoute_nhop; - } - default_action = srcRoute_nhop; - size = TOR_NUM_1; // TOR_NUM + 1 - } - - /* At each hop in reverse path - * update next hop to destination ToR in registers. - * index is set based on dstAddr - */ - table hula_bwd { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - hula_set_nhop; - } - size = TOR_NUM; - } - - /* On reverse path: - * - if source ToR (srcAddr = this switch) drop hula packet - * - otherwise, just forward in the reverse path based on source routing - */ - table hula_src { - key = { - hdr.ipv4.srcAddr: exact; - } - actions = { - drop; - srcRoute_nhop; - } - default_action = srcRoute_nhop; - size = 2; - } - - /* Get nexthop based on dstAddr using registers */ - table hula_nhop { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - hula_get_nhop; - drop; - } - default_action = drop; - size = TOR_NUM; - } - - /* Set right dmac for packets going to hosts */ - table dmac { - key = { - standard_metadata.egress_spec : exact; - } - actions = { - set_dmac; - nop; - } - default_action = nop; - size = 16; - } - - apply { - if (hdr.hula.isValid()){ - if (hdr.hula.dir == 0){ - switch(hula_fwd.apply().action_run){ - - /* if hula_dst action ran, this is the destination ToR */ - hula_dst: { - - /* if it is the destination ToR compare qdepth */ - qdepth_t old_qdepth; - srcindex_qdepth_reg.read(old_qdepth, meta.index); - - if (old_qdepth > hdr.hula.qdepth){ - change_best_path_at_dst(); - - /* only return hula packets that update best path */ - return_hula_to_src(); - }else{ - - /* update the best path even if it has gone worse - * so that other paths can replace it later - */ - digest_t old_digest; - srcindex_digest_reg.read(old_digest, meta.index); - if (old_digest == hdr.hula.digest){ - srcindex_qdepth_reg.write(meta.index, hdr.hula.qdepth); - } - - drop(); - } - } - } - }else { - /* update routing table in reverse path */ - hula_bwd.apply(); - - /* drop if source ToR */ - hula_src.apply(); - } - - }else if (hdr.ipv4.isValid()){ - bit<16> flow_hash; - hash( - flow_hash, - HashAlgorithm.crc16, - 16w0, - { hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.udp.srcPort}, - 32w65536); - - /* TODO: - * - Remove drop(); - * - Read nexthop port from flow_port_reg for the flow - * using flow_hash into a temporary variable - * - if port==0, - * - apply hula_nhop table to get next hop for destination ToR - * - write the next hop into the flow_port_reg register indexed by flow_hash - * - else: write port into standard_metadata.egress_spec - */ - drop(); - - /* set the right dmac so that ping and iperf work */ - dmac.apply(); - }else { - drop(); - } - - if (hdr.ipv4.isValid()){ - update_ttl(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { - /* TODO: - * if hula header is valid and this is forward path (hdr.hula.dir==0) - * check whether the qdepth in hula is smaller than - * (qdepth_t)standard_metadata.deq_qdepth - * if so, then update hdr.hula.qdepth - */ - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.hula); - packet.emit(hdr.srcRoutes); - packet.emit(hdr.ipv4); - packet.emit(hdr.udp); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/hula/p4app.json b/SIGCOMM_2017/exercises/hula/p4app.json deleted file mode 100644 index c5c656204..000000000 --- a/SIGCOMM_2017/exercises/hula/p4app.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "program": "hula.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["h2", "s2"], ["h3", "s3"], ["s1", "s11"], ["s1", "s22"], ["s2", "s11"], ["s2", "s22"], ["s11", "s3", "0", 1], ["s22", "s3", "0", 1]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - } - }, - "switches": { - "s1": { - "entries": "s1-commands.txt" - }, - "s2": { - "entries": "s2-commands.txt" - }, - "s3": { - "entries": "s3-commands.txt" - }, - "s11": { - "entries": "s11-commands.txt" - }, - "s22": { - "entries": "s22-commands.txt" - } - - } - } - } -} diff --git a/SIGCOMM_2017/exercises/hula/run.sh b/SIGCOMM_2017/exercises/hula/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/SIGCOMM_2017/exercises/hula/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/SIGCOMM_2017/exercises/hula/s1-commands.txt b/SIGCOMM_2017/exercises/hula/s1-commands.txt deleted file mode 100644 index 2ca3bf82b..000000000 --- a/SIGCOMM_2017/exercises/hula/s1-commands.txt +++ /dev/null @@ -1,16 +0,0 @@ -table_add hula_src drop 10.0.1.0 => -register_write dstindex_nhop_reg 0 1 -table_add hula_fwd hula_dst 10.0.1.0 10.0.1.0 => 0 -table_add hula_fwd hula_dst 10.0.1.0 10.0.2.0 => 1 -table_add hula_fwd hula_dst 10.0.1.0 10.0.3.0 => 2 -table_add dmac set_dmac 1 => 00:00:00:00:01:01 - -register_write srcindex_qdepth_reg 0 256 -register_write srcindex_qdepth_reg 1 256 -register_write srcindex_qdepth_reg 2 256 -table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0 -table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1 -table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2 -table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0 -table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1 -table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2 diff --git a/SIGCOMM_2017/exercises/hula/s11-commands.txt b/SIGCOMM_2017/exercises/hula/s11-commands.txt deleted file mode 100644 index f63d9395d..000000000 --- a/SIGCOMM_2017/exercises/hula/s11-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0 -table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1 -table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2 -table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0 -table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1 -table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2 diff --git a/SIGCOMM_2017/exercises/hula/s2-commands.txt b/SIGCOMM_2017/exercises/hula/s2-commands.txt deleted file mode 100644 index b5508f625..000000000 --- a/SIGCOMM_2017/exercises/hula/s2-commands.txt +++ /dev/null @@ -1,16 +0,0 @@ -table_add hula_src drop 10.0.2.0 => -register_write dstindex_nhop_reg 1 1 -table_add hula_fwd hula_dst 10.0.2.0 10.0.1.0 => 0 -table_add hula_fwd hula_dst 10.0.2.0 10.0.2.0 => 1 -table_add hula_fwd hula_dst 10.0.2.0 10.0.3.0 => 2 -table_add dmac set_dmac 1 => 00:00:00:00:02:02 - -register_write srcindex_qdepth_reg 0 256 -register_write srcindex_qdepth_reg 1 256 -register_write srcindex_qdepth_reg 2 256 -table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0 -table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1 -table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2 -table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0 -table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1 -table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2 diff --git a/SIGCOMM_2017/exercises/hula/s22-commands.txt b/SIGCOMM_2017/exercises/hula/s22-commands.txt deleted file mode 100644 index f63d9395d..000000000 --- a/SIGCOMM_2017/exercises/hula/s22-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0 -table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1 -table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2 -table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0 -table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1 -table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2 diff --git a/SIGCOMM_2017/exercises/hula/s3-commands.txt b/SIGCOMM_2017/exercises/hula/s3-commands.txt deleted file mode 100644 index 5600d499f..000000000 --- a/SIGCOMM_2017/exercises/hula/s3-commands.txt +++ /dev/null @@ -1,16 +0,0 @@ -table_add hula_src drop 10.0.3.0 => -register_write dstindex_nhop_reg 2 1 -table_add hula_fwd hula_dst 10.0.3.0 10.0.1.0 => 0 -table_add hula_fwd hula_dst 10.0.3.0 10.0.2.0 => 1 -table_add hula_fwd hula_dst 10.0.3.0 10.0.3.0 => 2 -table_add dmac set_dmac 1 => 00:00:00:00:03:03 - -register_write srcindex_qdepth_reg 0 256 -register_write srcindex_qdepth_reg 1 256 -register_write srcindex_qdepth_reg 2 256 -table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0 -table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1 -table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2 -table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0 -table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1 -table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2 diff --git a/SIGCOMM_2017/exercises/hula/solution/hula.p4 b/SIGCOMM_2017/exercises/hula/solution/hula.p4 deleted file mode 100644 index e8ea288de..000000000 --- a/SIGCOMM_2017/exercises/hula/solution/hula.p4 +++ /dev/null @@ -1,449 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; -const bit<16> TYPE_HULA = 0x2345; - -#define MAX_HOPS 9 -#define TOR_NUM 32 -#define TOR_NUM_1 33 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; -typedef bit<15> qdepth_t; -typedef bit<32> digest_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header srcRoute_t { - bit<1> bos; - bit<15> port; -} - -header hula_t { - /* 0 is forward path, 1 is the backward path */ - bit<1> dir; - /* max qdepth seen so far in the forward path */ - qdepth_t qdepth; - /* digest of the source routing list to uniquely identify each path */ - digest_t digest; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -header udp_t { - bit<16> srcPort; - bit<16> dstPort; - bit<16> length_; - bit<16> checksum; -} - -struct metadata { - /* At destination ToR, this is the index of register - that saves qdepth for the best path from each source ToR */ - bit<32> index; -} - -struct headers { - ethernet_t ethernet; - srcRoute_t[MAX_HOPS] srcRoutes; - ipv4_t ipv4; - udp_t udp; - hula_t hula; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_HULA : parse_hula; - TYPE_IPV4 : parse_ipv4; - default : accept; - } - } - - state parse_hula { - packet.extract(hdr.hula); - transition parse_srcRouting; - } - - state parse_srcRouting { - packet.extract(hdr.srcRoutes.next); - transition select(hdr.srcRoutes.last.bos) { - 1 : parse_ipv4; - default : parse_srcRouting; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition select(hdr.ipv4.protocol) { - 8w17: parse_udp; - default: accept; - } - } - - state parse_udp { - packet.extract(hdr.udp); - transition accept; - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - /* - * At destination ToR, saves the queue depth of the best path from - * each source ToR - */ - register(TOR_NUM) srcindex_qdepth_reg; - - /* - * At destination ToR, saves the digest of the best path from - * each source ToR - */ - register(TOR_NUM) srcindex_digest_reg; - - /* At each hop, saves the next hop to reach each destination ToR */ - register>(TOR_NUM) dstindex_nhop_reg; - - /* At each hop saves the next hop for each flow */ - register>(65536) flow_port_reg; - - /* This action will drop packets */ - action drop() { - mark_to_drop(); - } - - action nop() { - } - - action update_ttl(){ - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - action set_dmac(macAddr_t dstAddr){ - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - } - - /* This action just applies source routing */ - action srcRoute_nhop() { - standard_metadata.egress_spec = (bit<9>)hdr.srcRoutes[0].port; - hdr.srcRoutes.pop_front(1); - } - - /* - * Runs if it is the destination ToR. - * Control plane Gives the index of register for best path from source ToR - */ - action hula_dst(bit<32> index) { - meta.index = index; - } - - /* - * In reverse path, update nexthop to a destination ToR to ingress port - * where we receive hula packet - */ - action hula_set_nhop(bit<32> index) { - dstindex_nhop_reg.write(index, (bit<16>)standard_metadata.ingress_port); - } - - /* Read next hop that is saved in hula_set_nhop action for data packets */ - action hula_get_nhop(bit<32> index){ - bit<16> tmp; - dstindex_nhop_reg.read(tmp, index); - standard_metadata.egress_spec = (bit<9>)tmp; - } - - /* Record best path at destination ToR */ - action change_best_path_at_dst(){ - srcindex_qdepth_reg.write(meta.index, hdr.hula.qdepth); - srcindex_digest_reg.write(meta.index, hdr.hula.digest); - } - - /* - * At destination ToR, return packet to source by - * - changing its hula direction - * - send it to the port it came from - */ - action return_hula_to_src(){ - hdr.hula.dir = 1; - standard_metadata.egress_spec = standard_metadata.ingress_port; - } - - /* - * In forward path: - * - if destination ToR: run hula_dst to set the index based on srcAddr - * - otherwise run srcRoute_nhop to perform source routing - */ - table hula_fwd { - key = { - hdr.ipv4.dstAddr: exact; - hdr.ipv4.srcAddr: exact; - } - actions = { - hula_dst; - srcRoute_nhop; - } - default_action = srcRoute_nhop; - size = TOR_NUM_1; // TOR_NUM + 1 - } - - /* - * At each hop in reverse path - * update next hop to destination ToR in registers. - * index is set based on dstAddr - */ - table hula_bwd { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - hula_set_nhop; - } - size = TOR_NUM; - } - - /* - * in reverse path: - * - if source ToR (srcAddr = this switch) drop hula packet - * - otherwise, just forward in the reverse path based on source routing - */ - table hula_src { - key = { - hdr.ipv4.srcAddr: exact; - } - actions = { - drop; - srcRoute_nhop; - } - default_action = srcRoute_nhop; - size = 2; - } - - /* - * get nexthop based on dstAddr using registers - */ - table hula_nhop { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - hula_get_nhop; - drop; - } - default_action = drop; - size = TOR_NUM; - } - - /* - * set right dmac for packets going to hosts - */ - table dmac { - key = { - standard_metadata.egress_spec : exact; - } - actions = { - set_dmac; - nop; - } - default_action = nop; - size = 16; - } - - apply { - if (hdr.hula.isValid()){ - if (hdr.hula.dir == 0){ - switch(hula_fwd.apply().action_run){ - - /* if hula_dst action ran, this is the destination ToR */ - hula_dst: { - - /* if it is the destination ToR compare qdepth */ - qdepth_t old_qdepth; - srcindex_qdepth_reg.read(old_qdepth, meta.index); - - if (old_qdepth > hdr.hula.qdepth){ - change_best_path_at_dst(); - - /* only return hula packets that update best path */ - return_hula_to_src(); - }else{ - - /* update the best path even if it has gone worse - * so that other paths can replace it later - */ - digest_t old_digest; - srcindex_digest_reg.read(old_digest, meta.index); - if (old_digest == hdr.hula.digest){ - srcindex_qdepth_reg.write(meta.index, hdr.hula.qdepth); - } - - drop(); - } - } - } - }else { - /* update routing table in reverse path */ - hula_bwd.apply(); - - /* drop if source ToR */ - hula_src.apply(); - } - - }else if (hdr.ipv4.isValid()){ - bit<16> flow_hash; - hash( - flow_hash, - HashAlgorithm.crc16, - 16w0, - { hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.udp.srcPort}, - 32w65536); - - /* look into hula tables */ - bit<16> port; - flow_port_reg.read(port, (bit<32>)flow_hash); - - if (port == 0){ - /* if it is a new flow check hula paths */ - hula_nhop.apply(); - flow_port_reg.write((bit<32>)flow_hash, (bit<16>)standard_metadata.egress_spec); - }else{ - /* old flows still use old path to avoid oscilation and packet reordering */ - standard_metadata.egress_spec = (bit<9>)port; - } - - /* set the right dmac so that ping and iperf work */ - dmac.apply(); - }else { - drop(); - } - - if (hdr.ipv4.isValid()){ - update_ttl(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { - if (hdr.hula.isValid() && hdr.hula.dir == 0){ - - /* pick max qdepth in hula forward path */ - if (hdr.hula.qdepth < (qdepth_t)standard_metadata.deq_qdepth){ - - /* update queue length */ - hdr.hula.qdepth = (qdepth_t)standard_metadata.deq_qdepth; - } - } - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.hula); - packet.emit(hdr.srcRoutes); - packet.emit(hdr.ipv4); - packet.emit(hdr.udp); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/load_balance/README.md b/SIGCOMM_2017/exercises/load_balance/README.md deleted file mode 100644 index 1ff40fb67..000000000 --- a/SIGCOMM_2017/exercises/load_balance/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# Load Balancing - -In this exercise, you will implement a form of load balancing based on -a single version of Equal-Cost Multipath Forwarding. The switch you -will implement will use two tables to forward packets to one of two -destination hosts at random. The first table will use a hash function -(applied to a 5-tuple consisting of the source and destination -Ethernet addresses, source and destination IP addresses, and IP -protocol) to select one of two hosts. The second table will use the -computed hash value to forward the packet to the selected host. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`load_balance.p4`, which initially drops all packets. Your job (in -the next step) will be to extend it to properly forward packets. - -Before that, let's compile the incomplete `load_balance.p4` and bring -up a switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `load_balance.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle, each connected to one host (`h1`, `h2`, `h3`). - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc. - * We use the IP address 10.0.0.1 to indicate traffic that should be - load balanced between `h2` and `h3`. - -2. You should now see a Mininet command prompt. Open three terminals - for `h1`, `h2` and `h3`, respectively: - ```bash - mininet> xterm h1 h2 h3 - ``` -3. Each host includes a small Python-based messaging client and - server. In `h2` and `h3`'s XTerms, start the servers: - ```bash - ./receive.py - ``` -4. In `h1`'s XTerm, send a message from the client: - ```bash - ./send.py 10.0.0.1 "P4 is cool" - ``` - The message will not be received. -5. Type `exit` to leave each XTerm and the Mininet command line. - -The message was not received because each switch is programmed with -`load_balance.p4`, which drops all packets on arrival. Your job is to -extend this file. - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules -governing packet processing are inserted into the pipeline by the -control plane. When a rule matches a packet, its action is invoked -with parameters supplied by the control plane as part of the rule. - -In this exercise, the control plane logic has already been -implemented. As part of bringing up the Mininet instance, the -`run.sh` script will install packet-processing rules in the tables of -each switch. These are defined in the `s1-commands.txt` file. - -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. The `s1-commands.txt` file contains -a list of commands for the BMv2 switch API. These commands refer to -specific tables, keys, and actions by name, and any changes in the P4 -program that add or rename tables, keys, or actions will need to be -reflected in these command files. - -## Step 2: Implement Load Balancing - -The `load_balance.p4` file contains a skeleton P4 program with key -pieces of logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the -missing piece. - -A complete `load_balance.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 (`ipv4_t`). -2. Parsers for Ethernet and IPv4 that populate `ethernet_t` and `ipv4_t` fields. -3. An action to drop a packet, using `mark_to_drop()`. -4. **TODO:** An action (called `set_ecmp_select`), which will: - 1. Hashes the 5-tuple specified above using the `hash` extern - 2. Stores the result in the `meta.ecmp_select` field -5. **TODO:** A control that: - 1. Applies the `ecmp_group` table. - 2. Applies the `ecmp_nhop` table. -6. A deparser that selects the order in which fields inserted into the outgoing - packet. -7. A `package` instantiation supplied with the parser, control, and deparser. - > In general, a package also requires instances of checksum verification - > and recomputation controls. These are not necessary for this tutorial - > and are replaced with instantiations of empty controls. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, your message from -`h1` should be delivered to `h2` or `h3`. If you send several -messages, some should be received by each server. - -### Food for thought - - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `load_balance.p4` fails to compile. In this case, `run.sh` will -report the error emitted from the compiler and stop. - -2. `load_balance.p4` compiles but does not support the control plane -rules in the `sX-commands.txt` files that `run.sh` tries to install -using the BMv2 CLI. In this case, `run.sh` will report these errors -to `stderr`. Use these error messages to fix your `load_balance.p4` -implementation. - -3. `load_balance.p4` compiles, and the control plane rules are -installed, but the switch does not process packets in the desired way. -The `build/logs/.log` files contain trace messages -describing how each switch processes each packet. The output is -detailed and can help pinpoint logic errors in your implementation. - -#### Cleaning up Mininet - -In the latter two cases above, `run.sh` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to the next -exercise: [HULA](../hula). diff --git a/SIGCOMM_2017/exercises/load_balance/load_balance.p4 b/SIGCOMM_2017/exercises/load_balance/load_balance.p4 deleted file mode 100644 index 54dfed6bc..000000000 --- a/SIGCOMM_2017/exercises/load_balance/load_balance.p4 +++ /dev/null @@ -1,218 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - bit<32> srcAddr; - bit<32> dstAddr; -} - -header tcp_t { - bit<16> srcPort; - bit<16> dstPort; - bit<32> seqNo; - bit<32> ackNo; - bit<4> dataOffset; - bit<3> res; - bit<3> ecn; - bit<6> ctrl; - bit<16> window; - bit<16> checksum; - bit<16> urgentPtr; -} - -struct metadata { - bit<14> ecmp_select; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - tcp_t tcp; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - state start { - transition parse_ethernet; - } - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - 0x800: parse_ipv4; - default: accept; - } - } - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition select(hdr.ipv4.protocol) { - 6: parse_tcp; - default: accept; - } - } - state parse_tcp { - packet.extract(hdr.tcp); - transition accept; - } -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action drop() { - mark_to_drop(); - } - action set_ecmp_select(bit<16> ecmp_base, bit<32> ecmp_count) { - /* TODO: hash on 5-tuple and save the hash result in meta.ecmp_select - so that the ecmp_nhop table can use it to make a forwarding decision accordingly */ - } - action set_nhop(bit<48> nhop_dmac, bit<32> nhop_ipv4, bit<9> port) { - hdr.ethernet.dstAddr = nhop_dmac; - hdr.ipv4.dstAddr = nhop_ipv4; - standard_metadata.egress_spec = port; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - table ecmp_group { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - drop; - set_ecmp_select; - } - size = 1024; - } - table ecmp_nhop { - key = { - meta.ecmp_select: exact; - } - actions = { - drop; - set_nhop; - } - size = 2; - } - apply { - if (hdr.ipv4.isValid() && hdr.ipv4.ttl > 0) { - ecmp_group.apply(); - ecmp_nhop.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action rewrite_mac(bit<48> smac) { - hdr.ethernet.srcAddr = smac; - } - action drop() { - mark_to_drop(); - } - table send_frame { - key = { - standard_metadata.egress_port: exact; - } - actions = { - rewrite_mac; - drop; - } - size = 256; - } - apply { - send_frame.apply(); - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.tcp); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/load_balance/p4app.json b/SIGCOMM_2017/exercises/load_balance/p4app.json deleted file mode 100644 index 4754fda5d..000000000 --- a/SIGCOMM_2017/exercises/load_balance/p4app.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "program": "load_balance.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["s1", "s2"], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s3", "h3"]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - } - }, - "switches": { - "s1": { - "entries": "s1-commands.txt" - }, - "s2": { - "entries": "s2-commands.txt" - }, - "s3": { - "entries": "s3-commands.txt" - } - } - } - } -} diff --git a/SIGCOMM_2017/exercises/load_balance/receive.py b/SIGCOMM_2017/exercises/load_balance/receive.py deleted file mode 100755 index c93182f89..000000000 --- a/SIGCOMM_2017/exercises/load_balance/receive.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -import sys -import struct -import os - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/')) - iface = ifaces[0] - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="tcp", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/load_balance/run.sh b/SIGCOMM_2017/exercises/load_balance/run.sh deleted file mode 100755 index 15f786883..000000000 --- a/SIGCOMM_2017/exercises/load_balance/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/SIGCOMM_2017/exercises/load_balance/s1-commands.txt b/SIGCOMM_2017/exercises/load_balance/s1-commands.txt deleted file mode 100644 index a53e07c7d..000000000 --- a/SIGCOMM_2017/exercises/load_balance/s1-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.0.1/32 => 0 2 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:01:02 10.0.2.2 2 -table_add ecmp_nhop set_nhop 1 => 00:00:00:00:01:03 10.0.3.3 3 -table_add send_frame rewrite_mac 2 => 00:00:00:01:02:00 -table_add send_frame rewrite_mac 3 => 00:00:00:01:03:00 diff --git a/SIGCOMM_2017/exercises/load_balance/s2-commands.txt b/SIGCOMM_2017/exercises/load_balance/s2-commands.txt deleted file mode 100644 index ca78c19a1..000000000 --- a/SIGCOMM_2017/exercises/load_balance/s2-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.2.2/32 => 0 1 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:02:02 10.0.2.2 1 -table_add send_frame rewrite_mac 1 => 00:00:00:02:01:00 diff --git a/SIGCOMM_2017/exercises/load_balance/s3-commands.txt b/SIGCOMM_2017/exercises/load_balance/s3-commands.txt deleted file mode 100644 index 0f9e6b363..000000000 --- a/SIGCOMM_2017/exercises/load_balance/s3-commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -table_set_default ecmp_group drop -table_add ecmp_group set_ecmp_select 10.0.3.3/32 => 0 1 -table_add ecmp_nhop set_nhop 0 => 00:00:00:00:03:03 10.0.3.3 1 -table_add send_frame rewrite_mac 1 => 00:00:00:03:01:00 diff --git a/SIGCOMM_2017/exercises/load_balance/send.py b/SIGCOMM_2017/exercises/load_balance/send.py deleted file mode 100755 index 00496d9de..000000000 --- a/SIGCOMM_2017/exercises/load_balance/send.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr -from scapy.all import Packet -from scapy.all import Ether, IP, UDP, TCP - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - print "sending on interface %s to %s" % (iface, str(addr)) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2] - pkt.show2() - sendp(pkt, iface=iface, verbose=False) - - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/load_balance/solution/load_balance.p4 b/SIGCOMM_2017/exercises/load_balance/solution/load_balance.p4 deleted file mode 100644 index 64d9bcbfe..000000000 --- a/SIGCOMM_2017/exercises/load_balance/solution/load_balance.p4 +++ /dev/null @@ -1,225 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - bit<32> srcAddr; - bit<32> dstAddr; -} - -header tcp_t { - bit<16> srcPort; - bit<16> dstPort; - bit<32> seqNo; - bit<32> ackNo; - bit<4> dataOffset; - bit<3> res; - bit<3> ecn; - bit<6> ctrl; - bit<16> window; - bit<16> checksum; - bit<16> urgentPtr; -} - -struct metadata { - bit<14> ecmp_select; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - tcp_t tcp; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - 0x800: parse_ipv4; - default: accept; - } - } - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition select(hdr.ipv4.protocol) { - 6: parse_tcp; - default: accept; - } - } - state parse_tcp { - packet.extract(hdr.tcp); - transition accept; - } -} - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - action set_ecmp_select(bit<16> ecmp_base, bit<32> ecmp_count) { - hash(meta.ecmp_select, - HashAlgorithm.crc16, - ecmp_base, - { hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr, - hdr.ipv4.protocol, - hdr.tcp.srcPort, - hdr.tcp.dstPort }, - ecmp_count); - } - action set_nhop(bit<48> nhop_dmac, bit<32> nhop_ipv4, bit<9> port) { - hdr.ethernet.dstAddr = nhop_dmac; - hdr.ipv4.dstAddr = nhop_ipv4; - standard_metadata.egress_spec = port; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - table ecmp_group { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - drop; - set_ecmp_select; - } - size = 1024; - } - table ecmp_nhop { - key = { - meta.ecmp_select: exact; - } - actions = { - drop; - set_nhop; - } - size = 2; - } - apply { - if (hdr.ipv4.isValid() && hdr.ipv4.ttl > 0) { - ecmp_group.apply(); - ecmp_nhop.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action rewrite_mac(bit<48> smac) { - hdr.ethernet.srcAddr = smac; - } - action drop() { - mark_to_drop(); - } - table send_frame { - key = { - standard_metadata.egress_port: exact; - } - actions = { - rewrite_mac; - drop; - } - size = 256; - } - apply { - send_frame.apply(); - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.tcp); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/mri/README.md b/SIGCOMM_2017/exercises/mri/README.md deleted file mode 100644 index 30d20b1ec..000000000 --- a/SIGCOMM_2017/exercises/mri/README.md +++ /dev/null @@ -1,239 +0,0 @@ -# Implementing MRI - -## Introduction - -The objective of this tutorial is to extend basic L3 forwarding with a -scaled-down version of In-Band Network Telemetry (INT), which we call -Multi-Hop Route Inspection (MRI). - -MRI allows users to track the path and the length of queues that every -packet travels through. To support this functionality, you will need -to write a P4 program that appends an ID and queue length to the -header stack of every packet. At the destination, the sequence of -switch IDs correspond to the path, and each ID is followed by the -queue length of the port at switch. - -As before, we have already defined the control plane rules, so you -only need to implement the data plane logic of your P4 program. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`mri.p4`, which initially implements L3 forwarding. Your job (in the -next step) will be to extend it to properly prepend the MRI custom -headers. - -Before that, let's compile the incomplete `mri.p4` and bring up a -switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `mri.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle. There are 5 hosts. `h1` and `h11` are connected to `s1`. - `h2` and `h22` are connected to `s2` and `h3` is connected to `s3`. - * The hosts are assigned IPs of `10.0.1.10`, `10.0.2.10`, etc - (`10.0..`). - * The control plane programs the P4 tables in each switch based on - `sx-commands.txt` - -2. We want to send a low rate traffic from `h1` to `h2` and a high - rate iperf traffic from `h11` to `h22`. The link between `s1` and - `s2` is common between the flows and is a bottleneck because we - reduced its bandwidth to 512kbps in p4app.json. Therefore, if we - capture packets at `h2`, we should see high queue size for that - link. - -3. You should now see a Mininet command prompt. Open four terminals - for `h1`, `h11`, `h2`, `h22`, respectively: - ```bash - mininet> xterm h1 h11 h2 h22 - ``` -3. In `h2`'s xterm, start the server that captures packets: - ```bash - ./receive.py - ``` -4. in `h22`'s xterm, start the iperf UDP server: - ```bash - iperf -s -u - ``` - -5. In `h1`'s xterm, send one packet per second to `h2` using send.py - say for 30 seconds: - ```bash - ./send.py 10.0.2.2 "P4 is cool" 30 - ``` - The message "P4 is cool" should be received in `h2`'s xterm, -6. In `h11`'s xterm, start iperf client sending for 15 seconds - ```bash - h11 iperf -c 10.0.2.22 -t 15 -u - ``` -7. At `h2`, the MRI header has no hop info (`count=0`) -8. type `exit` to close each xterm window - -You should see the message received at host `h2`, but without any -information about the path the message took. Your job is to extend -the code in `mri.p4` to implement the MRI logic to record the path. - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules -governing packet processing are inserted into the pipeline by the -control plane. When a rule matches a packet, its action is invoked -with parameters supplied by the control plane as part of the rule. - -In this exercise, the control plane logic has already been -implemented. As part of bringing up the Mininet instance, the -`run.sh` script will install packet-processing rules in the tables of -each switch. These are defined in the `sX-commands.txt` files, where -`X` corresponds to the switch number. - -## Step 2: Implement MRI - -The `mri.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the -missing piece. - -MRI will require two custom headers. The first header, `mri_t`, -contains a single field `count`, which indicates the number of switch -IDs that follow. The second header, `switch_t`, contains switch ID and -Queue depth fields of each switch hop the packet goes through. - -One of the biggest challenges in implementing MRI is handling the -recursive logic for parsing these two headers. We will use a -`parser_metadata` field, `remaining`, to keep track of how many -`switch_t` headers we need to parse. In the `parse_mri` state, this -field should be set to `hdr.mri.count`. In the `parse_swtrace` state, -this field should be decremented. The `parse_swtrace` state will -transition to itself until `remaining` is 0. - -The MRI custom headers will be carried inside an IP Options -header. The IP Options header contains a field, `option`, which -indicates the type of the option. We will use a special type 31 to -indicate the presence of the MRI headers. - -Beyond the parser logic, you will add a table in egress, `swtrace` to -store the switch ID and queue depth, and actions that increment the -`count` field, and append a `switch_t` header. - -A complete `mri.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`), IPv4 (`ipv4_t`), - IP Options (`ipv4_option_t`), MRI (`mri_t`), and Switch (`switch_t`). -2. Parsers for Ethernet, IPv4, IP Options, MRI, and Switch that will -populate `ethernet_t`, `ipv4_t`, `ipv4_option_t`, `mri_t`, and -`switch_t`. -3. An action to drop a packet, using `mark_to_drop()`. -4. An action (called `ipv4_forward`), which will: - 1. Set the egress port for the next hop. - 2. Update the ethernet destination address with the address of - the next hop. - 3. Update the ethernet source address with the address of the switch. - 4. Decrement the TTL. -5. An ingress control that: - 1. Defines a table that will read an IPv4 destination address, and - invoke either `drop` or `ipv4_forward`. - 2. An `apply` block that applies the table. -6. At egress, an action (called `add_swtrace`) that will add the - switch ID and queue depth. -8. An egress control that applies a table (`swtrace`) to store the - switch ID and queue depth, and calls `add_swtrace`. -9. A deparser that selects the order in which fields inserted into the outgoing - packet. -10. A `package` instantiation supplied with the parser, control, - checksum verification and recomputation and deparser. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, when your message - from `h1` is delivered to `h2`, you should see the seqeunce of - switches through which the packet traveled plus the corresponding - queue depths. The expected output will look like the following, - which shows the MRI header, with a `count` of 2, and switch ids - (`swids`) 2 and 1. The queue depth at the common link (from s1 to - s2) is high. - -``` -got a packet -###[ Ethernet ]### - dst = 00:04:00:02:00:02 - src = f2:ed:e6:df:4e:fa - type = 0x800 -###[ IP ]### - version = 4L - ihl = 10L - tos = 0x0 - len = 42 - id = 1 - flags = - frag = 0L - ttl = 62 - proto = udp - chksum = 0x60c0 - src = 10.0.1.1 - dst = 10.0.2.2 - \options \ - |###[ MRI ]### - | copy_flag = 0L - | optclass = control - | option = 31L - | length = 20 - | count = 2 - | \swtraces \ - | |###[ SwitchTrace ]### - | | swid = 2 - | | qdepth = 0 - | |###[ SwitchTrace ]### - | | swid = 1 - | | qdepth = 17 -###[ Raw ]### - load = '\x04\xd2' -###[ Padding ]### - load = '\x10\xe1\x00\x12\x1c{P4 is cool' - -``` - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `mri.p4` fails to compile. In this case, `run.sh` will report the -error emitted from the compiler and stop. -2. `mri.p4` compiles but does not support the control plane rules in -the `sX-commands.txt` files that `run.sh` tries to install using the BMv2 CLI. -In this case, `run.sh` will report these errors to `stderr`. Use these error -messages to fix your `mri.p4` implementation. -3. `mri.p4` compiles, and the control plane rules are installed, but -the switch does not process packets in the desired way. The -`build/logs/.log` files contain trace messages describing -how each switch processes each packet. The output is detailed and can -help pinpoint logic errors in your implementation. The -`build/-.pcap` also contains the pcap of -packets on each interface. Use `tcpdump -r -xxx` to print -the hexdump of the packets. -4. `mri.p4` compiles and all rules are installed. Packets go through -and the logs show that the queue length is always 0. Then either -reduce the link bandwidth in `p4app.json`. - -#### Cleaning up Mininet - -In the latter two cases above, `run.sh` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to [Source -Routing](../source_routing). - diff --git a/SIGCOMM_2017/exercises/mri/mri.p4 b/SIGCOMM_2017/exercises/mri/mri.p4 deleted file mode 100644 index 1f7871b0f..000000000 --- a/SIGCOMM_2017/exercises/mri/mri.p4 +++ /dev/null @@ -1,280 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> UDP_PROTOCOL = 0x11; -const bit<16> TYPE_IPV4 = 0x800; -const bit<5> IPV4_OPTION_MRI = 31; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; -typedef bit<32> switchID_t; -typedef bit<32> qdepth_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -header ipv4_option_t { - bit<1> copyFlag; - bit<2> optClass; - bit<5> option; - bit<8> optionLength; -} - -header mri_t { - bit<16> count; -} - -header switch_t { - switchID_t swid; - qdepth_t qdepth; -} - -struct ingress_metadata_t { - bit<16> count; -} - -struct parser_metadata_t { - bit<16> remaining; -} - -struct metadata { - ingress_metadata_t ingress_metadata; - parser_metadata_t parser_metadata; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - ipv4_option_t ipv4_option; - mri_t mri; - switch_t[MAX_HOPS] swtraces; -} - -error { IPHeaderTooShort } - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - verify(hdr.ipv4.ihl >= 5, error.IPHeaderTooShort); - transition select(hdr.ipv4.ihl) { - 5 : accept; - default : parse_ipv4_option; - } - } - - state parse_ipv4_option { - /* - * TODO: Add logic to: - * - Extract the ipv4_option header. - * - If value is equal to IPV4_OPTION_MRI, transition to parse_mri. - * - Otherwise, accept. - */ - transition accept; - } - - state parse_mri { - /* - * TODO: Add logic to: - * - Extract hdr.mri. - * - Set meta.parser_metadata.remaining to hdr.mri.count - * - Select on the value of meta.parser_metadata.remaining - * - If the value is equal to 0, accept. - * - Otherwise, transition to parse_swid. - */ - transition accept; - } - - state parse_swtrace { - /* - * TODO: Add logic to: - * - Extract hdr.swtraces.next. - * - Decrement meta.parser_metadata.remaining by 1 - * - Select on the value of meta.parser_metadata.remaining - * - If the value is equal to 0, accept. - * - Otherwise, transition to parse_swtrace. - */ - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action add_swtrace(switchID_t swid) { - /* - * TODO: add logic to: - - Increment hdr.mri.count by 1 - - Add a new swtrace header by calling push_front(1) on hdr.swtraces. - - Set hdr.swtraces[0].swid to the id paremeter - - Set hdr.swtraces[0].qdepth to (qdepth_t)standard_metadata.deq_qdepth - - Incremement hdr.ipv4.ihl by 2 - - Incrememtn hdr.ipv4_option.optionLength by 8 - */ - } - - table swtrace { - actions = { - /* TODO: add the correct action */ - NoAction; - } - - default_action = NoAction(); - } - - apply { - /* - * TODO: add logic to: - * - If hdr.mri is valid: - * - Apply table swtrace - */ - swtrace.apply(); - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - - /* TODO: emit ipv4_option, mri and swtraces headers */ - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/mri/p4app.json b/SIGCOMM_2017/exercises/mri/p4app.json deleted file mode 100644 index 2931e536b..000000000 --- a/SIGCOMM_2017/exercises/mri/p4app.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "program": "mri.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["h11", "s1"], ["s1", "s2", "0", 0.5], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s2", "h22"], ["s3", "h3"]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - }, - "h11": { - }, - "h22": { - } - - }, - "switches": { - "s1": { - "entries": "s1-commands.txt" - }, - "s2": { - "entries": "s2-commands.txt" - }, - "s3": { - "entries": "s3-commands.txt" - } - } - } - } -} diff --git a/SIGCOMM_2017/exercises/mri/receive.py b/SIGCOMM_2017/exercises/mri/receive.py deleted file mode 100755 index b3c3b3558..000000000 --- a/SIGCOMM_2017/exercises/mri/receive.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -import sys -import struct - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import PacketListField, ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class SwitchTrace(Packet): - fields_desc = [ IntField("swid", 0), - IntField("qdepth", 0)] - def extract_padding(self, p): - return "", p - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swtraces", - adjust=lambda pkt,l:l*2+4), - ShortField("count", 0), - PacketListField("swtraces", - [], - SwitchTrace, - count_from=lambda pkt:(pkt.count*1)) ] - -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - iface = 'h2-eth0' - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/mri/run.sh b/SIGCOMM_2017/exercises/mri/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/SIGCOMM_2017/exercises/mri/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/SIGCOMM_2017/exercises/mri/s1-commands.txt b/SIGCOMM_2017/exercises/mri/s1-commands.txt deleted file mode 100644 index d476195d7..000000000 --- a/SIGCOMM_2017/exercises/mri/s1-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 1 -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1 -table_add ipv4_lpm ipv4_forward 10.0.1.11/32 => 00:00:00:00:01:0b 2 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:02:00 4 diff --git a/SIGCOMM_2017/exercises/mri/s2-commands.txt b/SIGCOMM_2017/exercises/mri/s2-commands.txt deleted file mode 100644 index 915c888b2..000000000 --- a/SIGCOMM_2017/exercises/mri/s2-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 2 -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1 -table_add ipv4_lpm ipv4_forward 10.0.2.22/32 => 00:00:00:00:02:16 2 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:03:00 3 -table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:03:00 4 diff --git a/SIGCOMM_2017/exercises/mri/s3-commands.txt b/SIGCOMM_2017/exercises/mri/s3-commands.txt deleted file mode 100644 index d27b73c50..000000000 --- a/SIGCOMM_2017/exercises/mri/s3-commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -table_set_default ipv4_lpm drop -table_set_default swtrace add_swtrace 3 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:01 1 -table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:04:00 2 -table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:04:00 3 diff --git a/SIGCOMM_2017/exercises/mri/send.py b/SIGCOMM_2017/exercises/mri/send.py deleted file mode 100755 index 7b0aca7fc..000000000 --- a/SIGCOMM_2017/exercises/mri/send.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python - -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import Ether, IP, UDP -from scapy.all import IntField, FieldListField, FieldLenField, ShortField, PacketListField -from scapy.layers.inet import _IPOption_HDR - -from time import sleep - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class SwitchTrace(Packet): - fields_desc = [ IntField("swid", 0), - IntField("qdepth", 0)] - def extract_padding(self, p): - return "", p - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swtraces", - adjust=lambda pkt,l:l*2+4), - ShortField("count", 0), - PacketListField("swtraces", - [], - SwitchTrace, - count_from=lambda pkt:(pkt.count*1)) ] - - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP( - dst=addr, options = IPOption_MRI(count=0, - swtraces=[])) / UDP( - dport=4321, sport=1234) / sys.argv[2] - - # pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP( - # dst=addr, options = IPOption_MRI(count=2, - # swtraces=[SwitchTrace(swid=0,qdepth=0), SwitchTrace(swid=1,qdepth=0)])) / UDP( - # dport=4321, sport=1234) / sys.argv[2] - pkt.show2() - #hexdump(pkt) - try: - for i in range(int(sys.argv[3])): - sendp(pkt, iface=iface) - sleep(1) - except KeyboardInterrupt: - raise - - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/mri/solution/mri.p4 b/SIGCOMM_2017/exercises/mri/solution/mri.p4 deleted file mode 100644 index 116207c9f..000000000 --- a/SIGCOMM_2017/exercises/mri/solution/mri.p4 +++ /dev/null @@ -1,272 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<8> UDP_PROTOCOL = 0x11; -const bit<16> TYPE_IPV4 = 0x800; -const bit<5> IPV4_OPTION_MRI = 31; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; -typedef bit<32> switchID_t; -typedef bit<32> qdepth_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -header ipv4_option_t { - bit<1> copyFlag; - bit<2> optClass; - bit<5> option; - bit<8> optionLength; -} - -header mri_t { - bit<16> count; -} - -header switch_t { - switchID_t swid; - qdepth_t qdepth; -} - -struct ingress_metadata_t { - bit<16> count; -} - -struct parser_metadata_t { - bit<16> remaining; -} - -struct metadata { - ingress_metadata_t ingress_metadata; - parser_metadata_t parser_metadata; -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; - ipv4_option_t ipv4_option; - mri_t mri; - switch_t[MAX_HOPS] swtraces; -} - -error { IPHeaderTooShort } - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - verify(hdr.ipv4.ihl >= 5, error.IPHeaderTooShort); - transition select(hdr.ipv4.ihl) { - 5 : accept; - default : parse_ipv4_option; - } - } - - state parse_ipv4_option { - packet.extract(hdr.ipv4_option); - transition select(hdr.ipv4_option.option) { - IPV4_OPTION_MRI: parse_mri; - default: accept; - } - } - - state parse_mri { - packet.extract(hdr.mri); - meta.parser_metadata.remaining = hdr.mri.count; - transition select(meta.parser_metadata.remaining) { - 0 : accept; - default: parse_swtrace; - } - } - - state parse_swtrace { - packet.extract(hdr.swtraces.next); - meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1; - transition select(meta.parser_metadata.remaining) { - 0 : accept; - default: parse_swtrace; - } - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action add_swtrace(switchID_t swid) { - hdr.mri.count = hdr.mri.count + 1; - hdr.swtraces.push_front(1); - // According to the P4_16 spec, pushed elements are invalid, so we need - // to call setValid(). Older bmv2 versions would mark the new header(s) - // valid automatically (P4_14 behavior), but starting with version 1.11, - // bmv2 conforms with the P4_16 spec. - hdr.swtraces[0].setValid(); - hdr.swtraces[0].swid = swid; - hdr.swtraces[0].qdepth = (qdepth_t)standard_metadata.deq_qdepth; - - hdr.ipv4.ihl = hdr.ipv4.ihl + 2; - hdr.ipv4_option.optionLength = hdr.ipv4_option.optionLength + 8; - } - - table swtrace { - actions = { - add_swtrace; - NoAction; - } - default_action = NoAction(); - } - - apply { - if (hdr.mri.isValid()) { - swtrace.apply(); - } - } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.ipv4_option); - packet.emit(hdr.mri); - packet.emit(hdr.swtraces); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/scrambler/README.md b/SIGCOMM_2017/exercises/scrambler/README.md deleted file mode 100644 index e5ae8703f..000000000 --- a/SIGCOMM_2017/exercises/scrambler/README.md +++ /dev/null @@ -1,123 +0,0 @@ -# Implementing basic forwarding with scrambled addresses - -## Introduction - -In this exercise, you will extend your solution to the basic -forwarding exercise with a new twist: switches will invert the bits -representing Ethernet and IPv4 address. Hence, in our triangle -topology, the packets in the interior of the network will have -unintelligble addresses. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`scrambler.p4`, which initially drops all packets. Your job (in the -next step) will be to extend it to properly forward IPv4 packets. - -Before that, let's compile the incomplete `scrambler.p4` and bring -up a switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `scrambler.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle, each connected to one host (`h1`, `h2`, `h3`). - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc. - -2. You should now see a Mininet command prompt. Open two terminals -for `h1` and `h2`, respectively: - ```bash - mininet> xterm h1 h2 - ``` -3. Each host includes a small Python-based messaging client and -server. In `h2`'s xterm, start the server: - ```bash - ./receive.py - ``` -4. In `h1`'s xterm, send a message from the client: - ```bash - ./send.py 10.0.2.2 "P4 is cool" - ``` - The message will not be received. -5. Type `exit` to leave each xterm and the Mininet command line. - -The message was not received because each switch is programmed with -`scrambler.p4`, which drops all packets on arrival. Your job is to -extend this file. - -### A note about the control plane - -P4 programs define a packet-processing pipeline, but the rules -governing packet processing are inserted into the pipeline by the -control plane. When a rule matches a packet, its action is invoked -with parameters supplied by the control plane as part of the rule. - -In this exercise, the control plane logic has already been -implemented. As part of bringing up the Mininet instance, the -`run.sh` script will install packet-processing rules in the tables of -each switch. These are defined in the `sX-commands.txt` files, where -`X` corresponds to the switch number. - -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. The `sX-commands.txt` files -contain lists of commands for the BMv2 switch API. These commands -refer to specific tables, keys, and actions by name, and any changes -in the P4 program that add or rename tables, keys, or actions will -need to be reflected in these command files. - -## Step 2: Extend the basic forwarding solution to flip bits - -The `scrambler.p4` file contains a skeleton P4 program in which one of -the actions has a `TODO` comment. These should guide your -implementation---replace the `TODO` with logic implementing the -missing piece. - -A complete `scrambler.p4` will add an action `flip()` that inverts the -bits in the Ethernet and IPv4 headers. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, your message from -`h1` should be delivered to `h2`. - -### Troubleshooting - -There are several issues that might arise when developing your -solution: - -1. `scrambler.p4` fails to compile. In this case, `run.sh` will -report the error emitted from the compiler and stop. - -2. `scrambler.p4` compiles but does not support the control plane -rules in the `sX-commands.txt` files that `run.sh` tries to install -using the BMv2 CLI. In this case, `run.sh` will report these errors -to `stderr`. Use these error messages to fix your `scrambler.p4` -implementation. - -3. `scrambler.p4` compiles, and the control plane rules are installed, -but the switch does not process packets in the desired way. The -`build/logs/.log` files contain trace messages describing -how each switch processes each packet. The output is detailed and can -help pinpoint logic errors in your implementation. - -#### Cleaning up Mininet - -In the latter two cases above, `run.sh` may leave a Mininet instance -running in the background. Use the following command to clean up -these instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to the next -exercise: implementing [Explicit Congestion Notification](../ecn). diff --git a/SIGCOMM_2017/exercises/scrambler/p4app.json b/SIGCOMM_2017/exercises/scrambler/p4app.json deleted file mode 100644 index 521560582..000000000 --- a/SIGCOMM_2017/exercises/scrambler/p4app.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "program": "scrambler.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["s1", "s2"], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s3", "h3"]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - } - - }, - "switches": { - "s1": { - "entries": "s1-commands.txt" - }, - "s2": { - "entries": "s2-commands.txt" - }, - "s3": { - "entries": "s3-commands.txt" - } - } - } - } -} diff --git a/SIGCOMM_2017/exercises/scrambler/receive.py b/SIGCOMM_2017/exercises/scrambler/receive.py deleted file mode 100755 index c93182f89..000000000 --- a/SIGCOMM_2017/exercises/scrambler/receive.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -import sys -import struct -import os - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr -from scapy.all import Packet, IPOption -from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField -from scapy.all import IP, UDP, Raw -from scapy.layers.inet import _IPOption_HDR - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - - -def main(): - ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/')) - iface = ifaces[0] - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="tcp", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/scrambler/run.sh b/SIGCOMM_2017/exercises/scrambler/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/SIGCOMM_2017/exercises/scrambler/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/SIGCOMM_2017/exercises/scrambler/s1-commands.txt b/SIGCOMM_2017/exercises/scrambler/s1-commands.txt deleted file mode 100644 index 0a63bdb26..000000000 --- a/SIGCOMM_2017/exercises/scrambler/s1-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:02:00 3 -table_add ipv4_lpm ipv4_forward 245.255.254.254/32 => ff:ff:ff:ff:fe:fe 1 -table_add ipv4_lpm ipv4_forward 245.255.253.253/32 => 00:00:00:02:02:00 2 -table_add ipv4_lpm ipv4_forward 245.255.252.252/32 => 00:00:00:03:02:00 3 diff --git a/SIGCOMM_2017/exercises/scrambler/s2-commands.txt b/SIGCOMM_2017/exercises/scrambler/s2-commands.txt deleted file mode 100644 index f1797a610..000000000 --- a/SIGCOMM_2017/exercises/scrambler/s2-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 -table_add ipv4_lpm ipv4_forward 245.255.254.254/32 => 00:00:00:01:02:00 2 -table_add ipv4_lpm ipv4_forward 245.255.253.253/32 => ff:ff:ff:ff:fd:fd 1 -table_add ipv4_lpm ipv4_forward 245.255.252.252/32 => 00:00:00:03:03:00 3 diff --git a/SIGCOMM_2017/exercises/scrambler/s3-commands.txt b/SIGCOMM_2017/exercises/scrambler/s3-commands.txt deleted file mode 100644 index a04f7bae7..000000000 --- a/SIGCOMM_2017/exercises/scrambler/s3-commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default ipv4_lpm drop -table_add ipv4_lpm ipv4_forward 10.0.0.1/32 => 00:00:00:01:02:00 2 -table_add ipv4_lpm ipv4_forward 10.0.0.2/32 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 245.255.254.254/32 => 00:00:00:01:01:00 2 -table_add ipv4_lpm ipv4_forward 245.255.253.253/32 => 00:00:00:02:03:00 3 -table_add ipv4_lpm ipv4_forward 245.255.252.252/32 => ff:ff:ff:ff:fc:fc 1 diff --git a/SIGCOMM_2017/exercises/scrambler/scrambler.p4 b/SIGCOMM_2017/exercises/scrambler/scrambler.p4 deleted file mode 100644 index 8d4e11503..000000000 --- a/SIGCOMM_2017/exercises/scrambler/scrambler.p4 +++ /dev/null @@ -1,180 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action flip() { - /* TODO: add code to flip bits in Ethernet and IPv4 addresses. */ - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - flip(); - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/scrambler/send.py b/SIGCOMM_2017/exercises/scrambler/send.py deleted file mode 100755 index 00496d9de..000000000 --- a/SIGCOMM_2017/exercises/scrambler/send.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr -from scapy.all import Packet -from scapy.all import Ether, IP, UDP, TCP - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -def main(): - - if len(sys.argv)<3: - print 'pass 2 arguments: ""' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - - print "sending on interface %s to %s" % (iface, str(addr)) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2] - pkt.show2() - sendp(pkt, iface=iface, verbose=False) - - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/scrambler/solution/scrambler.p4 b/SIGCOMM_2017/exercises/scrambler/solution/scrambler.p4 deleted file mode 100644 index e508af6ac..000000000 --- a/SIGCOMM_2017/exercises/scrambler/solution/scrambler.p4 +++ /dev/null @@ -1,191 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_IPV4: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - action drop() { - mark_to_drop(); - } - - action flip_ethernet() { - hdr.ethernet.srcAddr = ~hdr.ethernet.srcAddr; - hdr.ethernet.dstAddr = ~hdr.ethernet.dstAddr; - } - action flip_ipv4() { - hdr.ipv4.srcAddr = ~hdr.ipv4.srcAddr; - hdr.ipv4.dstAddr = ~hdr.ipv4.dstAddr; - } - - action flip() { - flip_ethernet(); - flip_ipv4(); - } - - action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { - standard_metadata.egress_spec = port; - hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; - hdr.ethernet.dstAddr = dstAddr; - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - flip(); - } - - table ipv4_lpm { - key = { - hdr.ipv4.dstAddr: lpm; - } - actions = { - ipv4_forward; - drop; - NoAction; - } - size = 1024; - default_action = NoAction(); - } - - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, - hdr.ipv4.ihl, - hdr.ipv4.diffserv, - hdr.ipv4.totalLen, - hdr.ipv4.identification, - hdr.ipv4.flags, - hdr.ipv4.fragOffset, - hdr.ipv4.ttl, - hdr.ipv4.protocol, - hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/source_routing/README.md b/SIGCOMM_2017/exercises/source_routing/README.md deleted file mode 100644 index 003e7a701..000000000 --- a/SIGCOMM_2017/exercises/source_routing/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# Implementing Source Routing - -## Introduction - -The objective of this exercise is to implement source routing. With -source routing, the source host guides each switch in the network to -send the packet to a specific port. The host puts a stack of output -ports in the packet. In this example, we just put the stack after -Ethernet header and select a special etherType to indicate that. Each -switch pops an item from the stack and forwards the packet according -to the specified port number. - -Your switch must parse the source routing stack. Each item has a bos -(bottom of stack) bit and a port number. The bos bit is 1 only for the -last entry of stack. Then at ingress, it should pop an entry from the -stack and set the egress port accordingly. Note that the last hop can -also revert back the etherType to `TYPE_IPV4`. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`source_routing.p4`, which initially drops all packets. Your job (in -the next step) will be to extend it to properly to route packets. - -Before that, let's compile the incomplete `source_routing.p4` and -bring up a network in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * compile `source_routing.p4`, and - * start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured - in a triangle, each connected to one host (`h1`, `h2`, `h3`). - Check the network topology using the `net` command in mininet. - You can also change the topology in p4app.json - * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc - (`10.0..`). - -2. You should now see a Mininet command prompt. Open two terminals for - `h1` and `h2`, respectively: - ```bash - mininet> xterm h1 h2 - ``` -3. Each host includes a small Python-based messaging client and - server. In `h2`'s xterm, start the server: - ```bash - ./receive.py - ``` -4. In `h1`'s xterm, send a message from the client: - ```bash - ./send.py 10.0.2.2 - ``` - -5. Type a list of port numbers. say `2 3 2 2 1`. This should send the - packet through `h1`, `s1`, `s2`, `s3`, `s1`, `s2`, and - `h2`. However, `h2` will not receive the message. - -6. Type `q` to exit send.py and type `exit` to leave each xterm and - the Mininet command line. - -The message was not received because each switch is programmed with -`source_routing.p4`, which drops all packets on arrival. You can -verify this by looking at `build/logs/s1.log`. Your job is to extend -the P4 code so packets are delivered to their destination. - -## Step 2: Implement source routing - -The `source_routing.p4` file contains a skeleton P4 program with key -pieces of logic replaced by `TODO` comments. These should guide your -implementation---replace each `TODO` with logic implementing the -missing piece. - -A complete `source_routing.p4` will contain the following components: - -1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 - (`ipv4_t`) and Source Route (`srcRoute_t`). -2. **TODO:** Parsers for Ethernet and Source Route that populate - `ethernet` and `srcRoutes` fields. -3. An action to drop a packet, using `mark_to_drop()`. -4. **TODO:** An action (called `srcRoute_nhop`), which will: - 1. Set the egress port for the next hop. - 2. remove the first entry of srcRoutes -5. A control with an `apply` block that: - 1. checks the existance of source routes. - 2. **TODO:** if statement to change etherent.etherType if it is the last hop - 3. **TODO:** call srcRoute_nhop action -6. A deparser that selects the order in which fields inserted into the outgoing - packet. -7. A `package` instantiation supplied with the parser, control, and deparser. - > In general, a package also requires instances of checksum verification - > and recomputation controls. These are not necessary for this tutorial - > and are replaced with instantiations of empty controls. - -## Step 3: Run your solution - -Follow the instructions from Step 1. This time, your message from `h1` -should be delivered to `h2`. - -Check the `ttl` of the IP header. Each hop decrements `ttl`. The port -sequence `2 3 2 2 1`, forces the packet to have a loop, so the `ttl` -should be 59 at `h2`. Can you find the port sequence for the shortest -path? - -### Food for thought -* Can we change the program to handle both IPv4 forwarding and source -routing at the same time? -* How would you enhance your program to let the first switch add the -path, so that source routing would be transparent to end-hosts? - -### Troubleshooting - -There are several ways that problems might manifest: - -1. `source_routing.p4` fails to compile. In this case, `run.sh` will - report the error emitted from the compiler and stop. -2. `source_routing.p4` compiles but switches or mininet do not start. - Do you have another instance of mininet running? Did the previous - run of mininet crash? if yes, check "Cleaning up Mininet" bellow. -3. `source_routing.p4` compiles but the switch does not process - packets in the desired way. The `build/logs/.log` - files contain trace messages describing how each switch processes - each packet. The output is detailed and can help pinpoint logic - errors in your implementation. The - `build/-.pcap` also contains the pcap - of packets on each interface. Use `tcpdump -r -xxx` to - print the hexdump of the packets. - -#### Cleaning up Mininet - -In the cases above, `run.sh` may leave a Mininet instance running in -the background. Use the following command to clean up these -instances: - -```bash -mn -c -``` - -## Next Steps - -Congratulations, your implementation works! Move on to -[Calculator](../calc). diff --git a/SIGCOMM_2017/exercises/source_routing/p4app.json b/SIGCOMM_2017/exercises/source_routing/p4app.json deleted file mode 100644 index 650886827..000000000 --- a/SIGCOMM_2017/exercises/source_routing/p4app.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "program": "source_routing.p4", - "language": "p4-16", - "targets": { - "multiswitch": { - "auto-control-plane": true, - "cli": true, - "pcap_dump": true, - "bmv2_log": true, - "links": [["h1", "s1"], ["s1", "s2"], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s3", "h3"]], - "hosts": { - "h1": { - }, - "h2": { - }, - "h3": { - } - - }, - "switches": { - "s1": { - }, - "s2": { - }, - "s3": { - } - } - } - } -} diff --git a/SIGCOMM_2017/exercises/source_routing/receive.py b/SIGCOMM_2017/exercises/source_routing/receive.py deleted file mode 100755 index efb93d691..000000000 --- a/SIGCOMM_2017/exercises/source_routing/receive.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -import sys -import struct - -from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr, bind_layers -from scapy.all import Packet, IPOption -from scapy.all import IP, UDP, Raw, Ether -from scapy.layers.inet import _IPOption_HDR -from scapy.fields import * - -def get_if(): - ifs=get_if_list() - iface=None - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class IPOption_MRI(IPOption): - name = "MRI" - option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] -def handle_pkt(pkt): - print "got a packet" - pkt.show2() -# hexdump(pkt) - sys.stdout.flush() - -class SourceRoute(Packet): - fields_desc = [ BitField("bos", 0, 1), - BitField("port", 0, 15)] -class SourceRoutingTail(Packet): - fields_desc = [ XShortField("etherType", 0x800)] - -bind_layers(Ether, SourceRoute, type=0x1234) -bind_layers(SourceRoute, SourceRoute, bos=0) -bind_layers(SourceRoute, SourceRoutingTail, bos=1) - -def main(): - iface = 'h2-eth0' - print "sniffing on %s" % iface - sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/source_routing/run.sh b/SIGCOMM_2017/exercises/source_routing/run.sh deleted file mode 100755 index d5c1947cc..000000000 --- a/SIGCOMM_2017/exercises/source_routing/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -P4APPRUNNER=../../utils/p4apprunner.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/SIGCOMM_2017/exercises/source_routing/send.py b/SIGCOMM_2017/exercises/source_routing/send.py deleted file mode 100755 index 21331dba4..000000000 --- a/SIGCOMM_2017/exercises/source_routing/send.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python -import argparse -import sys -import socket -import random -import struct - -from scapy.all import sendp, send, get_if_list, get_if_hwaddr, bind_layers -from scapy.all import Packet -from scapy.all import Ether, IP, UDP -from scapy.fields import * -import readline - -def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" - for i in get_if_list(): - if "eth0" in i: - iface=i - break; - if not iface: - print "Cannot find eth0 interface" - exit(1) - return iface - -class SourceRoute(Packet): - fields_desc = [ BitField("bos", 0, 1), - BitField("port", 0, 15)] - -bind_layers(Ether, SourceRoute, type=0x1234) -bind_layers(SourceRoute, SourceRoute, bos=0) -bind_layers(SourceRoute, IP, bos=1) - -def main(): - - if len(sys.argv)<2: - print 'pass 2 arguments: ' - exit(1) - - addr = socket.gethostbyname(sys.argv[1]) - iface = get_if() - print "sending on interface %s to %s" % (iface, str(addr)) - - while True: - print - s = str(raw_input('Type space separated port nums ' - '(example: "2 3 2 2 1") or "q" to quit: ')) - if s == "q": - break; - print - - i = 0 - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff'); - for p in s.split(" "): - try: - pkt = pkt / SourceRoute(bos=0, port=int(p)) - i = i+1 - except ValueError: - pass - if pkt.haslayer(SourceRoute): - pkt.getlayer(SourceRoute, i).bos = 1 - - pkt = pkt / IP(dst=addr) / UDP(dport=4321, sport=1234) - pkt.show2() - sendp(pkt, iface=iface, verbose=False) - - #pkt = pkt / SourceRoute(bos=0, port=2) / SourceRoute(bos=0, port=3); - #pkt = pkt / SourceRoute(bos=0, port=2) / SourceRoute(bos=0, port=2); - #pkt = pkt / SourceRoute(bos=1, port=1) - - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/exercises/source_routing/solution/source_routing.p4 b/SIGCOMM_2017/exercises/source_routing/solution/source_routing.p4 deleted file mode 100644 index 30c497b59..000000000 --- a/SIGCOMM_2017/exercises/source_routing/solution/source_routing.p4 +++ /dev/null @@ -1,181 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; -const bit<16> TYPE_SRCROUTING = 0x1234; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header srcRoute_t { - bit<1> bos; - bit<15> port; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - srcRoute_t[MAX_HOPS] srcRoutes; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - TYPE_SRCROUTING: parse_srcRouting; - default: accept; - } - } - - state parse_srcRouting { - packet.extract(hdr.srcRoutes.next); - transition select(hdr.srcRoutes.last.bos) { - 1: parse_ipv4; - default: parse_srcRouting; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action drop() { - mark_to_drop(); - } - - action srcRoute_nhop() { - standard_metadata.egress_spec = (bit<9>)hdr.srcRoutes[0].port; - hdr.srcRoutes.pop_front(1); - } - - action srcRoute_finish() { - hdr.ethernet.etherType = TYPE_IPV4; - } - - action update_ttl(){ - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - apply { - if (hdr.srcRoutes[0].isValid()){ - if (hdr.srcRoutes[0].bos == 1){ - srcRoute_finish(); - } - srcRoute_nhop(); - if (hdr.ipv4.isValid()){ - update_ttl(); - } - }else{ - drop(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.srcRoutes); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/exercises/source_routing/source_routing.p4 b/SIGCOMM_2017/exercises/source_routing/source_routing.p4 deleted file mode 100644 index a84479e87..000000000 --- a/SIGCOMM_2017/exercises/source_routing/source_routing.p4 +++ /dev/null @@ -1,191 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -const bit<16> TYPE_IPV4 = 0x800; -const bit<16> TYPE_SRCROUTING = 0x1234; - -#define MAX_HOPS 9 - -/************************************************************************* -*********************** H E A D E R S *********************************** -*************************************************************************/ - -typedef bit<9> egressSpec_t; -typedef bit<48> macAddr_t; -typedef bit<32> ip4Addr_t; - -header ethernet_t { - macAddr_t dstAddr; - macAddr_t srcAddr; - bit<16> etherType; -} - -header srcRoute_t { - bit<1> bos; - bit<15> port; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - ip4Addr_t srcAddr; - ip4Addr_t dstAddr; -} - -struct metadata { - /* empty */ -} - -struct headers { - ethernet_t ethernet; - srcRoute_t[MAX_HOPS] srcRoutes; - ipv4_t ipv4; -} - -/************************************************************************* -*********************** P A R S E R *********************************** -*************************************************************************/ - -parser MyParser(packet_in packet, - out headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - /* - * TODO: Modify the next line to select on hdr.ethernet.etherType - * If the value is TYPE_SRCROUTING transition to parse_srcRouting - * otherwise transition to accept. - */ - transition accept; - } - - state parse_srcRouting { - /* - * TODO: extract the next entry of hdr.srcRoutes - * while hdr.srcRoutes.last.bos is 0 transition to this state - * otherwise parse ipv4 - */ - transition accept; - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition accept; - } - -} - - -/************************************************************************* -************ C H E C K S U M V E R I F I C A T I O N ************* -*************************************************************************/ - -control MyVerifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - - -/************************************************************************* -************** I N G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyIngress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - action drop() { - mark_to_drop(); - } - - action srcRoute_nhop() { - /* - * TODO: set standard_metadata.egress_spec - * to the port in hdr.srcRoutes[0] and - * pop an entry from hdr.srcRoutes - */ - } - - action srcRoute_finish() { - hdr.ethernet.etherType = TYPE_IPV4; - } - - action update_ttl(){ - hdr.ipv4.ttl = hdr.ipv4.ttl - 1; - } - - apply { - if (hdr.srcRoutes[0].isValid()){ - /* - * TODO: add logic to: - * - If final srcRoutes (top of stack has bos==1): - * - change etherType to IP - * - choose next hop and remove top of srcRoutes stack - */ - - if (hdr.ipv4.isValid()){ - update_ttl(); - } - }else{ - drop(); - } - } -} - -/************************************************************************* -**************** E G R E S S P R O C E S S I N G ******************* -*************************************************************************/ - -control MyEgress(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - apply { } -} - -/************************************************************************* -************* C H E C K S U M C O M P U T A T I O N ************** -*************************************************************************/ - -control MyComputeChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -/************************************************************************* -*********************** D E P A R S E R ******************************* -*************************************************************************/ - -control MyDeparser(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.srcRoutes); - packet.emit(hdr.ipv4); - } -} - -/************************************************************************* -*********************** S W I T C H ******************************* -*************************************************************************/ - -V1Switch( -MyParser(), -MyVerifyChecksum(), -MyIngress(), -MyEgress(), -MyComputeChecksum(), -MyDeparser() -) main; diff --git a/SIGCOMM_2017/utils/mininet/appcontroller.py b/SIGCOMM_2017/utils/mininet/appcontroller.py deleted file mode 100644 index 2cf97323b..000000000 --- a/SIGCOMM_2017/utils/mininet/appcontroller.py +++ /dev/null @@ -1,104 +0,0 @@ -import subprocess - -from shortest_path import ShortestPath - -class AppController: - - def __init__(self, manifest=None, target=None, topo=None, net=None, links=None): - self.manifest = manifest - self.target = target - self.conf = manifest['targets'][target] - self.topo = topo - self.net = net - self.links = links - - def read_entries(self, filename): - entries = [] - with open(filename, 'r') as f: - for line in f: - line = line.strip() - if line == '': continue - entries.append(line) - return entries - - def add_entries(self, thrift_port=9090, sw=None, entries=None): - assert entries - if sw: thrift_port = sw.thrift_port - - print '\n'.join(entries) - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE) - p.communicate(input='\n'.join(entries)) - - def read_register(self, register, idx, thrift_port=9090, sw=None): - if sw: thrift_port = sw.thrift_port - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate(input="register_read %s %d" % (register, idx)) - reg_val = filter(lambda l: ' %s[%d]' % (register, idx) in l, stdout.split('\n'))[0].split('= ', 1)[1] - return long(reg_val) - - def start(self): - shortestpath = ShortestPath(self.links) - entries = {} - for sw in self.topo.switches(): - entries[sw] = [] - if 'switches' in self.conf and sw in self.conf['switches'] and 'entries' in self.conf['switches'][sw]: - extra_entries = self.conf['switches'][sw]['entries'] - if type(extra_entries) == list: # array of entries - entries[sw] += extra_entries - else: # path to file that contains entries - entries[sw] += self.read_entries(extra_entries) - #entries[sw] += [ - # 'table_set_default send_frame _drop', - # 'table_set_default forward _drop', - # 'table_set_default ipv4_lpm _drop'] - - for host_name in self.topo._host_links: - h = self.net.get(host_name) - for link in self.topo._host_links[host_name].values(): - sw = link['sw'] - #entries[sw].append('table_add send_frame rewrite_mac %d => %s' % (link['sw_port'], link['sw_mac'])) - #entries[sw].append('table_add forward set_dmac %s => %s' % (link['host_ip'], link['host_mac'])) - #entries[sw].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (link['host_ip'], link['host_ip'], link['sw_port'])) - iface = h.intfNames()[link['idx']] - # use mininet to set ip and mac to let it know the change - h.setIP(link['host_ip'], 24) - h.setMAC(link['host_mac']) - #h.cmd('ifconfig %s %s hw ether %s' % (iface, link['host_ip'], link['host_mac'])) - h.cmd('arp -i %s -s %s %s' % (iface, link['sw_ip'], link['sw_mac'])) - h.cmd('ethtool --offload %s rx off tx off' % iface) - h.cmd('ip route add %s dev %s' % (link['sw_ip'], iface)) - h.setDefaultRoute("via %s" % link['sw_ip']) - - for h in self.net.hosts: - h_link = self.topo._host_links[h.name].values()[0] - for sw in self.net.switches: - path = shortestpath.get(sw.name, h.name, exclude=lambda n: n[0]=='h') - if not path: continue - if not path[1][0] == 's': continue # next hop is a switch - sw_link = self.topo._sw_links[sw.name][path[1]] - #entries[sw.name].append('table_add send_frame rewrite_mac %d => %s' % (sw_link[0]['port'], sw_link[0]['mac'])) - #entries[sw.name].append('table_add forward set_dmac %s => %s' % (h_link['host_ip'], sw_link[1]['mac'])) - #entries[sw.name].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (h_link['host_ip'], h_link['host_ip'], sw_link[0]['port'])) - - for h2 in self.net.hosts: - if h == h2: continue - path = shortestpath.get(h.name, h2.name, exclude=lambda n: n[0]=='h') - if not path: continue - h_link = self.topo._host_links[h.name][path[1]] - h2_link = self.topo._host_links[h2.name].values()[0] - h.cmd('ip route add %s via %s' % (h2_link['host_ip'], h_link['sw_ip'])) - - - print "**********" - print "Configuring entries in p4 tables" - for sw_name in entries: - print - print "Configuring switch... %s" % sw_name - sw = self.net.get(sw_name) - if entries[sw_name]: - self.add_entries(sw=sw, entries=entries[sw_name]) - print "Configuration complete." - print "**********" - - def stop(self): - pass diff --git a/SIGCOMM_2017/utils/mininet/apptopo.py b/SIGCOMM_2017/utils/mininet/apptopo.py deleted file mode 100644 index 3491a3d29..000000000 --- a/SIGCOMM_2017/utils/mininet/apptopo.py +++ /dev/null @@ -1,70 +0,0 @@ -from mininet.topo import Topo - -class AppTopo(Topo): - - def __init__(self, links, latencies={}, manifest=None, target=None, - log_dir="/tmp", bws={}, **opts): - Topo.__init__(self, **opts) - - nodes = sum(map(list, zip(*links)), []) - host_names = sorted(list(set(filter(lambda n: n[0] == 'h', nodes)))) - sw_names = sorted(list(set(filter(lambda n: n[0] == 's', nodes)))) - sw_ports = dict([(sw, []) for sw in sw_names]) - - self._host_links = {} - self._sw_links = dict([(sw, {}) for sw in sw_names]) - - for sw_name in sw_names: - self.addSwitch(sw_name, log_file="%s/%s.log" %(log_dir, sw_name)) - - for host_name in host_names: - host_num = int(host_name[1:]) - - self.addHost(host_name) - - self._host_links[host_name] = {} - host_links = filter(lambda l: l[0]==host_name or l[1]==host_name, links) - - sw_idx = 0 - for link in host_links: - sw = link[0] if link[0] != host_name else link[1] - sw_num = int(sw[1:]) - assert sw[0]=='s', "Hosts should be connected to switches, not " + str(sw) - host_ip = "10.0.%d.%d" % (sw_num, host_num) - host_mac = '00:00:00:00:%02x:%02x' % (sw_num, host_num) - delay_key = ''.join([host_name, sw]) - delay = latencies[delay_key] if delay_key in latencies else '0ms' - bw = bws[delay_key] if delay_key in bws else None - sw_ports[sw].append(host_name) - self._host_links[host_name][sw] = dict( - idx=sw_idx, - host_mac = host_mac, - host_ip = host_ip, - sw = sw, - sw_mac = "00:00:00:00:%02x:%02x" % (sw_num, host_num), - sw_ip = "10.0.%d.%d" % (sw_num, 254), - sw_port = sw_ports[sw].index(host_name)+1 - ) - self.addLink(host_name, sw, delay=delay, bw=bw, - addr1=host_mac, addr2=self._host_links[host_name][sw]['sw_mac']) - sw_idx += 1 - - for link in links: # only check switch-switch links - sw1, sw2 = link - if sw1[0] != 's' or sw2[0] != 's': continue - - delay_key = ''.join(sorted([sw1, sw2])) - delay = latencies[delay_key] if delay_key in latencies else '0ms' - bw = bws[delay_key] if delay_key in bws else None - - self.addLink(sw1, sw2, delay=delay, bw=bw)#, max_queue_size=10) - sw_ports[sw1].append(sw2) - sw_ports[sw2].append(sw1) - - sw1_num, sw2_num = int(sw1[1:]), int(sw2[1:]) - sw1_port = dict(mac="00:00:00:%02x:%02x:00" % (sw1_num, sw2_num), port=sw_ports[sw1].index(sw2)+1) - sw2_port = dict(mac="00:00:00:%02x:%02x:00" % (sw2_num, sw1_num), port=sw_ports[sw2].index(sw1)+1) - - self._sw_links[sw1][sw2] = [sw1_port, sw2_port] - self._sw_links[sw2][sw1] = [sw2_port, sw1_port] - diff --git a/SIGCOMM_2017/utils/mininet/multi_switch_mininet.py b/SIGCOMM_2017/utils/mininet/multi_switch_mininet.py deleted file mode 100755 index 0bb406f61..000000000 --- a/SIGCOMM_2017/utils/mininet/multi_switch_mininet.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/env python2 - -# 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. -# - -import signal -import os -import sys -import subprocess -import argparse -import json -import importlib -import re -from time import sleep - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.link import TCLink -from mininet.log import setLogLevel, info -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host -import apptopo -import appcontroller - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--bmv2-log', help='verbose messages in log file', action="store_true") -parser.add_argument('--cli', help="start the mininet cli", action="store_true") -parser.add_argument('--auto-control-plane', help='enable automatic control plane population', action="store_true") -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - action="store_true") -parser.add_argument('--manifest', '-m', help='Path to manifest file', - type=str, action="store", required=True) -parser.add_argument('--target', '-t', help='Target in manifest file to run', - type=str, action="store", required=True) -parser.add_argument('--log-dir', '-l', help='Location to save output to', - type=str, action="store", required=True) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) - - -args = parser.parse_args() - - -next_thrift_port = args.thrift_port - -def run_command(command): - return os.WEXITSTATUS(os.system(command)) - -def configureP4Switch(**switch_args): - class ConfiguredP4Switch(P4Switch): - def __init__(self, *opts, **kwargs): - global next_thrift_port - kwargs.update(switch_args) - kwargs['thrift_port'] = next_thrift_port - next_thrift_port += 1 - P4Switch.__init__(self, *opts, **kwargs) - return ConfiguredP4Switch - - -def main(): - - with open(args.manifest, 'r') as f: - manifest = json.load(f) - - conf = manifest['targets'][args.target] - params = conf['parameters'] if 'parameters' in conf else {} - - os.environ.update(dict(map(lambda (k,v): (k, str(v)), params.iteritems()))) - - def formatParams(s): - for param in params: - s = re.sub('\$'+param+'(\W|$)', str(params[param]) + r'\1', s) - s = s.replace('${'+param+'}', str(params[param])) - return s - - AppTopo = apptopo.AppTopo - AppController = appcontroller.AppController - - if 'topo_module' in conf: - sys.path.insert(0, os.path.dirname(args.manifest)) - topo_module = importlib.import_module(conf['topo_module']) - AppTopo = topo_module.CustomAppTopo - - if 'controller_module' in conf: - sys.path.insert(0, os.path.dirname(args.manifest)) - controller_module = importlib.import_module(conf['controller_module']) - AppController = controller_module.CustomAppController - - if not os.path.isdir(args.log_dir): - if os.path.exists(args.log_dir): raise Exception('Log dir exists and is not a dir') - os.mkdir(args.log_dir) - os.environ['P4APP_LOGDIR'] = args.log_dir - - - links = [l[:2] for l in conf['links']] - latencies = dict([(''.join(sorted(l[:2])), l[2]) for l in conf['links'] if len(l)>=3]) - bws = dict([(''.join(sorted(l[:2])), l[3]) for l in conf['links'] if len(l)>=4]) - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'latency' not in host: continue - for a, b in links: - if a != host_name and b != host_name: continue - other = a if a != host_name else b - latencies[host_name+other] = host['latency'] - - for l in latencies: - if isinstance(latencies[l], (str, unicode)): - latencies[l] = formatParams(latencies[l]) - else: - latencies[l] = str(latencies[l]) + "ms" - - bmv2_log = args.bmv2_log or ('bmv2_log' in conf and conf['bmv2_log']) - pcap_dump = args.pcap_dump or ('pcap_dump' in conf and conf['pcap_dump']) - - topo = AppTopo(links, latencies, manifest=manifest, target=args.target, - log_dir=args.log_dir, bws=bws) - switchClass = configureP4Switch( - sw_path=args.behavioral_exe, - json_path=args.json, - log_console=bmv2_log, - pcap_dump=pcap_dump) - net = Mininet(topo = topo, - link = TCLink, - host = P4Host, - switch = switchClass, - controller = None) - net.start() - - sleep(1) - - controller = None - if args.auto_control_plane or 'controller_module' in conf: - controller = AppController(manifest=manifest, target=args.target, - topo=topo, net=net, links=links) - controller.start() - - - for h in net.hosts: - h.describe() - - if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: - print message_file.read() - - if args.cli or ('cli' in conf and conf['cli']): - CLI(net) - - stdout_files = dict() - return_codes = [] - host_procs = [] - - - def formatCmd(cmd): - for h in net.hosts: - cmd = cmd.replace(h.name, h.defaultIntf().updateIP()) - return cmd - - def _wait_for_exit(p, host): - print p.communicate() - if p.returncode is None: - p.wait() - print p.communicate() - return_codes.append(p.returncode) - if host_name in stdout_files: - stdout_files[host_name].flush() - stdout_files[host_name].close() - - print '\n'.join(map(lambda (k,v): "%s: %s"%(k,v), params.iteritems())) + '\n' - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'cmd' not in host: continue - - h = net.get(host_name) - stdout_filename = os.path.join(args.log_dir, h.name + '.stdout') - stdout_files[h.name] = open(stdout_filename, 'w') - cmd = formatCmd(host['cmd']) - print h.name, cmd - p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, preexec_fn=os.setpgrp) - if 'startup_sleep' in host: sleep(host['startup_sleep']) - - if 'wait' in host and host['wait']: - _wait_for_exit(p, host_name) - else: - host_procs.append((p, host_name)) - - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: - _wait_for_exit(p, host_name) - - - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: - continue - if p.returncode is None: - run_command('pkill -INT -P %d' % p.pid) - sleep(0.2) - rc = run_command('pkill -0 -P %d' % p.pid) # check if it's still running - if rc == 0: # the process group is still running, send TERM - sleep(1) # give it a little more time to exit gracefully - run_command('pkill -TERM -P %d' % p.pid) - _wait_for_exit(p, host_name) - - if 'after' in conf and 'cmd' in conf['after']: - cmds = conf['after']['cmd'] if type(conf['after']['cmd']) == list else [conf['after']['cmd']] - for cmd in cmds: - os.system(cmd) - - if controller: controller.stop() - - net.stop() - -# if bmv2_log: -# os.system('bash -c "cp /tmp/p4s.s*.log \'%s\'"' % args.log_dir) -# if pcap_dump: -# os.system('bash -c "cp *.pcap \'%s\'"' % args.log_dir) - - bad_codes = [rc for rc in return_codes if rc != 0] - if len(bad_codes): sys.exit(1) - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/SIGCOMM_2017/utils/mininet/p4_mininet.py b/SIGCOMM_2017/utils/mininet/p4_mininet.py deleted file mode 100644 index 8abe79f3a..000000000 --- a/SIGCOMM_2017/utils/mininet/p4_mininet.py +++ /dev/null @@ -1,161 +0,0 @@ -# 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. -# - -from mininet.net import Mininet -from mininet.node import Switch, Host -from mininet.log import setLogLevel, info, error, debug -from mininet.moduledeps import pathCheck -from sys import exit -from time import sleep -import os -import tempfile -import socket - -class P4Host(Host): - def config(self, **params): - r = super(P4Host, self).config(**params) - - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf().name, off) - self.cmd(cmd) - - # disable IPv6 - self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - - return r - - def describe(self, sw_addr=None, sw_mac=None): - print "**********" - print "Network configuration for: %s" % self.name - print "Default interface: %s\t%s\t%s" %( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC() - ) - if sw_addr is not None or sw_mac is not None: - print "Default route to switch: %s (%s)" % (sw_addr, sw_mac) - print "**********" - -class P4Switch(Switch): - """P4 virtual switch""" - device_id = 0 - - def __init__(self, name, sw_path = None, json_path = None, - log_file = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): - Switch.__init__(self, name, **kwargs) - assert(sw_path) - assert(json_path) - # make sure that the provided sw_path is valid - pathCheck(sw_path) - # make sure that the provided JSON file exists - if not os.path.isfile(json_path): - error("Invalid JSON file.\n") - exit(1) - self.sw_path = sw_path - self.json_path = json_path - self.verbose = verbose - self.log_file = log_file - if self.log_file is None: - self.log_file = "/tmp/p4s.{}.log".format(self.name) - self.output = open(self.log_file, 'w') - self.thrift_port = thrift_port - self.pcap_dump = pcap_dump - self.enable_debugger = enable_debugger - self.log_console = log_console - if device_id is not None: - self.device_id = device_id - P4Switch.device_id = max(P4Switch.device_id, device_id) - else: - self.device_id = P4Switch.device_id - P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) - - @classmethod - def setup(cls): - pass - - def check_switch_started(self, pid): - """While the process is running (pid exists), we check if the Thrift - server has been started. If the Thrift server is ready, we assume that - the switch was started successfully. This is only reliable if the Thrift - server is started at the end of the init process""" - while True: - if not os.path.exists(os.path.join("/proc", str(pid))): - return False - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(0.5) - result = sock.connect_ex(("localhost", self.thrift_port)) - if result == 0: - return True - - def start(self, controllers): - "Start up a new P4 switch" - info("Starting P4 switch {}.\n".format(self.name)) - args = [self.sw_path] - for port, intf in self.intfs.items(): - if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) - if self.pcap_dump: - args.append("--pcap") - # args.append("--useFiles") - if self.thrift_port: - args.extend(['--thrift-port', str(self.thrift_port)]) - if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) - P4Switch.device_id += 1 - args.append(self.json_path) - if self.enable_debugger: - args.append("--debugger") - if self.log_console: - args.append("--log-console") - info(' '.join(args) + "\n") - - pid = None - with tempfile.NamedTemporaryFile() as f: - # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &') - self.cmd(' '.join(args) + ' >' + self.log_file + ' 2>&1 & echo $! >> ' + f.name) - pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) - sleep(1) - if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly." - "Check the switch log file.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) - - def stop(self): - "Terminate P4 switch." - self.output.flush() - self.cmd('kill %' + self.sw_path) - self.cmd('wait') - self.deleteIntfs() - - def attach(self, intf): - "Connect a data port" - assert(0) - - def detach(self, intf): - "Disconnect a data port" - assert(0) diff --git a/SIGCOMM_2017/utils/mininet/shortest_path.py b/SIGCOMM_2017/utils/mininet/shortest_path.py deleted file mode 100644 index 971b1b446..000000000 --- a/SIGCOMM_2017/utils/mininet/shortest_path.py +++ /dev/null @@ -1,78 +0,0 @@ -class ShortestPath: - - def __init__(self, edges=[]): - self.neighbors = {} - for edge in edges: - self.addEdge(*edge) - - def addEdge(self, a, b): - if a not in self.neighbors: self.neighbors[a] = [] - if b not in self.neighbors[a]: self.neighbors[a].append(b) - - if b not in self.neighbors: self.neighbors[b] = [] - if a not in self.neighbors[b]: self.neighbors[b].append(a) - - def get(self, a, b, exclude=lambda node: False): - # Shortest path from a to b - return self._recPath(a, b, [], exclude) - - def _recPath(self, a, b, visited, exclude): - if a == b: return [a] - new_visited = visited + [a] - paths = [] - for neighbor in self.neighbors[a]: - if neighbor in new_visited: continue - if exclude(neighbor) and neighbor != b: continue - path = self._recPath(neighbor, b, new_visited, exclude) - if path: paths.append(path) - - paths.sort(key=len) - return [a] + paths[0] if len(paths) else None - -if __name__ == '__main__': - - edges = [ - (1, 2), - (1, 3), - (1, 5), - (2, 4), - (3, 4), - (3, 5), - (3, 6), - (4, 6), - (5, 6), - (7, 8) - - ] - sp = ShortestPath(edges) - - assert sp.get(1, 1) == [1] - assert sp.get(2, 2) == [2] - - assert sp.get(1, 2) == [1, 2] - assert sp.get(2, 1) == [2, 1] - - assert sp.get(1, 3) == [1, 3] - assert sp.get(3, 1) == [3, 1] - - assert sp.get(4, 6) == [4, 6] - assert sp.get(6, 4) == [6, 4] - - assert sp.get(2, 6) == [2, 4, 6] - assert sp.get(6, 2) == [6, 4, 2] - - assert sp.get(1, 6) in [[1, 3, 6], [1, 5, 6]] - assert sp.get(6, 1) in [[6, 3, 1], [6, 5, 1]] - - assert sp.get(2, 5) == [2, 1, 5] - assert sp.get(5, 2) == [5, 1, 2] - - assert sp.get(4, 5) in [[4, 3, 5], [4, 6, 5]] - assert sp.get(5, 4) in [[5, 3, 4], [6, 6, 4]] - - assert sp.get(7, 8) == [7, 8] - assert sp.get(8, 7) == [8, 7] - - assert sp.get(1, 7) == None - assert sp.get(7, 2) == None - diff --git a/SIGCOMM_2017/utils/mininet/single_switch_mininet.py b/SIGCOMM_2017/utils/mininet/single_switch_mininet.py deleted file mode 100755 index e2e76366c..000000000 --- a/SIGCOMM_2017/utils/mininet/single_switch_mininet.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python2 - -# 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. -# - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.log import setLogLevel, info -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host - -import argparse -from subprocess import PIPE, Popen -from time import sleep - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--num-hosts', help='Number of hosts to connect to switch', - type=int, action="store", default=2) -parser.add_argument('--mode', choices=['l2', 'l3'], type=str, default='l3') -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--log-file', help='Path to write the switch log file', - type=str, action="store", required=False) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - type=str, action="store", required=False, default=False) -parser.add_argument('--switch-config', help='simple_switch_CLI script to configure switch', - type=str, action="store", required=False, default=False) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) - -args = parser.parse_args() - - -class SingleSwitchTopo(Topo): - "Single switch connected to n (< 256) hosts." - def __init__(self, sw_path, json_path, log_file, - thrift_port, pcap_dump, n, **opts): - # Initialize topology and default options - Topo.__init__(self, **opts) - - switch = self.addSwitch('s1', - sw_path = sw_path, - json_path = json_path, - log_console = True, - log_file = log_file, - thrift_port = thrift_port, - enable_debugger = False, - pcap_dump = pcap_dump) - - for h in xrange(n): - host = self.addHost('h%d' % (h + 1), - ip = "10.0.%d.10/24" % h, - mac = '00:04:00:00:00:%02x' %h) - print "Adding host", str(host) - self.addLink(host, switch) - -def main(): - num_hosts = args.num_hosts - mode = args.mode - - topo = SingleSwitchTopo(args.behavioral_exe, - args.json, - args.log_file, - args.thrift_port, - args.pcap_dump, - num_hosts) - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None) - net.start() - - - sw_mac = ["00:aa:bb:00:00:%02x" % n for n in xrange(num_hosts)] - - sw_addr = ["10.0.%d.1" % n for n in xrange(num_hosts)] - - for n in xrange(num_hosts): - h = net.get('h%d' % (n + 1)) - if mode == "l2": - h.setDefaultRoute("dev %s" % h.defaultIntf().name) - else: - h.setARP(sw_addr[n], sw_mac[n]) - h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n])) - - for n in xrange(num_hosts): - h = net.get('h%d' % (n + 1)) - h.describe(sw_addr[n], sw_mac[n]) - - sleep(1) - - if args.switch_config is not None: - print - print "Reading switch configuration script:", args.switch_config - with open(args.switch_config, 'r') as config_file: - switch_config = config_file.read() - - print "Configuring switch..." - proc = Popen(["simple_switch_CLI"], stdin=PIPE) - proc.communicate(input=switch_config) - - print "Configuration complete." - print - - print "Ready !" - - if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: - print message_file.read() - - CLI( net ) - net.stop() - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/SIGCOMM_2017/utils/p4apprunner.py b/SIGCOMM_2017/utils/p4apprunner.py deleted file mode 100755 index 36b9eeab8..000000000 --- a/SIGCOMM_2017/utils/p4apprunner.py +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env python2 -# 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. - -from __future__ import print_function - -import argparse -from collections import OrderedDict -import json -import os -import sys -import tarfile - -parser = argparse.ArgumentParser(description='p4apprunner') -parser.add_argument('--build-dir', help='Directory to build in.', - type=str, action='store', required=False, default='/tmp') -parser.add_argument('--quiet', help='Suppress log messages.', - action='store_true', required=False, default=False) -parser.add_argument('--manifest', help='Path to manifest file.', - type=str, action='store', required=False, default='./p4app.json') -parser.add_argument('app', help='.p4app package to run.', type=str) -parser.add_argument('target', help=('Target to run. Defaults to the first target ' - 'in the package.'), - nargs='?', type=str) - -args = parser.parse_args() - -def log(*items): - if args.quiet != True: - print(*items) - -def log_error(*items): - print(*items, file=sys.stderr) - -def run_command(command): - log('>', command) - return os.WEXITSTATUS(os.system(command)) - -class Manifest: - def __init__(self, program_file, language, target, target_config): - self.program_file = program_file - self.language = language - self.target = target - self.target_config = target_config - -def read_manifest(manifest_file): - manifest = json.load(manifest_file, object_pairs_hook=OrderedDict) - - if 'program' not in manifest: - log_error('No program defined in manifest.') - sys.exit(1) - program_file = manifest['program'] - - if 'language' not in manifest: - log_error('No language defined in manifest.') - sys.exit(1) - language = manifest['language'] - - if 'targets' not in manifest or len(manifest['targets']) < 1: - log_error('No targets defined in manifest.') - sys.exit(1) - - if args.target is not None: - chosen_target = args.target - elif 'default-target' in manifest: - chosen_target = manifest['default-target'] - else: - chosen_target = manifest['targets'].keys()[0] - - if chosen_target not in manifest['targets']: - log_error('Target not found in manifest:', chosen_target) - sys.exit(1) - - return Manifest(program_file, language, chosen_target, manifest['targets'][chosen_target]) - - -def run_compile_bmv2(manifest): - if 'run-before-compile' in manifest.target_config: - commands = manifest.target_config['run-before-compile'] - if not isinstance(commands, list): - log_error('run-before-compile should be a list:', commands) - sys.exit(1) - for command in commands: - run_command(command) - - compiler_args = [] - - if manifest.language == 'p4-14': - compiler_args.append('--p4v 14') - elif manifest.language == 'p4-16': - compiler_args.append('--p4v 16') - else: - log_error('Unknown language:', manifest.language) - sys.exit(1) - - if 'compiler-flags' in manifest.target_config: - flags = manifest.target_config['compiler-flags'] - if not isinstance(flags, list): - log_error('compiler-flags should be a list:', flags) - sys.exit(1) - compiler_args.extend(flags) - - # Compile the program. - output_file = manifest.program_file + '.json' - compiler_args.append('"%s"' % manifest.program_file) - compiler_args.append('-o "%s"' % output_file) - rv = run_command('p4c-bm2-ss %s' % ' '.join(compiler_args)) - - if 'run-after-compile' in manifest.target_config: - commands = manifest.target_config['run-after-compile'] - if not isinstance(commands, list): - log_error('run-after-compile should be a list:', commands) - sys.exit(1) - for command in commands: - run_command(command) - - if rv != 0: - log_error('Compile failed.') - sys.exit(1) - - return output_file - -def run_mininet(manifest): - output_file = run_compile_bmv2(manifest) - - # Run the program using the BMV2 Mininet simple switch. - switch_args = [] - - # We'll place the switch's log file in current (build) folder. - cwd = os.getcwd() - log_file = os.path.join(cwd, manifest.program_file + '.log') - print ("*** Log file %s" % log_file) - switch_args.append('--log-file "%s"' % log_file) - - pcap_dir = os.path.join(cwd) - print ("*** Pcap folder %s" % pcap_dir) - switch_args.append('--pcap-dump "%s" '% pcap_dir) - - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: - - print(file=message) - print('======================================================================', - file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', - file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) - print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI', file=message) - print(file=message) - print('To view the switch log, run this command from your host OS:', file=message) - print(' tail -f %s' % log_file, file=message) - print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) - print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) - - switch_args.append('--cli-message "%s"' % message_file) - - if 'num-hosts' in manifest.target_config: - switch_args.append('--num-hosts %s' % manifest.target_config['num-hosts']) - - if 'switch-config' in manifest.target_config: - switch_args.append('--switch-config "%s"' % manifest.target_config['switch-config']) - - switch_args.append('--behavioral-exe "%s"' % 'simple_switch') - switch_args.append('--json "%s"' % output_file) - - program = '"%s/mininet/single_switch_mininet.py"' % sys.path[0] - return run_command('python2 %s %s' % (program, ' '.join(switch_args))) - -def run_multiswitch(manifest): - output_file = run_compile_bmv2(manifest) - - script_args = [] - cwd = os.getcwd() - log_dir = os.path.join(cwd, cwd + '/logs') - print ("*** Log directory %s" % log_dir) - script_args.append('--log-dir "%s"' % log_dir) - pcap_dir = os.path.join(cwd) - print ("*** Pcap directory %s" % cwd) - script_args.append('--manifest "%s"' % args.manifest) - script_args.append('--target "%s"' % manifest.target) - if 'auto-control-plane' in manifest.target_config and manifest.target_config['auto-control-plane']: - script_args.append('--auto-control-plane' ) - script_args.append('--behavioral-exe "%s"' % 'simple_switch') - script_args.append('--json "%s"' % output_file) - #script_args.append('--cli') - - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: - - print(file=message) - print('======================================================================', - file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', - file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) - print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI --thrift-port ', file=message) - print(file=message) - print('To view a switch log, run this command from your host OS:', file=message) - print(' tail -f %s/.log' % log_dir, file=message) - print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) - print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) - - script_args.append('--cli-message "%s"' % message_file) - - program = '"%s/mininet/multi_switch_mininet.py"' % sys.path[0] - return run_command('python2 %s %s' % (program, ' '.join(script_args))) - -def run_stf(manifest): - output_file = run_compile_bmv2(manifest) - - if not 'test' in manifest.target_config: - log_error('No STF test file provided.') - sys.exit(1) - stf_file = manifest.target_config['test'] - - # Run the program using the BMV2 STF interpreter. - stf_args = [] - stf_args.append('-v') - stf_args.append(os.path.join(args.build_dir, output_file)) - stf_args.append(os.path.join(args.build_dir, stf_file)) - - program = '"%s/stf/bmv2stf.py"' % sys.path[0] - rv = run_command('python2 %s %s' % (program, ' '.join(stf_args))) - if rv != 0: - sys.exit(1) - return rv - -def run_custom(manifest): - output_file = run_compile_bmv2(manifest) - python_path = 'PYTHONPATH=$PYTHONPATH:/scripts/mininet/' - script_args = [] - script_args.append('--behavioral-exe "%s"' % 'simple_switch') - script_args.append('--json "%s"' % output_file) - script_args.append('--cli "%s"' % 'simple_switch_CLI') - if not 'program' in manifest.target_config: - log_error('No mininet program file provided.') - sys.exit(1) - program = manifest.target_config['program'] - rv = run_command('%s python2 %s %s' % (python_path, program, ' '.join(script_args))) - - if rv != 0: - sys.exit(1) - return rv - -def main(): - log('Entering build directory.') - os.chdir(args.build_dir) - - # A '.p4app' package is really just a '.tar.gz' archive. Extract it so we - # can process its contents. - log('Extracting package.') - tar = tarfile.open(args.app) - tar.extractall() - tar.close() - - log('Reading package manifest.') - with open(args.manifest, 'r') as manifest_file: - manifest = read_manifest(manifest_file) - - # Dispatch to the backend implementation for this target. - backend = manifest.target - if 'use' in manifest.target_config: - backend = manifest.target_config['use'] - - if backend == 'mininet': - rc = run_mininet(manifest) - elif backend == 'multiswitch': - rc = run_multiswitch(manifest) - elif backend == 'stf': - rc = run_stf(manifest) - elif backend == 'custom': - rc = run_custom(manifest) - elif backend == 'compile-bmv2': - run_compile_bmv2(manifest) - rc = 0 - else: - log_error('Target specifies unknown backend:', backend) - sys.exit(1) - - sys.exit(rc) - -if __name__ == '__main__': - main() diff --git a/SIGCOMM_2017/vm/Vagrantfile b/SIGCOMM_2017/vm/Vagrantfile deleted file mode 100644 index 5498b020d..000000000 --- a/SIGCOMM_2017/vm/Vagrantfile +++ /dev/null @@ -1,17 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -Vagrant.configure(2) do |config| - config.vm.box = "bento/ubuntu-16.04" - config.vm.provider "virtualbox" do |vb| - vb.gui = true - vb.memory = "2048" - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] - end - config.vm.synced_folder '.', '/vagrant', disabled: true - config.vm.hostname = "p4" - config.vm.provision "file", source: "p4-logo.png", destination: "/home/vagrant/p4-logo.png" - config.vm.provision "file", source: "p4_16-mode.el", destination: "/home/vagrant/p4_16-mode.el" - config.vm.provision "shell", path: "root-bootstrap.sh" - config.vm.provision "shell", privileged: false, path: "user-bootstrap.sh" -end diff --git a/SIGCOMM_2017/vm/p4-logo.png b/SIGCOMM_2017/vm/p4-logo.png deleted file mode 100644 index cc36615ba..000000000 Binary files a/SIGCOMM_2017/vm/p4-logo.png and /dev/null differ diff --git a/SIGCOMM_2017/vm/p4_16-mode.el b/SIGCOMM_2017/vm/p4_16-mode.el deleted file mode 100644 index 7625d907e..000000000 --- a/SIGCOMM_2017/vm/p4_16-mode.el +++ /dev/null @@ -1,222 +0,0 @@ -;;; p4_16-mode.el --- Support for the P4_16 programming language - -;; Copyright (C) 2016- Barefoot Networks -;; Author: Vladimir Gurevich -;; Maintainer: Vladimir Gurevich -;; Created: 15 April 2017 -;; Version: 0.2 -;; Keywords: languages p4_16 -;; Homepage: http://p4.org - -;; This file is not part of GNU Emacs. - -;; This file is free software… - -;; This mode has preliminary support for P4_16. It covers the core language, -;; but it is not clear yet, how we can highlight the indentifiers, defined -;; for a particular architecture. Core library definitions are included - -;; Placeholder for user customization code -(defvar p4_16-mode-hook nil) - -;; Define the keymap (for now it is pretty much default) -(defvar p4_16-mode-map - (let ((map (make-keymap))) - (define-key map "\C-j" 'newline-and-indent) - map) - "Keymap for P4_16 major mode") - -;; Syntactic HighLighting - -;; Main keywors (declarations and operators) -(setq p4_16-keywords - '("action" "apply" - "control" - "default" - "else" "enum" "extern" "exit" - "header" "header_union" - "if" - "match_kind" - "package" "parser" - "return" - "select" "state" "struct" "switch" - "table" "transition" "tuple" "typedef" - "verify" - )) - -(setq p4_16-annotations - '("@name" "@metadata" "@alias" - )) - -(setq p4_16-attributes - '("const" "in" "inout" "out" - ;; Tables - "key" "actions" "default_action" "entries" "implementation" - "counters" "meters" - )) - -(setq p4_16-variables - '("packet_in" "packet_out" - )) - -(setq p4_16-operations - '("&&&" ".." "++" "?" ":")) - -(setq p4_16-constants - '( - ;;; Don't care - "_" - ;;; bool - "false" "true" - ;;; error - "NoError" "PacketTooShort" "NoMatch" "StackOutOfBounds" - "OverwritingHeader" "HeaderTooShort" "ParserTiimeout" - ;;; match_kind - "exact" "ternary" "lpm" "range" - ;;; We can add constants for supported architectures here - )) - -(setq p4_16-types - '("bit" "bool" "int" "varbit" "void" "error" - )) - -(setq p4_16-primitives - '( - ;;; Header methods - "isValid" "setValid" "setInvalid" - ;;; Table Methods - "hit" "action_run" - ;;; packet_in methods - "extract" "lookahead" "advance" "length" - ;;; packet_out methods - "emit" - ;;; Known parser states - "accept" "reject" - ;;; misc - "NoAction" - )) - -(setq p4_16-cpp - '("#include" - "#define" "#undef" - "#if" "#ifdef" "#ifndef" - "#elif" "#else" - "#endif" - "defined" - "#line" "#file")) - -(setq p4_16-cppwarn - '("#error" "#warning")) - -;; Optimize the strings -(setq p4_16-keywords-regexp (regexp-opt p4_16-keywords 'words)) -(setq p4_16-annotations-regexp (regexp-opt p4_16-annotations 1)) -(setq p4_16-attributes-regexp (regexp-opt p4_16-attributes 'words)) -(setq p4_16-variables-regexp (regexp-opt p4_16-variables 'words)) -(setq p4_16-operations-regexp (regexp-opt p4_16-operations 'words)) -(setq p4_16-constants-regexp (regexp-opt p4_16-constants 'words)) -(setq p4_16-types-regexp (regexp-opt p4_16-types 'words)) -(setq p4_16-primitives-regexp (regexp-opt p4_16-primitives 'words)) -(setq p4_16-cpp-regexp (regexp-opt p4_16-cpp 1)) -(setq p4_16-cppwarn-regexp (regexp-opt p4_16-cppwarn 1)) - - -;; create the list for font-lock. -;; each category of keyword is given a particular face -(defconst p4_16-font-lock-keywords - (list - (cons p4_16-cpp-regexp font-lock-preprocessor-face) - (cons p4_16-cppwarn-regexp font-lock-warning-face) - (cons p4_16-types-regexp font-lock-type-face) - (cons p4_16-constants-regexp font-lock-constant-face) - (cons p4_16-attributes-regexp font-lock-builtin-face) - (cons p4_16-variables-regexp font-lock-variable-name-face) - ;;; This is a special case to distinguish the method from the keyword - (cons "\\.apply" font-lock-function-name-face) - (cons p4_16-primitives-regexp font-lock-function-name-face) - (cons p4_16-operations-regexp font-lock-builtin-face) - (cons p4_16-keywords-regexp font-lock-keyword-face) - (cons p4_16-annotations-regexp font-lock-keyword-face) - (cons "\\(\\w*_t +\\)" font-lock-type-face) - (cons "[^A-Z_][A-Z] " font-lock-type-face) ;; Total hack for templates - (cons "<[A-Z, ]*>" font-lock-type-face) - (cons "\\(<[^>]+>\\)" font-lock-string-face) - (cons "\\([^_A-Za-z]\\([0-9]+w\\)?0x[0-9A-Fa-f]+\\)" font-lock-constant-face) - (cons "\\([^_A-Za-z]\\([0-9]+w\\)?0b[01]+\\)" font-lock-constant-face) - (cons "\\([^_A-Za-z][+-]?\\([0-9]+w\\)?[0-9]+\\)" font-lock-constant-face) - ;;(cons "\\(\\w*\\)" font-lock-variable-name-face) - ) - "Default Highlighting Expressions for P4_16") - -(defvar p4_16-mode-syntax-table - (let ((st (make-syntax-table))) - (modify-syntax-entry ?_ "w" st) - (modify-syntax-entry ?/ ". 124b" st) - (modify-syntax-entry ?* ". 23" st) - (modify-syntax-entry ?\n "> b" st) - st) - "Syntax table for p4_16-mode") - -;;; Indentation -(defvar p4_16-indent-offset 4 - "Indentation offset for `p4_16-mode'.") - -(defun p4_16-indent-line () - "Indent current line for any balanced-paren-mode'." - (interactive) - (let ((indent-col 0) - (indentation-increasers "[{(]") - (indentation-decreasers "[})]") - ) - (save-excursion - (beginning-of-line) - (condition-case nil - (while t - (backward-up-list 1) - (when (looking-at indentation-increasers) - (setq indent-col (+ indent-col p4_16-indent-offset)))) - (error nil))) - (save-excursion - (back-to-indentation) - (when (and (looking-at indentation-decreasers) - (>= indent-col p4_16-indent-offset)) - (setq indent-col (- indent-col p4_16-indent-offset)))) - (indent-line-to indent-col))) - -;;; Imenu support -(require 'imenu) -(setq p4_16-imenu-generic-expression - '( - ("Controls" "^ *control +\\([A-Za-z0-9_]*\\)" 1) - ("Externs" "^ *extern +\\([A-Za-z0-9_]*\\) *\\([A-Za-z0-9_]*\\)" 2) - ("Tables" "^ *table +\\([A-Za-z0-9_]*\\)" 1) - ("Actions" "^ *action +\\([A-Za-z0-9_]*\\)" 1) - ("Parsers" "^ *parser +\\([A-Za-z0-9_]*\\)" 1) - ("Parser States" "^ *state +\\([A-Za-z0-9_]*\\)" 1) - ("Headers" "^ *header +\\([A-Za-z0-9_]*\\)" 1) - ("Header Unions" "^ *header_union +\\([A-Za-z0-9_]*\\)" 1) - ("Structs" "^ *struct +\\([A-Za-z0-9_]*\\)" 1) - )) - -;;; Cscope Support -(require 'xcscope) - -;; Put everything together -(defun p4_16-mode () - "Major mode for editing P4_16 programs" - (interactive) - (kill-all-local-variables) - (set-syntax-table p4_16-mode-syntax-table) - (use-local-map p4_16-mode-map) - (set (make-local-variable 'font-lock-defaults) '(p4_16-font-lock-keywords)) - (set (make-local-variable 'indent-line-function) 'p4_16-indent-line) - (setq major-mode 'p4_16-mode) - (setq mode-name "P4_16") - (setq imenu-generic-expression p4_16-imenu-generic-expression) - (imenu-add-to-menubar "P4_16") - (cscope-minor-mode) - (run-hooks 'p4_16-mode-hook) -) - -;; The most important line -(provide 'p4_16-mode) diff --git a/SIGCOMM_2017/vm/root-bootstrap.sh b/SIGCOMM_2017/vm/root-bootstrap.sh deleted file mode 100755 index c460aa14a..000000000 --- a/SIGCOMM_2017/vm/root-bootstrap.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -set -x - -sudo add-apt-repository ppa:webupd8team/sublime-text-3 -sudo add-apt-repository ppa:webupd8team/atom - -apt-get update - -DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade - -apt-get install -y \ - lubuntu-desktop \ - git \ - vim \ - emacs24 \ - xcscope-el \ - sublime-text-installer \ - atom \ - xterm \ - mininet \ - autoconf \ - automake \ - libtool \ - curl \ - make \ - g++ \ - unzip \ - libgc-dev \ - bison \ - flex \ - libfl-dev \ - libgmp-dev \ - libboost-dev \ - libboost-iostreams-dev \ - pkg-config \ - python \ - python-scapy \ - python-ipaddr \ - tcpdump \ - cmake - -useradd -m -d /home/p4 -s /bin/bash p4 -echo "p4:p4" | chpasswd -echo "p4 ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/99_p4 -chmod 440 /etc/sudoers.d/99_p4 - -cd /usr/share/lubuntu/wallpapers/ -cp /home/vagrant/p4-logo.png . -rm lubuntu-default-wallpaper.png -ln -s p4-logo.png lubuntu-default-wallpaper.png -rm /home/vagrant/p4-logo.png -cd /home/vagrant -sed -i s@#background=@background=/usr/share/lubuntu/wallpapers/1604-lubuntu-default-wallpaper.png@ /etc/lightdm/lightdm-gtk-greeter.conf diff --git a/SIGCOMM_2017/vm/user-bootstrap.sh b/SIGCOMM_2017/vm/user-bootstrap.sh deleted file mode 100644 index 63e4875e8..000000000 --- a/SIGCOMM_2017/vm/user-bootstrap.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -set -x - -# Bmv2 -git clone https://github.com/p4lang/behavioral-model -cd behavioral-model -./install_deps.sh -./autogen.sh -./configure -make -sudo make install -cd .. - -# Protobuf -git clone https://github.com/google/protobuf.git -cd protobuf -git checkout v3.0.2 -./autogen.sh -./configure -make -sudo make install -sudo ldconfig -cd .. - -# P4C -git clone --recursive https://github.com/p4lang/p4c -cd p4c -mkdir build -cd build -cmake .. -make -j4 -sudo make install -cd .. -cd .. - -# Tutorials -pip install crcmod -git clone https://github.com/p4lang/tutorials -cd tutorials -git checkout sigcomm_17 -cd .. -sudo mv tutorials /home/p4 -sudo chown -R p4:p4 /home/p4/tutorials - -# Emacs -sudo cp p4_16-mode.el /usr/share/emacs/site-lisp/ -sudo echo "(add-to-list 'auto-mode-alist '(\"\\.p4\\'\" . p4_16-mode))" >> /home/p4/.emacs -sudo chown p4:p4 /home/p4/.emacs - diff --git a/Teaching/Stanford_CS344_2018/.gitignore b/Teaching/Stanford_CS344_2018/.gitignore deleted file mode 100644 index 816826e81..000000000 --- a/Teaching/Stanford_CS344_2018/.gitignore +++ /dev/null @@ -1 +0,0 @@ -simple_router.config diff --git a/Teaching/Stanford_CS344_2018/README.md b/Teaching/Stanford_CS344_2018/README.md deleted file mode 100644 index 6cdc7017c..000000000 --- a/Teaching/Stanford_CS344_2018/README.md +++ /dev/null @@ -1,126 +0,0 @@ -# Instructions - -## Introduction - -In this tutorial, you will implement a heavy hitter detection filter. - -Network flows typically have a fairly wide distribution in terms of the -data they transmit, with most of the flows sending little data and few -flows sending a lot. The latter flows are called heavy hitters, and they -often have a detrimental effect to network performance. This is -because they cause congestion, leading to significantly increased completion -times for small, short-lived flows. Detecting heavy hitters allows us to treat them -differently, e.g. we can put their packets in low priority queues, allowing -packets of other flows to face little or no congestion. - -In this example, you will implement a heavy hitter detection filter within -a router. You can find a skeleton of the program in simple_router.p4. In that -file, you have to fill in the parts that are marked with TODO. - -This example is based on [count-min sketch](http://theory.stanford.edu/~tim/s15/l/l2.pdf). -In fact, we use two count-min sketches which are reset with an offset -equal to their half-life. With every new packet coming in, we update -the values of both sketches but we use only the ones of the least -recently reset one to decide whether a packet belongs to a heavy hitter -flow or not. - -> **Spoiler alert:** There is a reference solution in the `solution` -> sub-directory. Feel free to compare your implementation to the -> reference. - - -## Step 1: Run the (incomplete) starter code - -The directory with this README also contains a skeleton P4 program, -`simple_router.p4`, which implements a simple router. Your job will be to -extend this skeleton program to properly implement a heavy hitter -detection filter. - -Before that, let's compile the incomplete `simple_router.p4` and bring -up a switch in Mininet to test its behavior. - -1. In your shell, run: - ```bash - ./run.sh - ``` - This will: - * create a p4app application, - * compile `simple_switch.p4`, - * generate control plane code, - * start a Mininet instance with one switch (`s1`) conected to - two hosts (`h1` and `h2`). - * install the control plane code to your switch, - * The hosts are assigned IPs of `10.0.0.10` and `10.0.1.10`. - -2. You should now see a Mininet command prompt. Run ping between - `h1` and `h2` to make sure that everything runs correctly: - ```bash - mininet> h1 ping h2 - ``` - You should see all packets going through. - -3. Type `exit` to leave each Mininet command line. - -### A note about the control plane - -A P4 program defines a packet-processing pipeline, but the rules -within each table are inserted by the control plane. When a rule -matches a packet, its action is invoked with parameters supplied by -the control plane as part of the rule. - -In this exercise, we have already implemented the control plane -logic for you. As part of invoking `run.sh`, a set of rules is generated -by `setup.py` and when bringing up the Mininet instance, these -packet-processing rules are installed in the tables of -the switch. These are defined in the `simple_router.config` file. - -## Step 2: Implement the heavy hitter detection filter - -The `simple_router.p4` file contains a skeleton P4 program with key pieces of -logic replaced by `TODO` comments. Your implementation should follow -the structure given in this file, just replace each `TODO` with logic -implementing the missing piece. - -More specifically, you need to implement the main actions used within -the heavy hitter detection block. In this example, when our filter -classifies a packet as belonging to a heavy hitter flow, it marks -it as such and then the switch drops it before reaching the -egress control. - -## Step 3: Run your solution - -Our heavy hitter filter requires periodic reset of the registers of the -count-min sketches. Running: -```bash -bash filter_reset.sh -``` -in a terminal window does that periodic reset for you. - -The filter currently allows 1000 bytes/sec (you can change that value -in `setup.py`). - -In another terminal window, run: -```bash -./run.sh -``` - -In the minigraph window, you can try: -``` -h1 ping -s 80 -i 0.1 h2 -``` -With this command h1, sends a packet with a total IP length -of 100 bytes every 100 ms. When you run this command, you -shouldn't see any drops. If on the other hand you run: -``` -h1 ping -s 80 -i 0.05 h2 -``` -h1 sends a packet every 50 ms, which puts the flow above -the filter limit. In this case you will observe that about -half of the packets send by h1 are being dropped at the switch. - -### Next steps -Check out the code in `setup.py` and `filter_reset.sh`. By changing -the constants in those, you can experiment with different -heavy hitter threshold levels, count-min sketch sizes and the accuracy -of the throughput approximation. - diff --git a/Teaching/Stanford_CS344_2018/filter_reset.sh b/Teaching/Stanford_CS344_2018/filter_reset.sh deleted file mode 100755 index f5ca34c98..000000000 --- a/Teaching/Stanford_CS344_2018/filter_reset.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -CONTAINER_ID=`docker ps | tail -n 1 | cut -d ' ' -f 1` -ACTIVE_FILTER='A' - -while true; do - CUR_TIME=`echo "get_time_elapsed" | docker exec -i $CONTAINER_ID simple_switch_CLI | grep Runtime | head -n 1 | cut -d ':' -f 2` - CUR_TIME=${CUR_TIME}000 - echo $CUR_TIME - echo "register_write last_reset_time 0 $CUR_TIME" | docker exec -i $CONTAINER_ID simple_switch_CLI - if [ $ACTIVE_FILTER == 'A' ] ; then - echo "register_write is_a_active 0 1" - echo "register_reset hashtable_b0" | docker exec -i $CONTAINER_ID simple_switch_CLI - echo "register_reset hashtable_b1" | docker exec -i $CONTAINER_ID simple_switch_CLI - echo "register_reset hashtable_b2" | docker exec -i $CONTAINER_ID simple_switch_CLI - echo "register_reset hashtable_b3" | docker exec -i $CONTAINER_ID simple_switch_CLI - ACTIVE_FILTER='B' - else - echo "register_write is_a_active 0 0" - echo "register_reset hashtable_a0" | docker exec -i $CONTAINER_ID simple_switch_CLI - echo "register_reset hashtable_a1" | docker exec -i $CONTAINER_ID simple_switch_CLI - echo "register_reset hashtable_a2" | docker exec -i $CONTAINER_ID simple_switch_CLI - echo "register_reset hashtable_a3" | docker exec -i $CONTAINER_ID simple_switch_CLI - ACTIVE_FILTER='A' - fi - sleep 4 -done diff --git a/Teaching/Stanford_CS344_2018/header.p4 b/Teaching/Stanford_CS344_2018/header.p4 deleted file mode 100644 index 8709cd613..000000000 --- a/Teaching/Stanford_CS344_2018/header.p4 +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef __HEADER_P4__ -#define __HEADER_P4__ 1 - -struct ingress_metadata_t { - bit<32> nhop_ipv4; -} - -header ethernet_t { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; -} - -header ipv4_t { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - bit<32> srcAddr; - bit<32> dstAddr; -} - -header tcp_t { - bit<16> srcPort; - bit<16> dstPort; - bit<32> seqNo; - bit<32> ackNo; - bit<4> dataOffset; - bit<4> res; - bit<8> flags; - bit<16> window; - bit<16> checksum; - bit<16> urgentPtr; -} - -header udp_t { - bit<16> srcPort; - bit<16> dstPort; - bit<16> hdrLength; - bit<16> checksum; -} - -struct hhd_t { - @name("filter_age") - bit<48> filter_age; - bit<32> value_a0; - bit<32> value_a1; - bit<32> value_a2; - bit<32> value_a3; - bit<32> value_b0; - bit<32> value_b1; - bit<32> value_b2; - bit<32> value_b3; - bit<32> threshold; - bit<1> is_a_active; - bit<1> is_heavy_hitter; -} - -struct metadata { - @name("ingress_metadata") - ingress_metadata_t ingress_metadata; - @name("hhd") - hhd_t hhd; -} - -struct headers { - @name("ethernet") - ethernet_t ethernet; - @name("ipv4") - ipv4_t ipv4; - @name("tcp") - tcp_t tcp; - @name("udp") - udp_t udp; -} - -#endif // __HEADER_P4__ diff --git a/Teaching/Stanford_CS344_2018/p4app.json b/Teaching/Stanford_CS344_2018/p4app.json deleted file mode 100644 index 3220cccc0..000000000 --- a/Teaching/Stanford_CS344_2018/p4app.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "program": "simple_router.p4", - "language": "p4-16", - "targets": { - "mininet": { - "num-hosts": 2, - "switch-config": "simple_router.config" - } - } -} diff --git a/Teaching/Stanford_CS344_2018/parser.p4 b/Teaching/Stanford_CS344_2018/parser.p4 deleted file mode 100644 index 0b64560cb..000000000 --- a/Teaching/Stanford_CS344_2018/parser.p4 +++ /dev/null @@ -1,51 +0,0 @@ -parser ParserImpl(packet_in packet, out headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - state start { - transition parse_ethernet; - } - - state parse_ethernet { - packet.extract(hdr.ethernet); - transition select(hdr.ethernet.etherType) { - 16w0x800: parse_ipv4; - default: accept; - } - } - - state parse_ipv4 { - packet.extract(hdr.ipv4); - transition select(hdr.ipv4.protocol) { - 8w0x6: parse_tcp; - default: accept; - } - } - - state parse_tcp { - packet.extract(hdr.tcp); - transition accept; - } -} - -control DeparserImpl(packet_out packet, in headers hdr) { - apply { - packet.emit(hdr.ethernet); - packet.emit(hdr.ipv4); - packet.emit(hdr.tcp); - } -} - -control verifyChecksum(inout headers hdr, inout metadata meta) { - apply { } -} - -control computeChecksum(inout headers hdr, inout metadata meta) { - apply { - update_checksum( - hdr.ipv4.isValid(), - { hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, - hdr.ipv4.totalLen, hdr.ipv4.identification, - hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, - hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr }, - hdr.ipv4.hdrChecksum, - HashAlgorithm.csum16); - } -} diff --git a/Teaching/Stanford_CS344_2018/run.sh b/Teaching/Stanford_CS344_2018/run.sh deleted file mode 100755 index d3dd067af..000000000 --- a/Teaching/Stanford_CS344_2018/run.sh +++ /dev/null @@ -1,6 +0,0 @@ -P4APPRUNNER=../utils/p4apprunner.py -python setup.py -mkdir -p build -tar -czf build/p4app.tgz * --exclude='build' -#cd build -sudo python $P4APPRUNNER p4app.tgz --build-dir ./build diff --git a/Teaching/Stanford_CS344_2018/setup.py b/Teaching/Stanford_CS344_2018/setup.py deleted file mode 100644 index f3e7087b8..000000000 --- a/Teaching/Stanford_CS344_2018/setup.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -from shutil import copyfile - -unit_duration = 20 # log_2 of unit duration (so 2**unit_duration) -total_time_bits = 48 -log_units = 3 # log_2 of number of units -units = 2**log_units -threshold = 8*1000.0 # in bytes - -copyfile('simple_router.config.template', 'simple_router.config') - -with open('simple_router.config', 'a') as fd: - time_mask = (2**(unit_duration+log_units)-1) - (2**unit_duration -1) - for unit in range(units): - time_value = unit*2**unit_duration - if unit < units/2: - unit_threshold = int((unit+1) * threshold / units + threshold/2 ) - else: - unit_threshold = int((unit+1) * threshold / units) - fd.write('table_add threshold_table set_threshold %d&&&%d => %d 0\n' % (time_value, time_mask, unit_threshold)) - diff --git a/Teaching/Stanford_CS344_2018/simple_router.config.template b/Teaching/Stanford_CS344_2018/simple_router.config.template deleted file mode 100644 index f22ae7036..000000000 --- a/Teaching/Stanford_CS344_2018/simple_router.config.template +++ /dev/null @@ -1,12 +0,0 @@ -set_crc16_parameters calc_2 0x1021 0xffff 0x0000 false false -set_crc32_parameters calc_0 0x4c11db7 0xffffffff 0x00000000 false false -table_set_default send_frame egress_drop -table_set_default forward ingress_drop -table_set_default ipv4_lpm ingress_drop -table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00 -table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:01 -table_add forward set_dmac 10.0.0.10 => 00:04:00:00:00:00 -table_add forward set_dmac 10.0.1.10 => 00:04:00:00:00:01 -table_add ipv4_lpm set_nhop 10.0.0.10/32 => 10.0.0.10 1 -table_add ipv4_lpm set_nhop 10.0.1.10/32 => 10.0.1.10 2 -table_add drop_heavy_hitter heavy_hitter_drop 1 0 diff --git a/Teaching/Stanford_CS344_2018/simple_router.p4 b/Teaching/Stanford_CS344_2018/simple_router.p4 deleted file mode 100644 index db9f0e50d..000000000 --- a/Teaching/Stanford_CS344_2018/simple_router.p4 +++ /dev/null @@ -1,210 +0,0 @@ -#include -#include - -#include "header.p4" -#include "parser.p4" - -const bit<16> MAX_ADDRESS = 0x1F; -const bit<16> THRESHOLD_COUNT = 8; - -register>(32w1) last_reset_time; -register>(32w32) hashtable_a0; -register>(32w32) hashtable_a1; -register>(32w32) hashtable_a2; -register>(32w32) hashtable_a3; -register>(32w32) hashtable_b0; -register>(32w32) hashtable_b1; -register>(32w32) hashtable_b2; -register>(32w32) hashtable_b3; -register>(32w1) is_a_active; - - -control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - action rewrite_mac(bit<48> smac) { - hdr.ethernet.srcAddr = smac; - } - action egress_drop() { - mark_to_drop(); - } - table send_frame { - actions = { - rewrite_mac; - egress_drop; - NoAction; - } - key = { - standard_metadata.egress_port: exact; - } - size = 256; - default_action = NoAction(); - } - apply { - if (hdr.ipv4.isValid()) { - send_frame.apply(); - } - } -} - -control HashtableUpdate(in register> hashtable, - in HashAlgorithm algo, - in headers hdr, - inout bit<32> bytecount) { - - action update_hashtable() { - /* TODO - Use a hashfunction and calculate the corresponding address - of the count-min sketch based on its five-tuple (hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr, hdr.ipv4.protocol, hdr.tcp.srcPort, hdr.tcp.dstPort) - Read the previous contents of that address, add the packet length to - the previous bytecount, update the register address and keep a - copy of the value in the metadata. - */ - } - - apply { - if (hdr.ipv4.isValid()) { - update_hashtable(); - } - } -} - -control HHD(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - - HashtableUpdate() update_hashtable_a0; - HashtableUpdate() update_hashtable_a1; - HashtableUpdate() update_hashtable_a2; - HashtableUpdate() update_hashtable_a3; - HashtableUpdate() update_hashtable_b0; - HashtableUpdate() update_hashtable_b1; - HashtableUpdate() update_hashtable_b2; - HashtableUpdate() update_hashtable_b3; - - action calculate_age() { - /* TODO - Read the last_reset_time register and calculate - how long has it been since last reset of sketch A based - on standard_metadata.ingress_global_timestamp. - Save the result in meta.hhd.filter_age. - */ - } - - action set_threshold(bit<32> threshold) { - /* TODO - Copy the threshlod to metamhhd.threshold - */ - } - - action set_filter() { - /* TODO - Check whether count-min sketch A is active - and set meta.hhd.is_a_active flag appropriately - */ - } - - action heavy_hitter_drop() { - mark_to_drop(); - } - - action decide_heavy_hitter() { - /* TODO - Based on whether A is active and the appropriate - meta.hhd.value_xx values, decide, whether - the packet belongs to a heavy hitter flow or not - and set meta.hhd.is_heavy_hitter flag. - */ - } - - - table threshold_table { - key = { - meta.hhd.filter_age : ternary; - } - - actions = { - set_threshold; - } - - size = THRESHOLD_COUNT; - } - - table drop_heavy_hitter { - key = { - meta.hhd.is_heavy_hitter : exact; - } - - actions = { - heavy_hitter_drop; - NoAction; - } - size = 2; - default_action = NoAction(); - } - - apply { - calculate_age(); - set_filter(); - threshold_table.apply(); - update_hashtable_a0.apply(hashtable_a0, HashAlgorithm.crc32, hdr, meta.hhd.value_a0); - update_hashtable_a1.apply(hashtable_a1, HashAlgorithm.crc32_custom, hdr, meta.hhd.value_a1); - update_hashtable_a2.apply(hashtable_a2, HashAlgorithm.crc16, hdr, meta.hhd.value_a2); - update_hashtable_a3.apply(hashtable_a3, HashAlgorithm.crc16_custom, hdr, meta.hhd.value_a3); - update_hashtable_b0.apply(hashtable_b0, HashAlgorithm.crc32, hdr, meta.hhd.value_b0); - update_hashtable_b1.apply(hashtable_b1, HashAlgorithm.crc32_custom, hdr, meta.hhd.value_b1); - update_hashtable_b2.apply(hashtable_b2, HashAlgorithm.crc16, hdr, meta.hhd.value_b2); - update_hashtable_b3.apply(hashtable_b3, HashAlgorithm.crc16_custom, hdr, meta.hhd.value_b3); - decide_heavy_hitter(); - drop_heavy_hitter.apply(); - } - -} - -control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - action ingress_drop() { - mark_to_drop(); - } - action set_nhop(bit<32> nhop_ipv4, bit<9> port) { - meta.ingress_metadata.nhop_ipv4 = nhop_ipv4; - standard_metadata.egress_spec = port; - hdr.ipv4.ttl = hdr.ipv4.ttl + 8w255; - } - action set_dmac(bit<48> dmac) { - hdr.ethernet.dstAddr = dmac; - } - table ipv4_lpm { - actions = { - ingress_drop; - set_nhop; - NoAction; - } - key = { - hdr.ipv4.dstAddr: lpm; - } - size = 1024; - default_action = NoAction(); - } - table forward { - actions = { - set_dmac; - ingress_drop; - NoAction; - } - key = { - meta.ingress_metadata.nhop_ipv4: exact; - } - size = 512; - default_action = NoAction(); - } - HHD() hhd; - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - forward.apply(); - hhd.apply(hdr, meta, standard_metadata); - } - } -} - -V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main; diff --git a/Teaching/Stanford_CS344_2018/solution/simple_router.p4 b/Teaching/Stanford_CS344_2018/solution/simple_router.p4 deleted file mode 100644 index 5b75ea8ee..000000000 --- a/Teaching/Stanford_CS344_2018/solution/simple_router.p4 +++ /dev/null @@ -1,222 +0,0 @@ -#include -#include - -#include "header.p4" -#include "parser.p4" - -const bit<16> MAX_ADDRESS = 0x1F; -const bit<16> THRESHOLD_COUNT = 8; - -register>(32w1) last_reset_time; -register>(32w32) hashtable_a0; -register>(32w32) hashtable_a1; -register>(32w32) hashtable_a2; -register>(32w32) hashtable_a3; -register>(32w32) hashtable_b0; -register>(32w32) hashtable_b1; -register>(32w32) hashtable_b2; -register>(32w32) hashtable_b3; -register>(32w1) is_a_active; - - -control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - action rewrite_mac(bit<48> smac) { - hdr.ethernet.srcAddr = smac; - } - action egress_drop() { - mark_to_drop(); - } - table send_frame { - actions = { - rewrite_mac; - egress_drop; - NoAction; - } - key = { - standard_metadata.egress_port: exact; - } - size = 256; - default_action = NoAction(); - } - apply { - if (hdr.ipv4.isValid()) { - send_frame.apply(); - } - } -} - -control HashtableUpdate(in register> hashtable, - in HashAlgorithm algo, - in headers hdr, - inout bit<32> bytecount) { - - action update_hashtable() { - bit<32> hashtable_address; - hash(hashtable_address, - algo, - 32w0, - { hdr.ipv4.srcAddr, - hdr.ipv4.dstAddr, - hdr.ipv4.protocol, - hdr.tcp.srcPort, - hdr.tcp.dstPort }, - MAX_ADDRESS); - hashtable.read(bytecount, hashtable_address); - bytecount = bytecount + (bit<32>)hdr.ipv4.totalLen; - hashtable.write(hashtable_address, bytecount); - - } - - apply { - if (hdr.ipv4.isValid()) { - update_hashtable(); - } - } -} - -control HHD(inout headers hdr, - inout metadata meta, - inout standard_metadata_t standard_metadata) { - - - HashtableUpdate() update_hashtable_a0; - HashtableUpdate() update_hashtable_a1; - HashtableUpdate() update_hashtable_a2; - HashtableUpdate() update_hashtable_a3; - HashtableUpdate() update_hashtable_b0; - HashtableUpdate() update_hashtable_b1; - HashtableUpdate() update_hashtable_b2; - HashtableUpdate() update_hashtable_b3; - - action calculate_age() { - last_reset_time.read(meta.hhd.filter_age, 32w0); - meta.hhd.filter_age = standard_metadata.ingress_global_timestamp - meta.hhd.filter_age; - } - - action set_threshold(bit<32> threshold) { - meta.hhd.threshold = threshold; - } - - action set_filter() { - is_a_active.read(meta.hhd.is_a_active, 32w0); - } - - action heavy_hitter_drop() { - mark_to_drop(); - } - - action decide_heavy_hitter() { - if (meta.hhd.is_a_active == 1w1) { - if (meta.hhd.value_a0 > meta.hhd.threshold && - meta.hhd.value_a1 > meta.hhd.threshold && - meta.hhd.value_a2 > meta.hhd.threshold && - meta.hhd.value_a3 > meta.hhd.threshold) { - - meta.hhd.is_heavy_hitter = 1w1; - } else { - meta.hhd.is_heavy_hitter = 1w0; - } - } else { - if (meta.hhd.value_b0 > meta.hhd.threshold && - meta.hhd.value_b1 > meta.hhd.threshold && - meta.hhd.value_b2 > meta.hhd.threshold && - meta.hhd.value_b3 > meta.hhd.threshold) { - - meta.hhd.is_heavy_hitter = 1w1; - } else { - meta.hhd.is_heavy_hitter = 1w0; - } - } - } - - - table threshold_table { - key = { - meta.hhd.filter_age : ternary; - } - - actions = { - set_threshold; - } - - size = THRESHOLD_COUNT; - } - - table drop_heavy_hitter { - key = { - meta.hhd.is_heavy_hitter : exact; - } - - actions = { - heavy_hitter_drop; - NoAction; - } - size = 2; - default_action = NoAction(); - } - - apply { - calculate_age(); - set_filter(); - threshold_table.apply(); - update_hashtable_a0.apply(hashtable_a0, HashAlgorithm.crc32, hdr, meta.hhd.value_a0); - update_hashtable_a1.apply(hashtable_a1, HashAlgorithm.crc32_custom, hdr, meta.hhd.value_a1); - update_hashtable_a2.apply(hashtable_a2, HashAlgorithm.crc16, hdr, meta.hhd.value_a2); - update_hashtable_a3.apply(hashtable_a3, HashAlgorithm.crc16_custom, hdr, meta.hhd.value_a3); - update_hashtable_b0.apply(hashtable_b0, HashAlgorithm.crc32, hdr, meta.hhd.value_b0); - update_hashtable_b1.apply(hashtable_b1, HashAlgorithm.crc32_custom, hdr, meta.hhd.value_b1); - update_hashtable_b2.apply(hashtable_b2, HashAlgorithm.crc16, hdr, meta.hhd.value_b2); - update_hashtable_b3.apply(hashtable_b3, HashAlgorithm.crc16_custom, hdr, meta.hhd.value_b3); - decide_heavy_hitter(); - drop_heavy_hitter.apply(); - } - -} - -control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { - action ingress_drop() { - mark_to_drop(); - } - action set_nhop(bit<32> nhop_ipv4, bit<9> port) { - meta.ingress_metadata.nhop_ipv4 = nhop_ipv4; - standard_metadata.egress_spec = port; - hdr.ipv4.ttl = hdr.ipv4.ttl + 8w255; - } - action set_dmac(bit<48> dmac) { - hdr.ethernet.dstAddr = dmac; - } - table ipv4_lpm { - actions = { - ingress_drop; - set_nhop; - NoAction; - } - key = { - hdr.ipv4.dstAddr: lpm; - } - size = 1024; - default_action = NoAction(); - } - table forward { - actions = { - set_dmac; - ingress_drop; - NoAction; - } - key = { - meta.ingress_metadata.nhop_ipv4: exact; - } - size = 512; - default_action = NoAction(); - } - HHD() hhd; - apply { - if (hdr.ipv4.isValid()) { - ipv4_lpm.apply(); - forward.apply(); - hhd.apply(hdr, meta, standard_metadata); - } - } -} - -V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main; diff --git a/Teaching/utils/Makefile b/Teaching/utils/Makefile deleted file mode 100644 index 3483f081c..000000000 --- a/Teaching/utils/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -BUILD_DIR = build -PCAP_DIR = pcaps -LOG_DIR = logs - -TOPO = topology.json -P4C = p4c-bm2-ss -RUN_SCRIPT = ../utils/run_exercise.py - -source := $(wildcard *.p4) -outfile := $(source:.p4=.json) - -compiled_json := $(BUILD_DIR)/$(outfile) - -# Define NO_P4 to start BMv2 without a program -ifndef NO_P4 -run_args += -j $(compiled_json) -endif - -# Set BMV2_SWITCH_EXE to override the BMv2 target -ifdef BMV2_SWITCH_EXE -run_args += -b $(BMV2_SWITCH_EXE) -endif - -all: run - -run: build - sudo python $(RUN_SCRIPT) -t $(TOPO) $(run_args) - -stop: - sudo mn -c - -build: dirs $(compiled_json) - -$(BUILD_DIR)/%.json: %.p4 - $(P4C) --p4v 16 $(P4C_ARGS) -o $@ $< - -dirs: - mkdir -p $(BUILD_DIR) $(PCAP_DIR) $(LOG_DIR) - -clean: stop - rm -f *.pcap - rm -rf $(BUILD_DIR) $(PCAP_DIR) $(LOG_DIR) diff --git a/Teaching/utils/mininet/appcontroller.py b/Teaching/utils/mininet/appcontroller.py deleted file mode 100644 index 57d2fca39..000000000 --- a/Teaching/utils/mininet/appcontroller.py +++ /dev/null @@ -1,93 +0,0 @@ -import subprocess - -from shortest_path import ShortestPath - -class AppController: - - def __init__(self, manifest=None, target=None, topo=None, net=None, links=None): - self.manifest = manifest - self.target = target - self.conf = manifest['targets'][target] - self.topo = topo - self.net = net - self.links = links - - def read_entries(self, filename): - entries = [] - with open(filename, 'r') as f: - for line in f: - line = line.strip() - if line == '': continue - entries.append(line) - return entries - - def add_entries(self, thrift_port=9090, sw=None, entries=None): - assert entries - if sw: thrift_port = sw.thrift_port - - print '\n'.join(entries) - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE) - p.communicate(input='\n'.join(entries)) - - def read_register(self, register, idx, thrift_port=9090, sw=None): - if sw: thrift_port = sw.thrift_port - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate(input="register_read %s %d" % (register, idx)) - reg_val = filter(lambda l: ' %s[%d]' % (register, idx) in l, stdout.split('\n'))[0].split('= ', 1)[1] - return long(reg_val) - - def start(self): - shortestpath = ShortestPath(self.links) - entries = {} - for sw in self.topo.switches(): - entries[sw] = [] - if 'switches' in self.conf and sw in self.conf['switches'] and 'entries' in self.conf['switches'][sw]: - extra_entries = self.conf['switches'][sw]['entries'] - if type(extra_entries) == list: # array of entries - entries[sw] += extra_entries - else: # path to file that contains entries - entries[sw] += self.read_entries(extra_entries) - - for host_name in self.topo._host_links: - h = self.net.get(host_name) - for link in self.topo._host_links[host_name].values(): - sw = link['sw'] - iface = h.intfNames()[link['idx']] - # use mininet to set ip and mac to let it know the change - h.setIP(link['host_ip'], 24) - h.setMAC(link['host_mac']) - h.cmd('arp -i %s -s %s %s' % (iface, link['sw_ip'], link['sw_mac'])) - h.cmd('ethtool --offload %s rx off tx off' % iface) - h.cmd('ip route add %s dev %s' % (link['sw_ip'], iface)) - h.setDefaultRoute("via %s" % link['sw_ip']) - - for h in self.net.hosts: - h_link = self.topo._host_links[h.name].values()[0] - for sw in self.net.switches: - path = shortestpath.get(sw.name, h.name, exclude=lambda n: n[0]=='h') - if not path: continue - if not path[1][0] == 's': continue # next hop is a switch - sw_link = self.topo._sw_links[sw.name][path[1]] - - for h2 in self.net.hosts: - if h == h2: continue - path = shortestpath.get(h.name, h2.name, exclude=lambda n: n[0]=='h') - if not path: continue - h_link = self.topo._host_links[h.name][path[1]] - h2_link = self.topo._host_links[h2.name].values()[0] - h.cmd('ip route add %s via %s' % (h2_link['host_ip'], h_link['sw_ip'])) - - - print "**********" - print "Configuring entries in p4 tables" - for sw_name in entries: - print - print "Configuring switch... %s" % sw_name - sw = self.net.get(sw_name) - if entries[sw_name]: - self.add_entries(sw=sw, entries=entries[sw_name]) - print "Configuration complete." - print "**********" - - def stop(self): - pass diff --git a/Teaching/utils/mininet/apptopo.py b/Teaching/utils/mininet/apptopo.py deleted file mode 100644 index 3491a3d29..000000000 --- a/Teaching/utils/mininet/apptopo.py +++ /dev/null @@ -1,70 +0,0 @@ -from mininet.topo import Topo - -class AppTopo(Topo): - - def __init__(self, links, latencies={}, manifest=None, target=None, - log_dir="/tmp", bws={}, **opts): - Topo.__init__(self, **opts) - - nodes = sum(map(list, zip(*links)), []) - host_names = sorted(list(set(filter(lambda n: n[0] == 'h', nodes)))) - sw_names = sorted(list(set(filter(lambda n: n[0] == 's', nodes)))) - sw_ports = dict([(sw, []) for sw in sw_names]) - - self._host_links = {} - self._sw_links = dict([(sw, {}) for sw in sw_names]) - - for sw_name in sw_names: - self.addSwitch(sw_name, log_file="%s/%s.log" %(log_dir, sw_name)) - - for host_name in host_names: - host_num = int(host_name[1:]) - - self.addHost(host_name) - - self._host_links[host_name] = {} - host_links = filter(lambda l: l[0]==host_name or l[1]==host_name, links) - - sw_idx = 0 - for link in host_links: - sw = link[0] if link[0] != host_name else link[1] - sw_num = int(sw[1:]) - assert sw[0]=='s', "Hosts should be connected to switches, not " + str(sw) - host_ip = "10.0.%d.%d" % (sw_num, host_num) - host_mac = '00:00:00:00:%02x:%02x' % (sw_num, host_num) - delay_key = ''.join([host_name, sw]) - delay = latencies[delay_key] if delay_key in latencies else '0ms' - bw = bws[delay_key] if delay_key in bws else None - sw_ports[sw].append(host_name) - self._host_links[host_name][sw] = dict( - idx=sw_idx, - host_mac = host_mac, - host_ip = host_ip, - sw = sw, - sw_mac = "00:00:00:00:%02x:%02x" % (sw_num, host_num), - sw_ip = "10.0.%d.%d" % (sw_num, 254), - sw_port = sw_ports[sw].index(host_name)+1 - ) - self.addLink(host_name, sw, delay=delay, bw=bw, - addr1=host_mac, addr2=self._host_links[host_name][sw]['sw_mac']) - sw_idx += 1 - - for link in links: # only check switch-switch links - sw1, sw2 = link - if sw1[0] != 's' or sw2[0] != 's': continue - - delay_key = ''.join(sorted([sw1, sw2])) - delay = latencies[delay_key] if delay_key in latencies else '0ms' - bw = bws[delay_key] if delay_key in bws else None - - self.addLink(sw1, sw2, delay=delay, bw=bw)#, max_queue_size=10) - sw_ports[sw1].append(sw2) - sw_ports[sw2].append(sw1) - - sw1_num, sw2_num = int(sw1[1:]), int(sw2[1:]) - sw1_port = dict(mac="00:00:00:%02x:%02x:00" % (sw1_num, sw2_num), port=sw_ports[sw1].index(sw2)+1) - sw2_port = dict(mac="00:00:00:%02x:%02x:00" % (sw2_num, sw1_num), port=sw_ports[sw2].index(sw1)+1) - - self._sw_links[sw1][sw2] = [sw1_port, sw2_port] - self._sw_links[sw2][sw1] = [sw2_port, sw1_port] - diff --git a/Teaching/utils/mininet/multi_switch_mininet.py b/Teaching/utils/mininet/multi_switch_mininet.py deleted file mode 100755 index 0bb406f61..000000000 --- a/Teaching/utils/mininet/multi_switch_mininet.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/env python2 - -# 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. -# - -import signal -import os -import sys -import subprocess -import argparse -import json -import importlib -import re -from time import sleep - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.link import TCLink -from mininet.log import setLogLevel, info -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host -import apptopo -import appcontroller - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--bmv2-log', help='verbose messages in log file', action="store_true") -parser.add_argument('--cli', help="start the mininet cli", action="store_true") -parser.add_argument('--auto-control-plane', help='enable automatic control plane population', action="store_true") -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - action="store_true") -parser.add_argument('--manifest', '-m', help='Path to manifest file', - type=str, action="store", required=True) -parser.add_argument('--target', '-t', help='Target in manifest file to run', - type=str, action="store", required=True) -parser.add_argument('--log-dir', '-l', help='Location to save output to', - type=str, action="store", required=True) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) - - -args = parser.parse_args() - - -next_thrift_port = args.thrift_port - -def run_command(command): - return os.WEXITSTATUS(os.system(command)) - -def configureP4Switch(**switch_args): - class ConfiguredP4Switch(P4Switch): - def __init__(self, *opts, **kwargs): - global next_thrift_port - kwargs.update(switch_args) - kwargs['thrift_port'] = next_thrift_port - next_thrift_port += 1 - P4Switch.__init__(self, *opts, **kwargs) - return ConfiguredP4Switch - - -def main(): - - with open(args.manifest, 'r') as f: - manifest = json.load(f) - - conf = manifest['targets'][args.target] - params = conf['parameters'] if 'parameters' in conf else {} - - os.environ.update(dict(map(lambda (k,v): (k, str(v)), params.iteritems()))) - - def formatParams(s): - for param in params: - s = re.sub('\$'+param+'(\W|$)', str(params[param]) + r'\1', s) - s = s.replace('${'+param+'}', str(params[param])) - return s - - AppTopo = apptopo.AppTopo - AppController = appcontroller.AppController - - if 'topo_module' in conf: - sys.path.insert(0, os.path.dirname(args.manifest)) - topo_module = importlib.import_module(conf['topo_module']) - AppTopo = topo_module.CustomAppTopo - - if 'controller_module' in conf: - sys.path.insert(0, os.path.dirname(args.manifest)) - controller_module = importlib.import_module(conf['controller_module']) - AppController = controller_module.CustomAppController - - if not os.path.isdir(args.log_dir): - if os.path.exists(args.log_dir): raise Exception('Log dir exists and is not a dir') - os.mkdir(args.log_dir) - os.environ['P4APP_LOGDIR'] = args.log_dir - - - links = [l[:2] for l in conf['links']] - latencies = dict([(''.join(sorted(l[:2])), l[2]) for l in conf['links'] if len(l)>=3]) - bws = dict([(''.join(sorted(l[:2])), l[3]) for l in conf['links'] if len(l)>=4]) - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'latency' not in host: continue - for a, b in links: - if a != host_name and b != host_name: continue - other = a if a != host_name else b - latencies[host_name+other] = host['latency'] - - for l in latencies: - if isinstance(latencies[l], (str, unicode)): - latencies[l] = formatParams(latencies[l]) - else: - latencies[l] = str(latencies[l]) + "ms" - - bmv2_log = args.bmv2_log or ('bmv2_log' in conf and conf['bmv2_log']) - pcap_dump = args.pcap_dump or ('pcap_dump' in conf and conf['pcap_dump']) - - topo = AppTopo(links, latencies, manifest=manifest, target=args.target, - log_dir=args.log_dir, bws=bws) - switchClass = configureP4Switch( - sw_path=args.behavioral_exe, - json_path=args.json, - log_console=bmv2_log, - pcap_dump=pcap_dump) - net = Mininet(topo = topo, - link = TCLink, - host = P4Host, - switch = switchClass, - controller = None) - net.start() - - sleep(1) - - controller = None - if args.auto_control_plane or 'controller_module' in conf: - controller = AppController(manifest=manifest, target=args.target, - topo=topo, net=net, links=links) - controller.start() - - - for h in net.hosts: - h.describe() - - if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: - print message_file.read() - - if args.cli or ('cli' in conf and conf['cli']): - CLI(net) - - stdout_files = dict() - return_codes = [] - host_procs = [] - - - def formatCmd(cmd): - for h in net.hosts: - cmd = cmd.replace(h.name, h.defaultIntf().updateIP()) - return cmd - - def _wait_for_exit(p, host): - print p.communicate() - if p.returncode is None: - p.wait() - print p.communicate() - return_codes.append(p.returncode) - if host_name in stdout_files: - stdout_files[host_name].flush() - stdout_files[host_name].close() - - print '\n'.join(map(lambda (k,v): "%s: %s"%(k,v), params.iteritems())) + '\n' - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'cmd' not in host: continue - - h = net.get(host_name) - stdout_filename = os.path.join(args.log_dir, h.name + '.stdout') - stdout_files[h.name] = open(stdout_filename, 'w') - cmd = formatCmd(host['cmd']) - print h.name, cmd - p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, preexec_fn=os.setpgrp) - if 'startup_sleep' in host: sleep(host['startup_sleep']) - - if 'wait' in host and host['wait']: - _wait_for_exit(p, host_name) - else: - host_procs.append((p, host_name)) - - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: - _wait_for_exit(p, host_name) - - - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: - continue - if p.returncode is None: - run_command('pkill -INT -P %d' % p.pid) - sleep(0.2) - rc = run_command('pkill -0 -P %d' % p.pid) # check if it's still running - if rc == 0: # the process group is still running, send TERM - sleep(1) # give it a little more time to exit gracefully - run_command('pkill -TERM -P %d' % p.pid) - _wait_for_exit(p, host_name) - - if 'after' in conf and 'cmd' in conf['after']: - cmds = conf['after']['cmd'] if type(conf['after']['cmd']) == list else [conf['after']['cmd']] - for cmd in cmds: - os.system(cmd) - - if controller: controller.stop() - - net.stop() - -# if bmv2_log: -# os.system('bash -c "cp /tmp/p4s.s*.log \'%s\'"' % args.log_dir) -# if pcap_dump: -# os.system('bash -c "cp *.pcap \'%s\'"' % args.log_dir) - - bad_codes = [rc for rc in return_codes if rc != 0] - if len(bad_codes): sys.exit(1) - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/Teaching/utils/mininet/p4_mininet.py b/Teaching/utils/mininet/p4_mininet.py deleted file mode 100644 index 8abe79f3a..000000000 --- a/Teaching/utils/mininet/p4_mininet.py +++ /dev/null @@ -1,161 +0,0 @@ -# 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. -# - -from mininet.net import Mininet -from mininet.node import Switch, Host -from mininet.log import setLogLevel, info, error, debug -from mininet.moduledeps import pathCheck -from sys import exit -from time import sleep -import os -import tempfile -import socket - -class P4Host(Host): - def config(self, **params): - r = super(P4Host, self).config(**params) - - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf().name, off) - self.cmd(cmd) - - # disable IPv6 - self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - - return r - - def describe(self, sw_addr=None, sw_mac=None): - print "**********" - print "Network configuration for: %s" % self.name - print "Default interface: %s\t%s\t%s" %( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC() - ) - if sw_addr is not None or sw_mac is not None: - print "Default route to switch: %s (%s)" % (sw_addr, sw_mac) - print "**********" - -class P4Switch(Switch): - """P4 virtual switch""" - device_id = 0 - - def __init__(self, name, sw_path = None, json_path = None, - log_file = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): - Switch.__init__(self, name, **kwargs) - assert(sw_path) - assert(json_path) - # make sure that the provided sw_path is valid - pathCheck(sw_path) - # make sure that the provided JSON file exists - if not os.path.isfile(json_path): - error("Invalid JSON file.\n") - exit(1) - self.sw_path = sw_path - self.json_path = json_path - self.verbose = verbose - self.log_file = log_file - if self.log_file is None: - self.log_file = "/tmp/p4s.{}.log".format(self.name) - self.output = open(self.log_file, 'w') - self.thrift_port = thrift_port - self.pcap_dump = pcap_dump - self.enable_debugger = enable_debugger - self.log_console = log_console - if device_id is not None: - self.device_id = device_id - P4Switch.device_id = max(P4Switch.device_id, device_id) - else: - self.device_id = P4Switch.device_id - P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) - - @classmethod - def setup(cls): - pass - - def check_switch_started(self, pid): - """While the process is running (pid exists), we check if the Thrift - server has been started. If the Thrift server is ready, we assume that - the switch was started successfully. This is only reliable if the Thrift - server is started at the end of the init process""" - while True: - if not os.path.exists(os.path.join("/proc", str(pid))): - return False - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(0.5) - result = sock.connect_ex(("localhost", self.thrift_port)) - if result == 0: - return True - - def start(self, controllers): - "Start up a new P4 switch" - info("Starting P4 switch {}.\n".format(self.name)) - args = [self.sw_path] - for port, intf in self.intfs.items(): - if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) - if self.pcap_dump: - args.append("--pcap") - # args.append("--useFiles") - if self.thrift_port: - args.extend(['--thrift-port', str(self.thrift_port)]) - if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) - P4Switch.device_id += 1 - args.append(self.json_path) - if self.enable_debugger: - args.append("--debugger") - if self.log_console: - args.append("--log-console") - info(' '.join(args) + "\n") - - pid = None - with tempfile.NamedTemporaryFile() as f: - # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &') - self.cmd(' '.join(args) + ' >' + self.log_file + ' 2>&1 & echo $! >> ' + f.name) - pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) - sleep(1) - if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly." - "Check the switch log file.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) - - def stop(self): - "Terminate P4 switch." - self.output.flush() - self.cmd('kill %' + self.sw_path) - self.cmd('wait') - self.deleteIntfs() - - def attach(self, intf): - "Connect a data port" - assert(0) - - def detach(self, intf): - "Disconnect a data port" - assert(0) diff --git a/Teaching/utils/mininet/shortest_path.py b/Teaching/utils/mininet/shortest_path.py deleted file mode 100644 index 971b1b446..000000000 --- a/Teaching/utils/mininet/shortest_path.py +++ /dev/null @@ -1,78 +0,0 @@ -class ShortestPath: - - def __init__(self, edges=[]): - self.neighbors = {} - for edge in edges: - self.addEdge(*edge) - - def addEdge(self, a, b): - if a not in self.neighbors: self.neighbors[a] = [] - if b not in self.neighbors[a]: self.neighbors[a].append(b) - - if b not in self.neighbors: self.neighbors[b] = [] - if a not in self.neighbors[b]: self.neighbors[b].append(a) - - def get(self, a, b, exclude=lambda node: False): - # Shortest path from a to b - return self._recPath(a, b, [], exclude) - - def _recPath(self, a, b, visited, exclude): - if a == b: return [a] - new_visited = visited + [a] - paths = [] - for neighbor in self.neighbors[a]: - if neighbor in new_visited: continue - if exclude(neighbor) and neighbor != b: continue - path = self._recPath(neighbor, b, new_visited, exclude) - if path: paths.append(path) - - paths.sort(key=len) - return [a] + paths[0] if len(paths) else None - -if __name__ == '__main__': - - edges = [ - (1, 2), - (1, 3), - (1, 5), - (2, 4), - (3, 4), - (3, 5), - (3, 6), - (4, 6), - (5, 6), - (7, 8) - - ] - sp = ShortestPath(edges) - - assert sp.get(1, 1) == [1] - assert sp.get(2, 2) == [2] - - assert sp.get(1, 2) == [1, 2] - assert sp.get(2, 1) == [2, 1] - - assert sp.get(1, 3) == [1, 3] - assert sp.get(3, 1) == [3, 1] - - assert sp.get(4, 6) == [4, 6] - assert sp.get(6, 4) == [6, 4] - - assert sp.get(2, 6) == [2, 4, 6] - assert sp.get(6, 2) == [6, 4, 2] - - assert sp.get(1, 6) in [[1, 3, 6], [1, 5, 6]] - assert sp.get(6, 1) in [[6, 3, 1], [6, 5, 1]] - - assert sp.get(2, 5) == [2, 1, 5] - assert sp.get(5, 2) == [5, 1, 2] - - assert sp.get(4, 5) in [[4, 3, 5], [4, 6, 5]] - assert sp.get(5, 4) in [[5, 3, 4], [6, 6, 4]] - - assert sp.get(7, 8) == [7, 8] - assert sp.get(8, 7) == [8, 7] - - assert sp.get(1, 7) == None - assert sp.get(7, 2) == None - diff --git a/Teaching/utils/mininet/single_switch_mininet.py b/Teaching/utils/mininet/single_switch_mininet.py deleted file mode 100755 index e2e76366c..000000000 --- a/Teaching/utils/mininet/single_switch_mininet.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python2 - -# 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. -# - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.log import setLogLevel, info -from mininet.cli import CLI - -from p4_mininet import P4Switch, P4Host - -import argparse -from subprocess import PIPE, Popen -from time import sleep - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--num-hosts', help='Number of hosts to connect to switch', - type=int, action="store", default=2) -parser.add_argument('--mode', choices=['l2', 'l3'], type=str, default='l3') -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--log-file', help='Path to write the switch log file', - type=str, action="store", required=False) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - type=str, action="store", required=False, default=False) -parser.add_argument('--switch-config', help='simple_switch_CLI script to configure switch', - type=str, action="store", required=False, default=False) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) - -args = parser.parse_args() - - -class SingleSwitchTopo(Topo): - "Single switch connected to n (< 256) hosts." - def __init__(self, sw_path, json_path, log_file, - thrift_port, pcap_dump, n, **opts): - # Initialize topology and default options - Topo.__init__(self, **opts) - - switch = self.addSwitch('s1', - sw_path = sw_path, - json_path = json_path, - log_console = True, - log_file = log_file, - thrift_port = thrift_port, - enable_debugger = False, - pcap_dump = pcap_dump) - - for h in xrange(n): - host = self.addHost('h%d' % (h + 1), - ip = "10.0.%d.10/24" % h, - mac = '00:04:00:00:00:%02x' %h) - print "Adding host", str(host) - self.addLink(host, switch) - -def main(): - num_hosts = args.num_hosts - mode = args.mode - - topo = SingleSwitchTopo(args.behavioral_exe, - args.json, - args.log_file, - args.thrift_port, - args.pcap_dump, - num_hosts) - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None) - net.start() - - - sw_mac = ["00:aa:bb:00:00:%02x" % n for n in xrange(num_hosts)] - - sw_addr = ["10.0.%d.1" % n for n in xrange(num_hosts)] - - for n in xrange(num_hosts): - h = net.get('h%d' % (n + 1)) - if mode == "l2": - h.setDefaultRoute("dev %s" % h.defaultIntf().name) - else: - h.setARP(sw_addr[n], sw_mac[n]) - h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n])) - - for n in xrange(num_hosts): - h = net.get('h%d' % (n + 1)) - h.describe(sw_addr[n], sw_mac[n]) - - sleep(1) - - if args.switch_config is not None: - print - print "Reading switch configuration script:", args.switch_config - with open(args.switch_config, 'r') as config_file: - switch_config = config_file.read() - - print "Configuring switch..." - proc = Popen(["simple_switch_CLI"], stdin=PIPE) - proc.communicate(input=switch_config) - - print "Configuration complete." - print - - print "Ready !" - - if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: - print message_file.read() - - CLI( net ) - net.stop() - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/Teaching/utils/netstat.py b/Teaching/utils/netstat.py deleted file mode 100644 index bb12ffdf5..000000000 --- a/Teaching/utils/netstat.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2017-present Open Networking Foundation -# -# 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. -# - -import psutil -def check_listening_on_port(port): - for c in psutil.net_connections(kind='inet'): - if c.status == 'LISTEN' and c.laddr[1] == port: - return True - return False diff --git a/Teaching/utils/p4_mininet.py b/Teaching/utils/p4_mininet.py deleted file mode 100644 index b7fbbcd80..000000000 --- a/Teaching/utils/p4_mininet.py +++ /dev/null @@ -1,162 +0,0 @@ -# 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. -# - -from mininet.net import Mininet -from mininet.node import Switch, Host -from mininet.log import setLogLevel, info, error, debug -from mininet.moduledeps import pathCheck -from sys import exit -import os -import tempfile -import socket -from time import sleep - -from netstat import check_listening_on_port - -SWITCH_START_TIMEOUT = 10 # seconds - -class P4Host(Host): - def config(self, **params): - r = super(Host, self).config(**params) - - self.defaultIntf().rename("eth0") - - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload eth0 %s off" % off - self.cmd(cmd) - - # disable IPv6 - self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - - return r - - def describe(self): - print "**********" - print self.name - print "default interface: %s\t%s\t%s" %( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC() - ) - print "**********" - -class P4Switch(Switch): - """P4 virtual switch""" - device_id = 0 - - def __init__(self, name, sw_path = None, json_path = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): - Switch.__init__(self, name, **kwargs) - assert(sw_path) - assert(json_path) - # make sure that the provided sw_path is valid - pathCheck(sw_path) - # make sure that the provided JSON file exists - if not os.path.isfile(json_path): - error("Invalid JSON file.\n") - exit(1) - self.sw_path = sw_path - self.json_path = json_path - self.verbose = verbose - logfile = "/tmp/p4s.{}.log".format(self.name) - self.output = open(logfile, 'w') - self.thrift_port = thrift_port - if check_listening_on_port(self.thrift_port): - error('%s cannot bind port %d because it is bound by another process\n' % (self.name, self.grpc_port)) - exit(1) - self.pcap_dump = pcap_dump - self.enable_debugger = enable_debugger - self.log_console = log_console - if device_id is not None: - self.device_id = device_id - P4Switch.device_id = max(P4Switch.device_id, device_id) - else: - self.device_id = P4Switch.device_id - P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) - - @classmethod - def setup(cls): - pass - - def check_switch_started(self, pid): - """While the process is running (pid exists), we check if the Thrift - server has been started. If the Thrift server is ready, we assume that - the switch was started successfully. This is only reliable if the Thrift - server is started at the end of the init process""" - while True: - if not os.path.exists(os.path.join("/proc", str(pid))): - return False - if check_listening_on_port(self.thrift_port): - return True - sleep(0.5) - - def start(self, controllers): - "Start up a new P4 switch" - info("Starting P4 switch {}.\n".format(self.name)) - args = [self.sw_path] - for port, intf in self.intfs.items(): - if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) - if self.pcap_dump: - args.append("--pcap") - # args.append("--useFiles") - if self.thrift_port: - args.extend(['--thrift-port', str(self.thrift_port)]) - if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) - P4Switch.device_id += 1 - args.append(self.json_path) - if self.enable_debugger: - args.append("--debugger") - if self.log_console: - args.append("--log-console") - logfile = "/tmp/p4s.{}.log".format(self.name) - info(' '.join(args) + "\n") - - pid = None - with tempfile.NamedTemporaryFile() as f: - # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &') - self.cmd(' '.join(args) + ' >' + logfile + ' 2>&1 & echo $! >> ' + f.name) - pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) - if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) - - def stop(self): - "Terminate P4 switch." - self.output.flush() - self.cmd('kill %' + self.sw_path) - self.cmd('wait') - self.deleteIntfs() - - def attach(self, intf): - "Connect a data port" - assert(0) - - def detach(self, intf): - "Disconnect a data port" - assert(0) diff --git a/Teaching/utils/p4apprunner.py b/Teaching/utils/p4apprunner.py deleted file mode 100755 index 36b9eeab8..000000000 --- a/Teaching/utils/p4apprunner.py +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env python2 -# 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. - -from __future__ import print_function - -import argparse -from collections import OrderedDict -import json -import os -import sys -import tarfile - -parser = argparse.ArgumentParser(description='p4apprunner') -parser.add_argument('--build-dir', help='Directory to build in.', - type=str, action='store', required=False, default='/tmp') -parser.add_argument('--quiet', help='Suppress log messages.', - action='store_true', required=False, default=False) -parser.add_argument('--manifest', help='Path to manifest file.', - type=str, action='store', required=False, default='./p4app.json') -parser.add_argument('app', help='.p4app package to run.', type=str) -parser.add_argument('target', help=('Target to run. Defaults to the first target ' - 'in the package.'), - nargs='?', type=str) - -args = parser.parse_args() - -def log(*items): - if args.quiet != True: - print(*items) - -def log_error(*items): - print(*items, file=sys.stderr) - -def run_command(command): - log('>', command) - return os.WEXITSTATUS(os.system(command)) - -class Manifest: - def __init__(self, program_file, language, target, target_config): - self.program_file = program_file - self.language = language - self.target = target - self.target_config = target_config - -def read_manifest(manifest_file): - manifest = json.load(manifest_file, object_pairs_hook=OrderedDict) - - if 'program' not in manifest: - log_error('No program defined in manifest.') - sys.exit(1) - program_file = manifest['program'] - - if 'language' not in manifest: - log_error('No language defined in manifest.') - sys.exit(1) - language = manifest['language'] - - if 'targets' not in manifest or len(manifest['targets']) < 1: - log_error('No targets defined in manifest.') - sys.exit(1) - - if args.target is not None: - chosen_target = args.target - elif 'default-target' in manifest: - chosen_target = manifest['default-target'] - else: - chosen_target = manifest['targets'].keys()[0] - - if chosen_target not in manifest['targets']: - log_error('Target not found in manifest:', chosen_target) - sys.exit(1) - - return Manifest(program_file, language, chosen_target, manifest['targets'][chosen_target]) - - -def run_compile_bmv2(manifest): - if 'run-before-compile' in manifest.target_config: - commands = manifest.target_config['run-before-compile'] - if not isinstance(commands, list): - log_error('run-before-compile should be a list:', commands) - sys.exit(1) - for command in commands: - run_command(command) - - compiler_args = [] - - if manifest.language == 'p4-14': - compiler_args.append('--p4v 14') - elif manifest.language == 'p4-16': - compiler_args.append('--p4v 16') - else: - log_error('Unknown language:', manifest.language) - sys.exit(1) - - if 'compiler-flags' in manifest.target_config: - flags = manifest.target_config['compiler-flags'] - if not isinstance(flags, list): - log_error('compiler-flags should be a list:', flags) - sys.exit(1) - compiler_args.extend(flags) - - # Compile the program. - output_file = manifest.program_file + '.json' - compiler_args.append('"%s"' % manifest.program_file) - compiler_args.append('-o "%s"' % output_file) - rv = run_command('p4c-bm2-ss %s' % ' '.join(compiler_args)) - - if 'run-after-compile' in manifest.target_config: - commands = manifest.target_config['run-after-compile'] - if not isinstance(commands, list): - log_error('run-after-compile should be a list:', commands) - sys.exit(1) - for command in commands: - run_command(command) - - if rv != 0: - log_error('Compile failed.') - sys.exit(1) - - return output_file - -def run_mininet(manifest): - output_file = run_compile_bmv2(manifest) - - # Run the program using the BMV2 Mininet simple switch. - switch_args = [] - - # We'll place the switch's log file in current (build) folder. - cwd = os.getcwd() - log_file = os.path.join(cwd, manifest.program_file + '.log') - print ("*** Log file %s" % log_file) - switch_args.append('--log-file "%s"' % log_file) - - pcap_dir = os.path.join(cwd) - print ("*** Pcap folder %s" % pcap_dir) - switch_args.append('--pcap-dump "%s" '% pcap_dir) - - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: - - print(file=message) - print('======================================================================', - file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', - file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) - print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI', file=message) - print(file=message) - print('To view the switch log, run this command from your host OS:', file=message) - print(' tail -f %s' % log_file, file=message) - print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) - print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) - - switch_args.append('--cli-message "%s"' % message_file) - - if 'num-hosts' in manifest.target_config: - switch_args.append('--num-hosts %s' % manifest.target_config['num-hosts']) - - if 'switch-config' in manifest.target_config: - switch_args.append('--switch-config "%s"' % manifest.target_config['switch-config']) - - switch_args.append('--behavioral-exe "%s"' % 'simple_switch') - switch_args.append('--json "%s"' % output_file) - - program = '"%s/mininet/single_switch_mininet.py"' % sys.path[0] - return run_command('python2 %s %s' % (program, ' '.join(switch_args))) - -def run_multiswitch(manifest): - output_file = run_compile_bmv2(manifest) - - script_args = [] - cwd = os.getcwd() - log_dir = os.path.join(cwd, cwd + '/logs') - print ("*** Log directory %s" % log_dir) - script_args.append('--log-dir "%s"' % log_dir) - pcap_dir = os.path.join(cwd) - print ("*** Pcap directory %s" % cwd) - script_args.append('--manifest "%s"' % args.manifest) - script_args.append('--target "%s"' % manifest.target) - if 'auto-control-plane' in manifest.target_config and manifest.target_config['auto-control-plane']: - script_args.append('--auto-control-plane' ) - script_args.append('--behavioral-exe "%s"' % 'simple_switch') - script_args.append('--json "%s"' % output_file) - #script_args.append('--cli') - - # Generate a message that will be printed by the Mininet CLI to make - # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: - - print(file=message) - print('======================================================================', - file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', - file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) - print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI --thrift-port ', file=message) - print(file=message) - print('To view a switch log, run this command from your host OS:', file=message) - print(' tail -f %s/.log' % log_dir, file=message) - print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) - print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) - - script_args.append('--cli-message "%s"' % message_file) - - program = '"%s/mininet/multi_switch_mininet.py"' % sys.path[0] - return run_command('python2 %s %s' % (program, ' '.join(script_args))) - -def run_stf(manifest): - output_file = run_compile_bmv2(manifest) - - if not 'test' in manifest.target_config: - log_error('No STF test file provided.') - sys.exit(1) - stf_file = manifest.target_config['test'] - - # Run the program using the BMV2 STF interpreter. - stf_args = [] - stf_args.append('-v') - stf_args.append(os.path.join(args.build_dir, output_file)) - stf_args.append(os.path.join(args.build_dir, stf_file)) - - program = '"%s/stf/bmv2stf.py"' % sys.path[0] - rv = run_command('python2 %s %s' % (program, ' '.join(stf_args))) - if rv != 0: - sys.exit(1) - return rv - -def run_custom(manifest): - output_file = run_compile_bmv2(manifest) - python_path = 'PYTHONPATH=$PYTHONPATH:/scripts/mininet/' - script_args = [] - script_args.append('--behavioral-exe "%s"' % 'simple_switch') - script_args.append('--json "%s"' % output_file) - script_args.append('--cli "%s"' % 'simple_switch_CLI') - if not 'program' in manifest.target_config: - log_error('No mininet program file provided.') - sys.exit(1) - program = manifest.target_config['program'] - rv = run_command('%s python2 %s %s' % (python_path, program, ' '.join(script_args))) - - if rv != 0: - sys.exit(1) - return rv - -def main(): - log('Entering build directory.') - os.chdir(args.build_dir) - - # A '.p4app' package is really just a '.tar.gz' archive. Extract it so we - # can process its contents. - log('Extracting package.') - tar = tarfile.open(args.app) - tar.extractall() - tar.close() - - log('Reading package manifest.') - with open(args.manifest, 'r') as manifest_file: - manifest = read_manifest(manifest_file) - - # Dispatch to the backend implementation for this target. - backend = manifest.target - if 'use' in manifest.target_config: - backend = manifest.target_config['use'] - - if backend == 'mininet': - rc = run_mininet(manifest) - elif backend == 'multiswitch': - rc = run_multiswitch(manifest) - elif backend == 'stf': - rc = run_stf(manifest) - elif backend == 'custom': - rc = run_custom(manifest) - elif backend == 'compile-bmv2': - run_compile_bmv2(manifest) - rc = 0 - else: - log_error('Target specifies unknown backend:', backend) - sys.exit(1) - - sys.exit(rc) - -if __name__ == '__main__': - main() diff --git a/Teaching/utils/p4runtime_switch.py b/Teaching/utils/p4runtime_switch.py deleted file mode 100644 index df919ed20..000000000 --- a/Teaching/utils/p4runtime_switch.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright 2017-present Barefoot Networks, Inc. -# Copyright 2017-present Open Networking Foundation -# -# 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. -# - -import sys, os, tempfile, socket -from time import sleep - -from mininet.node import Switch -from mininet.moduledeps import pathCheck -from mininet.log import info, error, debug - -from p4_mininet import P4Switch, SWITCH_START_TIMEOUT -from netstat import check_listening_on_port - -class P4RuntimeSwitch(P4Switch): - "BMv2 switch with gRPC support" - next_grpc_port = 50051 - - def __init__(self, name, sw_path = None, json_path = None, - grpc_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): - Switch.__init__(self, name, **kwargs) - assert (sw_path) - self.sw_path = sw_path - # make sure that the provided sw_path is valid - pathCheck(sw_path) - - if json_path is not None: - # make sure that the provided JSON file exists - if not os.path.isfile(json_path): - error("Invalid JSON file.\n") - exit(1) - self.json_path = json_path - else: - self.json_path = None - - if grpc_port is not None: - self.grpc_port = grpc_port - else: - self.grpc_port = P4RuntimeSwitch.next_grpc_port - P4RuntimeSwitch.next_grpc_port += 1 - - if check_listening_on_port(self.grpc_port): - error('%s cannot bind port %d because it is bound by another process\n' % (self.name, self.grpc_port)) - exit(1) - - self.verbose = verbose - logfile = "/tmp/p4s.{}.log".format(self.name) - self.output = open(logfile, 'w') - self.pcap_dump = pcap_dump - self.enable_debugger = enable_debugger - self.log_console = log_console - if device_id is not None: - self.device_id = device_id - P4Switch.device_id = max(P4Switch.device_id, device_id) - else: - self.device_id = P4Switch.device_id - P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) - - - def check_switch_started(self, pid): - for _ in range(SWITCH_START_TIMEOUT * 2): - if not os.path.exists(os.path.join("/proc", str(pid))): - return False - if check_listening_on_port(self.grpc_port): - return True - sleep(0.5) - - def start(self, controllers): - info("Starting P4 switch {}.\n".format(self.name)) - args = [self.sw_path] - for port, intf in self.intfs.items(): - if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) - if self.pcap_dump: - args.append("--pcap") - if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) - P4Switch.device_id += 1 - if self.json_path: - args.append(self.json_path) - else: - args.append("--no-p4") - if self.enable_debugger: - args.append("--debugger") - if self.log_console: - args.append("--log-console") - if self.grpc_port: - args.append("-- --grpc-server-addr 0.0.0.0:" + str(self.grpc_port)) - cmd = ' '.join(args) - info(cmd + "\n") - - logfile = "/tmp/p4s.{}.log".format(self.name) - pid = None - with tempfile.NamedTemporaryFile() as f: - self.cmd(cmd + ' >' + logfile + ' 2>&1 & echo $! >> ' + f.name) - pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) - if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) - diff --git a/env.sh b/env.sh deleted file mode 100644 index f56fd7890..000000000 --- a/env.sh +++ /dev/null @@ -1,8 +0,0 @@ -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 ------------------ diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 6e831eb1e..000000000 --- a/examples/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# 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 -- `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 as support -- `resubmit`: how to resubmit packets to the ingress pipelines -- `simple_nat`: a basic implementation of a full-cone NAT for TCP traffic - -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 clone 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 the root directory of this -repository. 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 :) diff --git a/examples/TLV_parsing/README.md b/examples/TLV_parsing/README.md deleted file mode 100644 index a7c74a3e2..000000000 --- a/examples/TLV_parsing/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# 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). diff --git a/examples/TLV_parsing/commands.txt b/examples/TLV_parsing/commands.txt deleted file mode 100644 index 504d25eee..000000000 --- a/examples/TLV_parsing/commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -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 => diff --git a/examples/TLV_parsing/p4src/TLV_parsing.p4 b/examples/TLV_parsing/p4src/TLV_parsing.p4 deleted file mode 100644 index 02df510ac..000000000 --- a/examples/TLV_parsing/p4src/TLV_parsing.p4 +++ /dev/null @@ -1,243 +0,0 @@ -/* -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); -} diff --git a/examples/TLV_parsing/run_switch.sh b/examples/TLV_parsing/run_switch.sh deleted file mode 100755 index 7197818b1..000000000 --- a/examples/TLV_parsing/run_switch.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/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 -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH 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 diff --git a/examples/TLV_parsing/send_one.py b/examples/TLV_parsing/send_one.py deleted file mode 100644 index 7e99b83a3..000000000 --- a/examples/TLV_parsing/send_one.py +++ /dev/null @@ -1,6 +0,0 @@ -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 = "veth1") diff --git a/examples/action_profile/README.md b/examples/action_profile/README.md deleted file mode 100644 index 2ff3f4550..000000000 --- a/examples/action_profile/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# Action Profile - -## Description - -This example gives a skeleton ECMP implementation, using an action profile. A P4 -action profile is mapped to an indirect table in bmv2. More precisely, in our -case, the P4 objects `ecmp_group` and `ecmp_action_profile` are mapped to one -bmv2 indirect match table. This has the limitation that sharing action profiles -between P4 tables is not supported in bmv2. - -### Running the demo - -As always, you can start the switch with `./run_switch.sh` (and wait until -`READY` is displayed). We program the dataplane with the following CLI commands -(from [commands.txt](commands.txt)): - -``` -01. table_indirect_create_group ecmp_group -02. table_indirect_create_member ecmp_group _nop -03. table_indirect_create_member ecmp_group set_ecmp_nexthop 1 -04. table_indirect_create_member ecmp_group set_ecmp_nexthop 2 -05. table_indirect_create_member ecmp_group set_ecmp_nexthop 3 -06. table_indirect_add_member_to_group ecmp_group 1 0 -07. table_indirect_add_member_to_group ecmp_group 3 0 -08. table_indirect_set_default ecmp_group 0 -09. table_indirect_add_with_group ecmp_group 10.0.0.1 => 0 -10. table_dump ecmp_group -``` - -These commands do the following: - -01. create a group, which receives group handle 0 -02. create a member with action _nop and no action data (member handle 0) -03. create a member with action set_ecmp_nexthop and action data port=1 (member handle 1) -04. create a member with action set_ecmp_nexthop and action data port=2 (member handle 2) -05. create a member with action set_ecmp_nexthop and action data port=3 (member handle 3) -06. add member 1 to group 0 -07. add member 3 to group 0 -08. set member 0 (_nop) has the default for table ecmp_group -09. add an entry to table ecmp_group, which maps destination IPv4 address - 10.0.0.1 to group 0 (which includes members 1 and 3) -10. simply dump the table - -These commands are sent automatically when you run `./run_switch.sh`. The last -command should display the following information: - -``` -ecmp_group: -0: 0a000001 => group(0) -members: -0: _nop - -1: set_ecmp_nexthop - 1, -2: set_ecmp_nexthop - 2, -3: set_ecmp_nexthop - 3, -4: set_ecmp_nexthop - 4, -groups: -0: { 1, 3, } -``` - -When we send a packet to the switch (on any port) with destination address -10.0.0.1, the egress port will be set to either 1 or 3. This is because we only -added members 1 and 3 to the group. Which port / member is used for a given -packet is determined by computing a hash over the IP source address and the IP -protocol number. See the P4 program for more details. - -After starting the switch, run `sudo python send_and_count.py`. This Python -script will send 200 packets to the switch and count the pakcets coming out of -ports 2 and 3. The two numbers should be almost the same: - -``` -Sending 200 packets on port 0 -port 1: 106 packets (53%) -port 2: 0 packets (0%) -port 3: 94 packets (47%) -port 4: 0 packets (0%) -``` diff --git a/examples/action_profile/commands.txt b/examples/action_profile/commands.txt deleted file mode 100644 index e7da93c13..000000000 --- a/examples/action_profile/commands.txt +++ /dev/null @@ -1,11 +0,0 @@ -table_indirect_create_group ecmp_group -table_indirect_create_member ecmp_group _nop -table_indirect_create_member ecmp_group set_ecmp_nexthop 1 -table_indirect_create_member ecmp_group set_ecmp_nexthop 2 -table_indirect_create_member ecmp_group set_ecmp_nexthop 3 -table_indirect_create_member ecmp_group set_ecmp_nexthop 4 -table_indirect_add_member_to_group ecmp_group 1 0 -table_indirect_add_member_to_group ecmp_group 3 0 -table_indirect_set_default ecmp_group 0 -table_indirect_add_with_group ecmp_group 10.0.0.1 => 0 -table_dump ecmp_group diff --git a/examples/action_profile/p4src/action_profile.p4 b/examples/action_profile/p4src/action_profile.p4 deleted file mode 100644 index 92757f27c..000000000 --- a/examples/action_profile/p4src/action_profile.p4 +++ /dev/null @@ -1,124 +0,0 @@ -/* -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_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; - } -} - -header_type intrinsic_metadata_t { - fields { - mcast_grp : 4; - egress_rid : 4; - mcast_hash : 16; - lf_field_list: 32; - } -} - -parser start { - return parse_ethernet; -} - -header ethernet_t ethernet; -header ipv4_t ipv4; -metadata intrinsic_metadata_t intrinsic_metadata; - -#define ETHERTYPE_IPV4 0x0800 - -parser parse_ethernet { - extract(ethernet); - return select(ethernet.etherType) { - ETHERTYPE_IPV4: parse_ipv4; - default: ingress; - } -} - -parser parse_ipv4 { - extract(ipv4); - return ingress; -} - -action _drop() { - drop(); -} - -action _nop() { -} - -field_list l3_hash_fields { - ipv4.srcAddr; - ipv4.protocol; -} - -field_list_calculation ecmp_hash { - input { - l3_hash_fields; - } - algorithm : bmv2_hash; - output_width : 16; -} - -action_selector ecmp_selector { - selection_key : ecmp_hash; -} - -action set_ecmp_nexthop(port) { - modify_field(standard_metadata.egress_spec, port); -} - -action_profile ecmp_action_profile { - actions { - _nop; - set_ecmp_nexthop; - } - size : 64; - dynamic_action_selection : ecmp_selector; -} - -table ecmp_group { - reads { - ipv4.dstAddr : exact; - } - action_profile: ecmp_action_profile; - size : 1024; -} - -control ingress { - apply(ecmp_group); -} - -control egress { -} diff --git a/examples/action_profile/run_switch.sh b/examples/action_profile/run_switch.sh deleted file mode 100755 index 254e1a24b..000000000 --- a/examples/action_profile/run_switch.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/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/action_profile.p4 --json action_profile.json -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH action_profile.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 action_profile.json < commands.txt -echo "READY!!!" -fg diff --git a/examples/action_profile/send_and_count.py b/examples/action_profile/send_and_count.py deleted file mode 100644 index d67c93572..000000000 --- a/examples/action_profile/send_and_count.py +++ /dev/null @@ -1,59 +0,0 @@ -from scapy.all import * -import sys -import threading -import time - - -big_lock = threading.Lock() -counts = {} - - -class Receiver(threading.Thread): - def __init__(self, port, veth): - threading.Thread.__init__(self) - self.daemon = True - self.port = port - self.veth = veth - - def received(self, p): - # no need for a lock, each thread is accessing a different key, and the - # dictionary itself is not modified - counts[self.port] += 1 - - def run(self): - sniff(iface=self.veth, prn=lambda x: self.received(x)) - - -def main(): - try: - num_packets = int(sys.argv[1]) - except: - num_packets = 200 - print "Sending", num_packets, "packets on port 0" - - port_map = { - 1: "veth3", - 2: "veth5", - 3: "veth7", - 4: "veth9" - } - - for port in port_map: - counts[port] = 0 - for port, veth in port_map.items(): - Receiver(port, veth).start() - - for i in xrange(num_packets): - src = "11.0.%d.%d" % (i >> 256, i % 256) - p = Ether(src="aa:aa:aa:aa:aa:aa") / IP(dst="10.0.0.1", src=src) / TCP() / "aaaaaaaaaaaaaaaaaaa" - sendp(p, iface="veth1", verbose=0) - - time.sleep(1) - - for port in port_map: - print "port {0}: {1} packets ({2}%)".format( - port, counts[port], (100 * counts[port]) / num_packets - ) - -if __name__ == '__main__': - main() diff --git a/examples/axon/README.md b/examples/axon/README.md deleted file mode 100644 index a12aa2f63..000000000 --- a/examples/axon/README.md +++ /dev/null @@ -1,102 +0,0 @@ -# Axon source routing protocol - -## Description - -This program implements the Axon protocol for source-routed Ethernet -described in the ANCS '10 paper "Axon: A Flexible Substrate for -Source-routed Ethernet". The most notable aspect of the Axon protocol -is that, in addition to maintaining a list of forward hops, it also -builds a list of the input ports the packet was received on. Together, -these hops define a reverse path to the source that uses the same links -as the forward path. - -More specifically, the Axon header format is as follows: - -| | | | | | | -|---|---|---|---|---|---| -| AxonType : 8 | AxonHdrLength : 16 | FwdHopCount : 8 | RevHopCount : 8 | [FwdHops : 8] | [RevHops : 8] | -| | | | | | | - -Note that the number of bits per field both shown above and used in this -program are different from those described in the Axon ANCS '10 paper. -Specifically, the width of the AxonType, FwdHopCount, and RevHopCount -fields have been rounded up to the nearest multiple of 8. - -Upon receiving an Axon packet, an Axon switch performs three operations: - -1. It validates the header length matches the described number of - forward hops and reverse hops (and is less than a maximum length). -2. It pushes the input port of the packet onto the list of reverse hops - and increments the reverse hop count. -3. It pops the head off of the list of forward hops, decrements the - forward hop count, and then uses this port as an output port for the - packet. - -This program implements a switch that only performs these three operations. - -As an example, this program builds upon the P4 concepts introduced by -the EasyRoute protocol, adding in simple TLV processing. Similar to -EasyRoute, the Axon protocol pops the next hop it should follow off of a -list and decrements a header field. However, the EasyRoute program can -avoid TLV parsing by only parsing up to the first hop of the source -route and then removing it. On the other hand, the Axon protocol also -requires that the input port of a packet is pushed onto a list. This -means that at the list of forward hops must be parsed. Because this list -may be variable in length, this program must perform simple TLV parsing. -However, unlike parsing an IP header, this TLV example is not -complicated by issues related to ordering packet headers. Additionally, -this program parses the reverse hops of the Axon header, even though -they do not strictly need to be if only Axon forwarding is performed, -i.e., subsequent packet headers do not need to be parsed. - -Note that the header stacks parsed in this program (`axon_fwdHop` and -`axon_revHop`) can only hold 64 entries, even though the parser could -try to parse up to 256 entries (8 bits). This is because of a -limitation in `p4c-bmv2/p4c_bm/gen_json.py`. If stack sizes of 256 are -used, the script stalls for an extended period of time then generates -the following error: `RuntimeError: maximum recursion depth exceeded`. - -Because of this error and because *parser exceptions* are not yet -supported by bmv2, improperly formatted packets can cause simple\_switch -to crash. In practice, this occurs when IPv6 discovery packets are -received. In order to avoid this problem, like EasyRoute, this program -also adds a 64bit preamble to the start of packets and requires that -this preamble equals 0. However, this only mitigates the problem. A -carefully crafted packet could still exceed the header stack of 64 -entries. - -### Running the demo - -We provide a small demo to let you test the program. It consists of the -following scripts: -- [run_demo.sh](run_demo.sh): compiles the P4 program, starts the switch, - configures the data plane by running the CLI [commands](commands.txt), and - starts the mininet console. -- [receive.py](receive.py): listens for Axon formatted packets. This command is - intended to be run by a mininet host. -- [send.py](send.py): sends Axon formatted packets from one host to another. - This command is intended to be run by a mininet host. - -To run the demo: -./run_demo.sh will compile your code and create the Mininet network described -above. It will also use commands.txt to configure each one of the switches. -Once the network is up and running, you should type the following in the Mininet -CLI: - -- `xterm h1` -- `xterm h3` - -This will open a terminal for you on h1 and h3. - -On h3 run: `./receive.py`. - -On h1 run: `./send.py h1 h3`. - -You should then be able to type messages on h1 and receive them on h3. The -`send.py` program finds the shortest path between h1 and h3 using Dijkstra, then -send correctly-formatted packets to h3 through s1 and s3. Once you are -done testing, quit mininet. .pcap files will be generated for every -interface (9 files: 3 for each of the 3 switches). You can look at the -appropriate files and check that packets are being processed correctly, -e.g., the forward hops and reverse hops are updated appropriately and -the correct output and input ports are used. diff --git a/examples/axon/cleanup b/examples/axon/cleanup deleted file mode 100755 index 4035587d3..000000000 --- a/examples/axon/cleanup +++ /dev/null @@ -1,2 +0,0 @@ -sudo killall simple_switch -redis-cli FLUSHDB \ No newline at end of file diff --git a/examples/axon/commands.txt b/examples/axon/commands.txt deleted file mode 100644 index 862e557a0..000000000 --- a/examples/axon/commands.txt +++ /dev/null @@ -1,3 +0,0 @@ -table_set_default route_pkt route -table_add route_pkt _drop 0 0 => -table_set_default drop_pkt _drop diff --git a/examples/axon/p4src/axon.p4 b/examples/axon/p4src/axon.p4 deleted file mode 100755 index aae0363d2..000000000 --- a/examples/axon/p4src/axon.p4 +++ /dev/null @@ -1,196 +0,0 @@ -/* -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. -*/ - -/* - * Author: Brent Stephens (brentstephens "at" cs.wisc.edu) - * - * Notes and Future Directions: - * - * 1) In order to avoid problems caused by IPv6 packets, this program follows - * the EasyRoute conventiion and requires that all packets start with a 64b - * preamble that must be all zeros. - * - * 2) Currently this program assumes that hosts are sending Axon packets. In - * some scenarios, it could also be desirable to transparently encapsulate - * and decapsulate standard Ethernet packets based on whether the switch - * port is configured as an Ethernet port or an Axon port as is done on the - * NetFPGA implementation of Axons. - */ - -/* - * The sizes of these fields differ from those published in the Axon - * paper, but they seem like reasonable values. - */ -header_type axon_head_t { - fields { - /* - * If compatibility with EasyRoute is desirable, then a 64-bit preamble - * should also be included as a field of the packet header. - * - * While not strictly necessary and would waste header space on a real - * network, because p4-validate crashes with a stack of fwd/rev hops of - * size 256, if IPv6 discovery packets are not disabled, then we get - * the following error without this: "simple_switch: - * ./include/bm_sim/header_stacks.h:128: Header& - * HeaderStack::get_next(): Assertion `next < headers.size() && "header - * stack full"' failed." - */ - preamble : 64; - - axonType : 8; - axonLength : 16; - fwdHopCount : 8; - revHopCount : 8; - } -} - -header_type axon_hop_t { - fields { - port : 8; - } -} - -header_type my_metadata_t { - fields { - fwdHopCount : 8; - revHopCount : 8; - headerLen : 16; - } -} - -header axon_head_t axon_head; -//header axon_hop_t axon_fwdHop[256]; -//header axon_hop_t axon_revHop[256]; -//XXX: Workaround to avoid a "RuntimeError: maximum recursion depth exceeded" error from p4-validate -/* - * Specifically, using a stack size of 256 causes the following error: - * File "p4c-bmv2/p4c_bm/gen_json.py", line 370, in walk_rec - * if hdr not in header_graph: - * RuntimeError: maximum recursion depth exceeded - */ -header axon_hop_t axon_fwdHop[64]; -header axon_hop_t axon_revHop[64]; -metadata my_metadata_t my_metadata; - -parser start { - /* Enable if compatibility with EasyRoute is desired. */ - return select(current(0, 64)) { - 0: parse_head; - default: ingress; - } - - /* Enable if EasyRoute is being ignored */ - //return parse_head; -} - -parser parse_head { - extract(axon_head); - set_metadata(my_metadata.fwdHopCount, latest.fwdHopCount); - set_metadata(my_metadata.revHopCount, latest.revHopCount); - set_metadata(my_metadata.headerLen, 2 + axon_head.fwdHopCount + axon_head.revHopCount); - return select(latest.fwdHopCount) { - 0: ingress; // Drop packets with no forward hop - default: parse_next_fwdHop; - } -} - -parser parse_next_fwdHop { - // Parse fwdHops until we have parsed them all - return select(my_metadata.fwdHopCount) { - 0x0 : parse_next_revHop; - default : parse_fwdHop; - } -} - -parser parse_fwdHop { - extract(axon_fwdHop[next]); - set_metadata(my_metadata.fwdHopCount, - my_metadata.fwdHopCount - 1); - return parse_next_fwdHop; -} - -parser parse_next_revHop { - // Parse revHops until we have parsed them all - return select(my_metadata.revHopCount) { - 0x0 : ingress; - default : parse_revHop; - } -} - -parser parse_revHop { - extract(axon_revHop[next]); - set_metadata(my_metadata.revHopCount, - my_metadata.revHopCount - 1); - return parse_next_revHop; -} - -action _drop() { - drop(); -} - -action route() { - // Set the output port - modify_field(standard_metadata.egress_spec, axon_fwdHop[0].port); - - // Pop the fwdHop - modify_field(axon_head.fwdHopCount, axon_head.fwdHopCount - 1); - pop(axon_fwdHop, 1); - - // Push the revHop - modify_field(axon_head.revHopCount, axon_head.revHopCount + 1); - push(axon_revHop, 1); - modify_field(axon_revHop[0].port, standard_metadata.ingress_port); - - // Because we push and pop one port, the total length of the header does - // not change and thus does not need to be updated. -} - -table drop_pkt { - actions { - _drop; - } - size: 1; -} - -/* Question: will this drop packets that did not have a forward hop? */ -table route_pkt { - reads { - /* Technically axon_head is only written, not read. Is this still - * correct then? */ - axon_head: valid; - - axon_fwdHop[0]: valid; // Is using axon_fwdHop[0] correct? - } - actions { - _drop; - route; - } - size: 1; -} - -control ingress { - // Drop packets whose length does not equal the total length of the header - if (axon_head.axonLength != my_metadata.headerLen) { - apply(drop_pkt); - } - else { - apply(route_pkt); - } -} - -control egress { - // leave empty -} diff --git a/examples/axon/receive.py b/examples/axon/receive.py deleted file mode 100755 index 841cc5052..000000000 --- a/examples/axon/receive.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/python - -# 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. - -# -# Author: Brent Stephens -# - -from scapy.all import sniff, sendp -from scapy.all import Packet -from scapy.all import ShortField, IntField, LongField, BitField - -import sys -import struct - -def handle_pkt(pkt): - pkt = str(pkt) - if len(pkt) < 13: return - preamble = pkt[:8] - preamble_exp = "\x00" * 8 - if preamble != preamble_exp: return - axonType = pkt[8] - if axonType != "\x00": return - axonLength = struct.unpack("!H", pkt[9:11])[0] - fwdHopCount = struct.unpack("B", pkt[11])[0] - revHopCount = struct.unpack("B", pkt[12])[0] - if fwdHopCount != 0: - print 'received a packet that has not been fully forwarded' - if revHopCount <= 0: - print 'received a packet that has no reverse hops' - if axonLength != 2 + fwdHopCount + revHopCount: - print 'received a packet with either an incorrect axonLength, fwdHopCount, or revHopCount' - msg = pkt[11 + axonLength:] - print msg - sys.stdout.flush() - - # Optional debugging - #print 'axonLength:', axonLength - #print 'fwdHopCount:', fwdHopCount - #print 'revHopCount:', revHopCount - #print pkt - -def main(): - sniff(iface = "eth0", - prn = lambda x: handle_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/examples/axon/run_demo.sh b/examples/axon/run_demo.sh deleted file mode 100755 index 8799b95dc..000000000 --- a/examples/axon/run_demo.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/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/tools/runtime_CLI.py - -$P4C_BM_SCRIPT p4src/axon.p4 --json axon.json -# This gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \ - --behavioral-exe $SWITCH_PATH \ - --json axon.json \ - --cli $CLI_PATH diff --git a/examples/axon/send.py b/examples/axon/send.py deleted file mode 100755 index 6d32fa859..000000000 --- a/examples/axon/send.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/python - -# 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. - -from scapy.all import sniff, sendp -from scapy.all import Packet -from scapy.all import ByteField, ShortField, IntField, LongField, BitField - -import networkx as nx - -import sys - -class AxonRoute(Packet): - name = "AxonRoute" - fields_desc = [ - LongField("preamble", 0), - ByteField("axonType", 0), - ShortField("axonLength", 0), - ByteField("fwdHopCount", 0), - ByteField("revHopCount", 0) - ] - -def read_topo(): - nb_hosts = 0 - nb_switches = 0 - links = [] - with open("topo.txt", "r") as f: - line = f.readline()[:-1] - w, nb_switches = line.split() - assert(w == "switches") - line = f.readline()[:-1] - w, nb_hosts = line.split() - assert(w == "hosts") - for line in f: - if not f: break - a, b = line.split() - links.append( (a, b) ) - return int(nb_hosts), int(nb_switches), links - -def main(): - if len(sys.argv) != 3: - print "Usage: send.py [this_host] [target_host]" - print "For example: send.py h1 h2" - sys.exit(1) - - src, dst = sys.argv[1:] - - nb_hosts, nb_switches, links = read_topo() - - port_map = {} - - for a, b in links: - if a not in port_map: - port_map[a] = {} - if b not in port_map: - port_map[b] = {} - - assert(b not in port_map[a]) - assert(a not in port_map[b]) - port_map[a][b] = len(port_map[a]) + 1 - port_map[b][a] = len(port_map[b]) + 1 - - - G = nx.Graph() - for a, b in links: - G.add_edge(a, b) - - shortest_paths = nx.shortest_path(G) - shortest_path = shortest_paths[src][dst] - - print "path is:", shortest_path - - port_list = [] - first = shortest_path[1] - for h in shortest_path[2:]: - port_list.append(port_map[first][h]) - first = h - - print "port list is:", port_list - - port_str = "" - for p in port_list: - port_str += chr(p) - - while(1): - msg = raw_input("What do you want to send: ") - - # finding the route - first = None - - p = AxonRoute(axonLength = (2 + len(port_list)), fwdHopCount = len(port_list)) / port_str / msg - print p.show() - sendp(p, iface = "eth0") - # print msg - -if __name__ == '__main__': - main() diff --git a/examples/axon/topo.py b/examples/axon/topo.py deleted file mode 100755 index abfa57c9d..000000000 --- a/examples/axon/topo.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/python - -# 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. - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.log import setLogLevel, info -from mininet.cli import CLI -from mininet.link import TCLink - -from p4_mininet import P4Switch, P4Host - -import argparse -from time import sleep -import os -import subprocess - -_THIS_DIR = os.path.dirname(os.path.realpath(__file__)) -_THRIFT_BASE_PORT = 22222 - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--cli', help='Path to BM CLI', - type=str, action="store", required=True) - -args = parser.parse_args() - -class MyTopo(Topo): - def __init__(self, sw_path, json_path, nb_hosts, nb_switches, links, **opts): - # Initialize topology and default options - Topo.__init__(self, **opts) - - for i in xrange(nb_switches): - switch = self.addSwitch('s%d' % (i + 1), - sw_path = sw_path, - json_path = json_path, - thrift_port = _THRIFT_BASE_PORT + i, - pcap_dump = True, - device_id = i) - - for h in xrange(nb_hosts): - host = self.addHost('h%d' % (h + 1)) - - for a, b in links: - self.addLink(a, b) - -def read_topo(): - nb_hosts = 0 - nb_switches = 0 - links = [] - with open("topo.txt", "r") as f: - line = f.readline()[:-1] - w, nb_switches = line.split() - assert(w == "switches") - line = f.readline()[:-1] - w, nb_hosts = line.split() - assert(w == "hosts") - for line in f: - if not f: break - a, b = line.split() - links.append( (a, b) ) - return int(nb_hosts), int(nb_switches), links - - -def main(): - nb_hosts, nb_switches, links = read_topo() - - topo = MyTopo(args.behavioral_exe, - args.json, - nb_hosts, nb_switches, links) - - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None ) - net.start() - - for n in xrange(nb_hosts): - h = net.get('h%d' % (n + 1)) - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload eth0 %s off" % off - print cmd - h.cmd(cmd) - print "disable ipv6" - h.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") - h.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") - h.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") - h.cmd("sysctl -w net.ipv4.tcp_congestion_control=reno") - h.cmd("iptables -I OUTPUT -p icmp --icmp-type destination-unreachable -j DROP") - - sleep(1) - - for i in xrange(nb_switches): - cmd = [args.cli, "--json", args.json, - "--thrift-port", str(_THRIFT_BASE_PORT + i)] - with open("commands.txt", "r") as f: - print " ".join(cmd) - try: - output = subprocess.check_output(cmd, stdin = f) - print output - except subprocess.CalledProcessError as e: - print e - print e.output - - sleep(1) - - print "Ready !" - - CLI( net ) - net.stop() - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/examples/axon/topo.txt b/examples/axon/topo.txt deleted file mode 100644 index 08f8387b1..000000000 --- a/examples/axon/topo.txt +++ /dev/null @@ -1,8 +0,0 @@ -switches 3 -hosts 3 -h1 s1 -h2 s2 -h3 s3 -s1 s2 -s1 s3 -s2 s3 diff --git a/examples/copy_to_cpu/README.md b/examples/copy_to_cpu/README.md deleted file mode 100644 index f85f04f64..000000000 --- a/examples/copy_to_cpu/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# 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 (veth7) and print a hexdump - of them -- [send_one.py](send_one.py): send one simple IPv4 packet on port 0 (veth1) - -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. diff --git a/examples/copy_to_cpu/commands.txt b/examples/copy_to_cpu/commands.txt deleted file mode 100644 index 906c31f03..000000000 --- a/examples/copy_to_cpu/commands.txt +++ /dev/null @@ -1,4 +0,0 @@ -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 diff --git a/examples/copy_to_cpu/p4src/copy_to_cpu.p4 b/examples/copy_to_cpu/p4src/copy_to_cpu.p4 deleted file mode 100644 index 1517ead8f..000000000 --- a/examples/copy_to_cpu/p4src/copy_to_cpu.p4 +++ /dev/null @@ -1,103 +0,0 @@ -/* -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); -} diff --git a/examples/copy_to_cpu/receive.py b/examples/copy_to_cpu/receive.py deleted file mode 100644 index 19d37f53b..000000000 --- a/examples/copy_to_cpu/receive.py +++ /dev/null @@ -1,3 +0,0 @@ -from scapy.all import * - -sniff(iface = "veth7", prn = lambda x: hexdump(x)) diff --git a/examples/copy_to_cpu/run_switch.sh b/examples/copy_to_cpu/run_switch.sh deleted file mode 100755 index efdbaf833..000000000 --- a/examples/copy_to_cpu/run_switch.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/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 -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH 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 diff --git a/examples/copy_to_cpu/send_one.py b/examples/copy_to_cpu/send_one.py deleted file mode 100644 index c261ea1c2..000000000 --- a/examples/copy_to_cpu/send_one.py +++ /dev/null @@ -1,6 +0,0 @@ -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 = "veth1") diff --git a/examples/counter/README.md b/examples/counter/README.md deleted file mode 100644 index b1efeaf0f..000000000 --- a/examples/counter/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Counter - -## Description - -This directory is a playground for counters in P4. The P4 program includes a -direct counter and an indirect counter. - -### Running the demo - -To run the demo: -- start the switch with `./run_switch.sh`, wait until `READY` is displayed. -- run the demo script with `./run_demo.sh`. The script will send packets / - display the counter values / reset the counters. Look at the script for more - details. diff --git a/examples/counter/commands.txt b/examples/counter/commands.txt deleted file mode 100644 index 69ba5f794..000000000 --- a/examples/counter/commands.txt +++ /dev/null @@ -1,2 +0,0 @@ -table_set_default m_table m_action -table_add m_table m_action aa:bb:cc:dd:ee:ff => 0 diff --git a/examples/counter/p4src/counter.p4 b/examples/counter/p4src/counter.p4 deleted file mode 100644 index beff9c0ce..000000000 --- a/examples/counter/p4src/counter.p4 +++ /dev/null @@ -1,92 +0,0 @@ -/* -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 { - register_tmp : 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() { -} - -counter my_indirect_counter { - type: packets; - static: m_table; - instance_count: 16384; -} - -counter my_direct_counter { - type: bytes; - direct: m_table; -} - -action m_action(idx) { - count(my_indirect_counter, idx); - drop(); -} - -table m_table { - reads { - ethernet.srcAddr : exact; - } - actions { - m_action; _nop; - } - size : 16384; -} - -control ingress { - apply(m_table); -} - -control egress { -} diff --git a/examples/counter/run_demo.sh b/examples/counter/run_demo.sh deleted file mode 100755 index 11b4da853..000000000 --- a/examples/counter/run_demo.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/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 - -CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI - -echo "resetting counters" -echo "counter_reset my_indirect_counter" | $CLI_PATH counter.json -echo "counter_reset my_direct_counter" | $CLI_PATH counter.json -echo -echo -echo -echo "displaying counters [0]" -echo "counter_read my_indirect_counter 0" | $CLI_PATH counter.json -echo "counter_read my_direct_counter 0" | $CLI_PATH counter.json -echo -echo -echo -echo "sending 1 packet of size 70" -sudo python send_one.py -sleep 1 -echo -echo -echo -echo "displaying counters [0]" -echo "counter_read my_indirect_counter 0" | $CLI_PATH counter.json -echo "counter_read my_direct_counter 0" | $CLI_PATH counter.json -echo -echo -echo -echo "sending 3 packets of size 70" -sudo python send_one.py -sudo python send_one.py -sudo python send_one.py -sleep 1 -echo -echo -echo -echo "displaying counters [0]" -echo "counter_read my_indirect_counter 0" | $CLI_PATH counter.json -echo "counter_read my_direct_counter 0" | $CLI_PATH counter.json -echo -echo -echo -echo "resetting counters" -echo "counter_reset my_indirect_counter" | $CLI_PATH counter.json -echo "counter_reset my_direct_counter" | $CLI_PATH counter.json diff --git a/examples/counter/run_switch.sh b/examples/counter/run_switch.sh deleted file mode 100755 index 9f1ad44b2..000000000 --- a/examples/counter/run_switch.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/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/counter.p4 --json counter.json -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH counter.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 counter.json < commands.txt -echo "READY!!!" -fg diff --git a/examples/counter/send_one.py b/examples/counter/send_one.py deleted file mode 100644 index ea122ca52..000000000 --- a/examples/counter/send_one.py +++ /dev/null @@ -1,4 +0,0 @@ -from scapy.all import * - -p = Ether(src="aa:bb:cc:dd:ee:ff") / IP(dst="10.0.1.10") / TCP() / "aaaaaaaaaaaaaaaa" -sendp(p, iface = "veth1", verbose = 0) diff --git a/examples/meter/README.md b/examples/meter/README.md deleted file mode 100644 index 44b1e33dd..000000000 --- a/examples/meter/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# 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. diff --git a/examples/meter/commands.txt b/examples/meter/commands.txt deleted file mode 100644 index 04f59cd12..000000000 --- a/examples/meter/commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -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_array_set_rates my_meter 0.0000005:1 0.00001:1 diff --git a/examples/meter/p4src/meter.p4 b/examples/meter/p4src/meter.p4 deleted file mode 100644 index f7cee7f8c..000000000 --- a/examples/meter/p4src/meter.p4 +++ /dev/null @@ -1,114 +0,0 @@ -/* -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. -*/ - -// to test direct meters -// #define USE_DIRECT_METER - -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() { -} - -#ifdef USE_DIRECT_METER -meter my_meter { - type: packets; // or bytes - direct: m_table; - result: meta.meter_tag; -} -#else -meter my_meter { - type: packets; // or bytes - static: m_table; - instance_count: 16384; -} -#endif - -action m_action(meter_idx) { -#ifdef USE_DIRECT_METER - // just a hack to ensure that meter_idx is not removed by compiler - modify_field(standard_metadata.egress_spec, meter_idx); -#else - execute_meter(my_meter, meter_idx, meta.meter_tag); -#endif - 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 { -} diff --git a/examples/meter/run_switch.sh b/examples/meter/run_switch.sh deleted file mode 100755 index 80a637f39..000000000 --- a/examples/meter/run_switch.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/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 -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH 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 diff --git a/examples/meter/send_and_receive.py b/examples/meter/send_and_receive.py deleted file mode 100644 index e4647ac66..000000000 --- a/examples/meter/send_and_receive.py +++ /dev/null @@ -1,44 +0,0 @@ -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="veth3", 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 " - 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="veth1", verbose=0) - print "Sent one" - big_lock.release() - time.sleep(packet_int) - - -if __name__ == '__main__': - main() diff --git a/examples/register/README.md b/examples/register/README.md deleted file mode 100644 index 75e15e82a..000000000 --- a/examples/register/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Register - -## Description - -This program illustrates as simply as possible how to use registers in P4 and -read / write the register state using the bmv2 runtime CLI. - -This will probably change in the future but as of now the data plane is not -doing anything (so don't try to send packets). However you can take a look at -the P4 source code and at the read_register and write_register scripts. You can -also run the following demo: - -### Running the demo - -To run the demo: -- start the switch with `./run_switch.sh`. -- write a register cell with `write_register.sh`. For example: - `./write_register.sh 123 88` to set `my_register[123]` to 88. -- read a register cell with `read_register.sh`. For example: - `./read_register.sh 123` to read `my_register[123]`. diff --git a/examples/register/p4src/register.p4 b/examples/register/p4src/register.p4 deleted file mode 100644 index 11b41729e..000000000 --- a/examples/register/p4src/register.p4 +++ /dev/null @@ -1,89 +0,0 @@ -/* -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 { - register_tmp : 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() { -} - -register my_register { - width: 32; - static: m_table; - instance_count: 16384; -} - -action m_action(register_idx) { - // modify_field_rng_uniform(meta.register_tmp, 100, 200); - // register_write(my_register, register_idx, meta.register_tmp); - register_read(meta.register_tmp, my_register, register_idx); - // TODO -} - -table m_table { - reads { - ethernet.srcAddr : exact; - } - actions { - m_action; _nop; - } - size : 16384; -} - -control ingress { - apply(m_table); -} - -control egress { -} diff --git a/examples/register/read_register.sh b/examples/register/read_register.sh deleted file mode 100755 index 51b480446..000000000 --- a/examples/register/read_register.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/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 - -CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI - -if [ $# -lt 1 ]; then - echo "Please specify register index" - exit 1 -fi -index=$1 - -echo "register_read my_register $index" | $CLI_PATH register.json diff --git a/examples/register/run_switch.sh b/examples/register/run_switch.sh deleted file mode 100755 index 287f23b8e..000000000 --- a/examples/register/run_switch.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/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/register.p4 --json register.json -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH register.json \ - -i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \ - --nanolog ipc:///tmp/bm-0-log.ipc \ - --pcap diff --git a/examples/register/write_register.sh b/examples/register/write_register.sh deleted file mode 100755 index 73bcbe0fe..000000000 --- a/examples/register/write_register.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/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 - -CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI - -if [ $# -lt 2 ]; then - echo "Please specify register index and value, in this order" - exit 1 -fi -index=$1 -value=$2 - -echo "register_write my_register $index $value" | $CLI_PATH register.json diff --git a/examples/resubmit/README.md b/examples/resubmit/README.md deleted file mode 100644 index 21713a752..000000000 --- a/examples/resubmit/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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 deleted file mode 100644 index 2b58ff360..000000000 --- a/examples/resubmit/commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 8d560fee3..000000000 --- a/examples/resubmit/p4src/resubmit.p4 +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 deleted file mode 100755 index 2dbec35a6..000000000 --- a/examples/resubmit/run_switch.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/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 -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH 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 deleted file mode 100644 index cae643fe3..000000000 --- a/examples/resubmit/send_and_receive.py +++ /dev/null @@ -1,29 +0,0 @@ -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() diff --git a/examples/simple_nat/README.md b/examples/simple_nat/README.md deleted file mode 100644 index 9d671a18e..000000000 --- a/examples/simple_nat/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Simple NAT - -## Description - -This program implements a very basic full-cone NAT for TCP traffic (over -IPv4). According to -[Wikipedia](https://en.wikipedia.org/wiki/Network_address_translation#Methods_of_translation), -a full-cone NAT is defined as follows: - -Once an internal address (iAddr:iPort) is mapped to an external address -(eAddr:ePort), any packets from iAddr:iPort are sent through eAddr:ePort. Any -external host can send packets to iAddr:iPort by sending packets to eAddr:ePort. - -Note that this program was built on top of the simple_router P4 program, so you -will find some similarities. - -This program was added to illustrate two things: -- how to re-compute a TCP checksum after having modified the TCP header (which -is required for a TCP NAT) -- how to write a simple app that receives and emits "CPU packets" - -This program is slightly more advanced than most others in this repository, so -we recommend that you become familiar with the copy_to_cpu example before -studying this one. - -The program implements very basic funtionality only. For example it supports -only one internal interface, even though we may extend this in the future. The -Mininet topology used for the demo is the following: - -![Simple NAT topology](resources/topo.png) - -The program in a nutshell: -- non-TCP traffic is dropped. -- when a TCP packet is received on the external interface, for which there is no -mapping, the packet is dropped. -- when a TCP packet is received on the internal interface, for which there is no -mapping, the packet is sent to CPU and 2 new rules are added to the `nat` table -to allow translation and forwarding of TCP packets in both directions. The -packet is then re-injected in the dataplane (to avoid dropping the SYN packet). -- when a TCP packet is received on the either interface, for which there is a -mapping, the appropriate rewrites are executed for the IPv4 and TCP headers and -the packet is forwarded appropriately. - -The most important part of the program is the `nat` table. If you understand -what this table is doing, the rest of the program is mostly IPv4 forwarding. You -should also take a long look at [nat_app.py](nat_app.py), which manages the -mappings and dynamically adds rules to the `nat` table. - -We use 11 as the CPU port. There is a special CPU veth pair (`cpu-veth-0` / -`cpu-veth-1`), which is use by the switch / the app to send and receive CPU -packets. The veth pair is created by `run_demo.sh` and can be destroyed with -`cleanup.sh`. - -### Running the demo - -To run the demo: -- start the 1-switch Mininet topo with `sudo ./run_demo.sh`. -- start the app with `sudo python nat_app.py`. -- create a TCP flow in Mininet from `h1` (internal host) to `h2` (external host) - - `h2 iperf -s&` - - `h1 iperf -c h2` -- you can cleanup the demo with `sudo ./cleanup.sh` diff --git a/examples/simple_nat/cleanup.sh b/examples/simple_nat/cleanup.sh deleted file mode 100755 index 095b66fef..000000000 --- a/examples/simple_nat/cleanup.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/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. - -if [[ $EUID -ne 0 ]]; then - echo "This script should be run using sudo or as the root user" - exit 1 -fi - -killall lt-simple_switch &> /dev/null -mn -c &> /dev/null -intf="cpu-veth-0" -if ip link show $intf &> /dev/null; then - ip link delete $intf type veth -fi diff --git a/examples/simple_nat/commands.txt b/examples/simple_nat/commands.txt deleted file mode 100644 index c4d73f02a..000000000 --- a/examples/simple_nat/commands.txt +++ /dev/null @@ -1,17 +0,0 @@ -table_set_default if_info _drop -table_add if_info set_if_info 1 => 10.0.0.1 00:aa:bb:00:00:04 0 -table_add if_info set_if_info 2 => 192.168.0.1 00:aa:bb:00:00:05 1 -table_set_default nat _drop -table_add nat nat_miss_ext_to_int 1 1 1 0.0.0.0&&&0.0.0.0 0.0.0.0&&&0.0.0.0 0&&&0 0&&&0 => 99 -table_add nat nat_miss_int_to_ext 0 1 1 0.0.0.0&&&0.0.0.0 0.0.0.0&&&0.0.0.0 0&&&0 0&&&0 => 99 -table_set_default ipv4_lpm _drop -table_add ipv4_lpm set_nhop 10.0.0.10/32 => 10.0.0.10 1 -table_add ipv4_lpm set_nhop 192.168.0.10/32 => 192.168.0.10 2 -table_set_default forward _drop -table_add forward set_dmac 10.0.0.10 => 00:04:00:00:00:10 -table_add forward set_dmac 192.168.0.10 => 00:05:00:00:00:10 -table_set_default send_frame _drop -table_add send_frame do_rewrites 1 => 00:aa:bb:00:00:04 -table_add send_frame do_rewrites 2 => 00:aa:bb:00:00:05 -mirroring_add 250 11 -table_set_default send_to_cpu do_cpu_encap diff --git a/examples/simple_nat/nat_app.py b/examples/simple_nat/nat_app.py deleted file mode 100644 index 882fa929f..000000000 --- a/examples/simple_nat/nat_app.py +++ /dev/null @@ -1,77 +0,0 @@ -# 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. - -from scapy.all import * -import subprocess -import os - -CLI_PATH = None - -EXTERN_IP = "192.168.0.1" - -current_nat_port = 1025 -nat_mappings = {} - -def send_to_CLI(cmd): - this_dir = os.path.dirname(os.path.realpath(__file__)) - p = Popen(os.path.join(this_dir, 'sswitch_CLI.sh'), stdout=PIPE, stdin=PIPE) - output = p.communicate(input=cmd)[0] - # print output - -# This is a very basic implementation of a full-cone NAT for TCP traffic -# We do not maintain a state machine for each connection, so we are not able to -# cleanup the port mappings, but this is sufficient for demonstration purposes -def process_cpu_pkt(p): - global current_nat_port - global EXTERN_IP - - p_str = str(p) - # 0-7 : preamble - # 8 : device - # 9 : reason - # 10 : iface - # 11- : data packet (TCP) - if p_str[:8] != '\x00' * 8 or p_str[8] != '\x00' or p_str[9] != '\xab': - return - ip_hdr = None - tcp_hdr = None - try: - p2 = Ether(p_str[11:]) - ip_hdr = p2['IP'] - tcp_hdr = p2['TCP'] - except: - return - print "Packet received" - print p2.summary() - if (ip_hdr.src, tcp_hdr.sport) not in nat_mappings: - ext_port = current_nat_port - current_nat_port += 1 - print "Allocating external port", ext_port - nat_mappings[(ip_hdr.src, tcp_hdr.sport)] = ext_port - # internal to external rule for this mapping - send_to_CLI("table_add nat nat_hit_int_to_ext 0 1 1 %s&&&255.255.255.255 0.0.0.0&&&0.0.0.0 %d&&&0xffff 0&&&0 => %s %d 1" %\ - (ip_hdr.src, tcp_hdr.sport, EXTERN_IP, ext_port)) - # external to internal rule for this mapping - send_to_CLI("table_add nat nat_hit_ext_to_int 1 1 1 0.0.0.0&&&0.0.0.0 %s&&&255.255.255.255 0&&&0 %d&&&0xffff => %s %d 1" %\ - (EXTERN_IP, ext_port, ip_hdr.src, tcp_hdr.sport)) - # a little bit hacky, this essentially ensures that the packet we re-inject - # in the CPU iface will not be processed again by this method - new_p = p_str[:9] + '\xac' + p_str[10:] - sendp(new_p, iface="cpu-veth-0", verbose=0) - -def main(): - sniff(iface="cpu-veth-0", prn=lambda x: process_cpu_pkt(x)) - -if __name__ == '__main__': - main() diff --git a/examples/simple_nat/p4src/simple_nat.p4 b/examples/simple_nat/p4src/simple_nat.p4 deleted file mode 100644 index 904c134b9..000000000 --- a/examples/simple_nat/p4src/simple_nat.p4 +++ /dev/null @@ -1,364 +0,0 @@ -/* -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_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; - } -} - -parser start { - set_metadata(meta.if_index, standard_metadata.ingress_port); - return select(current(0, 64)) { - 0 : parse_cpu_header; // dummy transition - default: parse_ethernet; - } -} - -header_type cpu_header_t { - fields { - preamble: 64; - device: 8; - reason: 8; - if_index: 8; - } -} - -header cpu_header_t cpu_header; - -parser parse_cpu_header { - extract(cpu_header); - set_metadata(meta.if_index, cpu_header.if_index); - return parse_ethernet; -} - -#define ETHERTYPE_IPV4 0x0800 - -header ethernet_t ethernet; - -parser parse_ethernet { - extract(ethernet); - return select(latest.etherType) { - ETHERTYPE_IPV4 : parse_ipv4; - default: ingress; - } -} - -header ipv4_t ipv4; - -field_list ipv4_checksum_list { - ipv4.version; - ipv4.ihl; - ipv4.diffserv; - ipv4.totalLen; - ipv4.identification; - ipv4.flags; - ipv4.fragOffset; - ipv4.ttl; - ipv4.protocol; - ipv4.srcAddr; - ipv4.dstAddr; -} - -field_list_calculation ipv4_checksum { - input { - ipv4_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field ipv4.hdrChecksum { - verify ipv4_checksum; - update ipv4_checksum; -} - -#define IP_PROT_TCP 0x06 - -parser parse_ipv4 { - extract(ipv4); - set_metadata(meta.ipv4_sa, ipv4.srcAddr); - set_metadata(meta.ipv4_da, ipv4.dstAddr); - set_metadata(meta.tcpLength, ipv4.totalLen - 20); - return select(ipv4.protocol) { - IP_PROT_TCP : parse_tcp; - default : ingress; - } -} - -header_type tcp_t { - fields { - srcPort : 16; - dstPort : 16; - seqNo : 32; - ackNo : 32; - dataOffset : 4; - res : 4; - flags : 8; - window : 16; - checksum : 16; - urgentPtr : 16; - } -} - -header tcp_t tcp; - -parser parse_tcp { - extract(tcp); - set_metadata(meta.tcp_sp, tcp.srcPort); - set_metadata(meta.tcp_dp, tcp.dstPort); - return ingress; -} - -field_list tcp_checksum_list { - ipv4.srcAddr; - ipv4.dstAddr; - 8'0; - ipv4.protocol; - meta.tcpLength; - tcp.srcPort; - tcp.dstPort; - tcp.seqNo; - tcp.ackNo; - tcp.dataOffset; - tcp.res; - tcp.flags; - tcp.window; - tcp.urgentPtr; - payload; -} - -field_list_calculation tcp_checksum { - input { - tcp_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field tcp.checksum { - verify tcp_checksum if(valid(tcp)); - update tcp_checksum if(valid(tcp)); -} - -action _drop() { - drop(); -} - -header_type intrinsic_metadata_t { - fields { - mcast_grp : 4; - egress_rid : 4; - mcast_hash : 16; - lf_field_list: 32; - } -} - -metadata intrinsic_metadata_t intrinsic_metadata; - -header_type meta_t { - fields { - do_forward : 1; - ipv4_sa : 32; - ipv4_da : 32; - tcp_sp : 16; - tcp_dp : 16; - nhop_ipv4 : 32; - if_ipv4_addr : 32; - if_mac_addr : 48; - is_ext_if : 1; - tcpLength : 16; - if_index : 8; - } -} - -metadata meta_t meta; - -action set_if_info(ipv4_addr, mac_addr, is_ext) { - modify_field(meta.if_ipv4_addr, ipv4_addr); - modify_field(meta.if_mac_addr, mac_addr); - modify_field(meta.is_ext_if, is_ext); -} - -table if_info { - reads { - meta.if_index : exact; - } - actions { - _drop; - set_if_info; - } -} - -action nat_miss_ext_to_int() { - modify_field(meta.do_forward, 0); - drop(); -} - -#define CPU_MIRROR_SESSION_ID 250 - -field_list copy_to_cpu_fields { - standard_metadata; -} - -action nat_miss_int_to_ext() { - clone_ingress_pkt_to_egress(CPU_MIRROR_SESSION_ID, copy_to_cpu_fields); -} - -action nat_hit_int_to_ext(srcAddr, srcPort) { - modify_field(meta.do_forward, 1); - modify_field(meta.ipv4_sa, srcAddr); - modify_field(meta.tcp_sp, srcPort); -} - -action nat_hit_ext_to_int(dstAddr, dstPort) { - modify_field(meta.do_forward, 1); - modify_field(meta.ipv4_da, dstAddr); - modify_field(meta.tcp_dp, dstPort); -} - -action nat_no_nat() { - modify_field(meta.do_forward, 1); -} - -table nat { - reads { - meta.is_ext_if : exact; - ipv4 : valid; - tcp : valid; - ipv4.srcAddr : ternary; - ipv4.dstAddr : ternary; - tcp.srcPort : ternary; - tcp.dstPort : ternary; - } - actions { - _drop; - nat_miss_int_to_ext; - nat_miss_ext_to_int; - nat_hit_int_to_ext; - nat_hit_ext_to_int; - nat_no_nat; // for debugging - } - size : 128; -} - -action set_nhop(nhop_ipv4, port) { - modify_field(meta.nhop_ipv4, nhop_ipv4); - modify_field(standard_metadata.egress_spec, port); - add_to_field(ipv4.ttl, -1); -} - -table ipv4_lpm { - reads { - meta.ipv4_da : lpm; - } - actions { - set_nhop; - _drop; - } - size: 1024; -} - -action set_dmac(dmac) { - modify_field(ethernet.dstAddr, dmac); -} - -table forward { - reads { - meta.nhop_ipv4 : exact; - } - actions { - set_dmac; - _drop; - } - size: 512; -} - -action do_rewrites(smac) { - // in case packet was injected by CPU - remove_header(cpu_header); - modify_field(ethernet.srcAddr, smac); - modify_field(ipv4.srcAddr, meta.ipv4_sa); - modify_field(ipv4.dstAddr, meta.ipv4_da); - modify_field(tcp.srcPort, meta.tcp_sp); - modify_field(tcp.dstPort, meta.tcp_dp); -} - -table send_frame { - reads { - standard_metadata.egress_port: exact; - } - actions { - do_rewrites; - _drop; - } - size: 256; -} - -action do_cpu_encap() { - add_header(cpu_header); - modify_field(cpu_header.preamble, 0); - modify_field(cpu_header.device, 0); - modify_field(cpu_header.reason, 0xab); // does not mean anything - modify_field(cpu_header.if_index, meta.if_index); -} - -table send_to_cpu { - actions { do_cpu_encap; } - size : 0; -} - -control ingress { - // retrieve information on the ingress interface - apply(if_info); - // determine what to do with the packet and which rewrites to apply - // depending on direction (int -> ext or ext -> int) and existing nat rules - apply(nat); - // forward packet - if (meta.do_forward == 1 and ipv4.ttl > 0) { - apply(ipv4_lpm); - apply(forward); - } -} - -control egress { - if (standard_metadata.instance_type == 0) { - // regular packet: execute rewrites - apply(send_frame); - } else { - // cpu packet: encap - apply(send_to_cpu); - } -} diff --git a/examples/simple_nat/resources/topo.graphml b/examples/simple_nat/resources/topo.graphml deleted file mode 100644 index e937c3e18..000000000 --- a/examples/simple_nat/resources/topo.graphml +++ /dev/null @@ -1,306 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - s1 - - - - - - - - - - - - - - - - - - h2 - - - - - - - - - - - - - - - - - - h1 - - - - - - - - - - - - - - - - - - nat_app.py - - - - - - - - - - - - - - - - - - cpu-veth-0 - - - - - - - - - - - - - - - - - - cpu-veth-1, port 11 - - - - - - - - - - - - - - - - - - port 1 -00:aa:bb:00:00:04 -10.0.0.1 - - - - - - - - - - - - - - - - - - port 2 -00:aa:bb:00:00:05 -192.168.0.1 - - - - - - - - - - - - - - - - - - 00:04:00:00:00:10 -10.0.0.10 - - - - - - - - - - - - - - - - - - 00:05:00:00:00:10 -192.168.0.10 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Internal - - - - - - - - - - - - - - - - - - External - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/simple_nat/resources/topo.png b/examples/simple_nat/resources/topo.png deleted file mode 100644 index 443b374a1..000000000 Binary files a/examples/simple_nat/resources/topo.png and /dev/null differ diff --git a/examples/simple_nat/run_demo.sh b/examples/simple_nat/run_demo.sh deleted file mode 100755 index 91d8c9eda..000000000 --- a/examples/simple_nat/run_demo.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/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. - -if [[ $EUID -ne 0 ]]; then - echo "This script should be run using sudo or as the root user" - exit 1 -fi - -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 - -# create CPU port -intf0="cpu-veth-0" -intf1="cpu-veth-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 - -$P4C_BM_SCRIPT p4src/simple_nat.p4 --json simple_nat.json -# This gives libtool the opportunity to "warm-up" -$SWITCH_PATH >/dev/null 2>&1 -PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \ - --behavioral-exe $SWITCH_PATH \ - --json simple_nat.json \ - --cli $CLI_PATH \ - --thrift-port 22222 diff --git a/examples/simple_nat/sswitch_CLI.sh b/examples/simple_nat/sswitch_CLI.sh deleted file mode 100755 index 491642cfb..000000000 --- a/examples/simple_nat/sswitch_CLI.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) - -source $THIS_DIR/../../env.sh - -CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI - -$CLI_PATH simple_nat.json 22222 diff --git a/examples/simple_nat/topo.py b/examples/simple_nat/topo.py deleted file mode 100644 index 658606895..000000000 --- a/examples/simple_nat/topo.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python - -# 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. - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.log import setLogLevel, info -from mininet.cli import CLI -from mininet.link import TCLink, Intf - -from p4_mininet import P4Switch, P4Host - -import argparse -from time import sleep -import os -import subprocess - -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--cli', help='Path to BM CLI', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", required=True) - -args = parser.parse_args() - -class MyTopo(Topo): - def __init__(self, sw_path, json_path, thrift_port, **opts): - # Initialize topology and default options - Topo.__init__(self, **opts) - - switch = self.addSwitch('s1', - sw_path = sw_path, - json_path = json_path, - thrift_port = thrift_port, - pcap_dump = True) - - # internal host - h1 = self.addHost('h1', - ip = "10.0.0.10", - mac = "00:04:00:00:00:10") - self.addLink(h1, switch) - - # external host - h2 = self.addHost('h2', - ip = "192.168.0.10", - mac = "00:05:00:00:00:10") - self.addLink(h2, switch) - -def main(): - topo = MyTopo(args.behavioral_exe, - args.json, - args.thrift_port) - - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None) - - cpu_intf = Intf("cpu-veth-1", net.get('s1'), 11) - - net.start() - - sw_macs = ["00:aa:bb:00:00:04", "00:aa:bb:00:00:05"] - - sw_addrs = ["10.0.0.1", "192.168.0.1"] - - for n in xrange(2): - h = net.get('h%d' % (n + 1)) - h.setARP(sw_addrs[n], sw_macs[n]) - h.setDefaultRoute("dev eth0 via %s" % sw_addrs[n]) - - for n in xrange(2): - h = net.get('h%d' % (n + 1)) - h.describe() - - sleep(1) - - cmd = [args.cli, args.json, str(args.thrift_port)] - with open("commands.txt", "r") as f: - print " ".join(cmd) - try: - output = subprocess.check_output(cmd, stdin = f) - print output - except subprocess.CalledProcessError as e: - print e - print e.output - - sleep(1) - - print "Ready !" - - CLI( net ) - net.stop() - -if __name__ == '__main__': - setLogLevel( 'info' ) - main() diff --git a/examples/veth_setup.sh b/examples/veth_setup.sh deleted file mode 100755 index 40835412c..000000000 --- a/examples/veth_setup.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/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 diff --git a/P4D2_2017_Fall/exercises/p4runtime/Makefile b/exercises/basic/Makefile similarity index 100% rename from P4D2_2017_Fall/exercises/p4runtime/Makefile rename to exercises/basic/Makefile diff --git a/P4D2_2018_East/exercises/basic/README.md b/exercises/basic/README.md similarity index 89% rename from P4D2_2018_East/exercises/basic/README.md rename to exercises/basic/README.md index 70a7e95a2..77fefc933 100644 --- a/P4D2_2018_East/exercises/basic/README.md +++ b/exercises/basic/README.md @@ -80,14 +80,15 @@ the control plane as part of the rule. In this exercise, we have already implemented the the control plane logic for you. As part of bringing up the Mininet instance, the `make run` command will install packet-processing rules in the tables of -each switch. These are defined in the `sX-commands.txt` files, where +each switch. These are defined in the `sX-runtime.json` files, where `X` corresponds to the switch number. -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. The commands in the files -`sX-commands.txt` refer to specific tables, keys, and actions by name, -and any changes in the P4 program that add or rename tables, keys, or -actions will need to be reflected in these command files. +**Important:** We use P4Runtime to install the control plane rules. The +content of files `sX-runtime.json` refer to specific names of tables, keys, and +actions, as defined in the P4Info file produced by the compiler (look for the +file `build/basic.p4info` after executing `make run`). Any changes in the P4 +program that add or rename tables, keys, or actions will need to be reflected in +these `sX-runtime.json` files. ## Step 2: Implement L3 forwarding @@ -144,9 +145,9 @@ There are several problems that might manifest as you develop your program: report the error emitted from the compiler and halt. 2. `basic.p4` might compile but fail to support the control plane -rules in the `s1-commands.txt` through `s3-command.txt` files that -`make run` tries to install using the Bmv2 CLI. In this case, `make run` -will log the CLI tool output in the `logs` directory. Use these error +rules in the `s1-runtime.json` through `s3-runtime.json` files that +`make run` tries to install using P4Runtime. In this case, `make run` will +report errors if control plane rules cannot be installed. Use these error messages to fix your `basic.p4` implementation. 3. `basic.p4` might compile, and the control plane rules might be diff --git a/P4D2_2017_Fall/exercises/basic/basic.p4 b/exercises/basic/basic.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/basic/basic.p4 rename to exercises/basic/basic.p4 diff --git a/P4D2_2017_Fall/exercises/basic/receive.py b/exercises/basic/receive.py similarity index 100% rename from P4D2_2017_Fall/exercises/basic/receive.py rename to exercises/basic/receive.py diff --git a/exercises/basic/s1-runtime.json b/exercises/basic/s1-runtime.json new file mode 100644 index 000000000..c773b6328 --- /dev/null +++ b/exercises/basic/s1-runtime.json @@ -0,0 +1,46 @@ +{ + "target": "bmv2", + "p4info": "build/basic.p4info", + "bmv2_json": "build/basic.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "default_action": true, + "action_name": "MyIngress.drop", + "action_params": { } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.1", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:01:01", + "port": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:02:02:00", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:03:03:00", + "port": 3 + } + } + ] +} diff --git a/exercises/basic/s2-runtime.json b/exercises/basic/s2-runtime.json new file mode 100644 index 000000000..554992ee0 --- /dev/null +++ b/exercises/basic/s2-runtime.json @@ -0,0 +1,46 @@ +{ + "target": "bmv2", + "p4info": "build/basic.p4info", + "bmv2_json": "build/basic.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "default_action": true, + "action_name": "MyIngress.drop", + "action_params": { } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.1", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:01:02:00", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:02:02", + "port": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:03:03:00", + "port": 3 + } + } + ] +} diff --git a/exercises/basic/s3-runtime.json b/exercises/basic/s3-runtime.json new file mode 100644 index 000000000..52ea4704d --- /dev/null +++ b/exercises/basic/s3-runtime.json @@ -0,0 +1,46 @@ +{ + "target": "bmv2", + "p4info": "build/basic.p4info", + "bmv2_json": "build/basic.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "default_action": true, + "action_name": "MyIngress.drop", + "action_params": { } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.1", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:01:03:00", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:02:03:00", + "port": 3 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:03:03", + "port": 1 + } + } + ] +} diff --git a/P4D2_2017_Fall/exercises/basic/send.py b/exercises/basic/send.py similarity index 100% rename from P4D2_2017_Fall/exercises/basic/send.py rename to exercises/basic/send.py diff --git a/P4D2_2017_Fall/exercises/basic/solution/basic.p4 b/exercises/basic/solution/basic.p4 similarity index 99% rename from P4D2_2017_Fall/exercises/basic/solution/basic.p4 rename to exercises/basic/solution/basic.p4 index 7d491ea43..02a5cab8b 100644 --- a/P4D2_2017_Fall/exercises/basic/solution/basic.p4 +++ b/exercises/basic/solution/basic.p4 @@ -107,7 +107,7 @@ control MyIngress(inout headers hdr, NoAction; } size = 1024; - default_action = NoAction(); + default_action = drop(); } apply { diff --git a/P4D2_2017_Fall/exercises/basic/topology.json b/exercises/basic/topology.json similarity index 56% rename from P4D2_2017_Fall/exercises/basic/topology.json rename to exercises/basic/topology.json index e33477cc9..e43e3c0c4 100644 --- a/P4D2_2017_Fall/exercises/basic/topology.json +++ b/exercises/basic/topology.json @@ -5,9 +5,9 @@ "h3" ], "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } + "s1": { "runtime_json" : "s1-runtime.json" }, + "s2": { "runtime_json" : "s2-runtime.json" }, + "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], diff --git a/P4D2_2018_East/exercises/p4runtime/Makefile b/exercises/basic_tunnel/Makefile similarity index 100% rename from P4D2_2018_East/exercises/p4runtime/Makefile rename to exercises/basic_tunnel/Makefile diff --git a/P4D2_2018_East/exercises/basic_tunnel/README.md b/exercises/basic_tunnel/README.md similarity index 88% rename from P4D2_2018_East/exercises/basic_tunnel/README.md rename to exercises/basic_tunnel/README.md index d3c21ed3a..da356027b 100644 --- a/P4D2_2018_East/exercises/basic_tunnel/README.md +++ b/exercises/basic_tunnel/README.md @@ -69,14 +69,15 @@ the control plane as part of the rule. For this exercise, we have already added the necessary static control plane entries. As part of bringing up the Mininet instance, the `make run` command will install packet-processing rules in the tables -of each switch. These are defined in the `sX-commands.txt` files, +of each switch. These are defined in the `sX-runtime.json` files, where `X` corresponds to the switch number. -**Important:** A P4 program also defines the interface between the -switch pipeline and control plane. The commands in the files -`sX-commands.txt` refer to specific tables, keys, and actions by name, -and any changes in the P4 program that add or rename tables, keys, or -actions will need to be reflected in these command files. +**Important:** We use P4Runtime to install the control plane rules. The +content of files `sX-runtime.json` refer to specific names of tables, keys, and +actions, as defined in the P4Info file produced by the compiler (look for the +file `build/basic.p4info` after executing `make run`). Any changes in the P4 +program that add or rename tables, keys, or actions will need to be reflected in +these `sX-runtime.json` files. ## Step 2: Implement Basic Tunneling @@ -96,7 +97,7 @@ Your job will be to do the following: 4. **TODO:** Define a new table called `myTunnel_exact` that perfoms an exact match on the `dst_id` field of the `myTunnel` header. This table should invoke either the `myTunnel_forward` action if the there is a match in the table and it should invoke the `drop` action otherwise. 5. **TODO:** Update the `apply` statement in the `MyIngress` control block to apply your newly defined `myTunnel_exact` table if the `myTunnel` header is valid. Otherwise, invoke the `ipv4_lpm` table if the `ipv4` header is valid. 6. **TODO:** Update the deparser to emit the `ethernet`, then `myTunnel`, then `ipv4` headers. Remember that the deparser will only emit a header if it is valid. A header's implicit validity bit is set by the parser upon extraction. So there is no need to check header validity here. -7. **TODO:** Add static rules for your newly defined table so that the switches will forward correctly for each possible value of `dst_id`. See the diagram below for the topology's port configuration as well as how we will assign IDs to hosts. For this step you will need to add your forwarding rules to the `sX-commands.txt` files. +7. **TODO:** Add static rules for your newly defined table so that the switches will forward correctly for each possible value of `dst_id`. See the diagram below for the topology's port configuration as well as how we will assign IDs to hosts. For this step you will need to add your forwarding rules to the `sX-runtime.json` files. ![topology](./topo.png) @@ -127,7 +128,7 @@ header to an IP packet upon ingress to the network and then remove the Hints: - - The ingress switch will need to map the destination IP address to the corresponding `dst_id` for the `myTunnel` header. Also remember to set explicitly set the validity bit for the `myTunnel` header so that it can be emitted by the deparser. + - The ingress switch will need to map the destination IP address to the corresponding `dst_id` for the `myTunnel` header. Also, remember to set the validity bit for the `myTunnel` header so that it can be emitted by the deparser. - The egress switch will need to remove the `myTunnel` header from the packet after looking up the appropriate output port using the `dst_id` field. ### Troubleshooting @@ -138,10 +139,10 @@ There are several problems that might manifest as you develop your program: report the error emitted from the compiler and halt. 2. `basic_tunnel.p4` might compile but fail to support the control plane -rules in the `s1-commands.txt` through `s3-command.txt` files that -`make run` tries to install using the Bmv2 CLI. In this case, `make run` -will log the CLI tool output in the `logs` directory. Use these error -messages to fix your `basic_tunnel.p4` implementation or forwarding rules. +rules in the `sX-runtime.json` files that `make run` tries to install using +the P4Runtime. In this case, `make run` will report errors if control plane +rules cannot be installed. Use these error messages to fix your `basic_tunnel.p4` +implementation or forwarding rules. 3. `basic_tunnel.p4` might compile, and the control plane rules might be installed, but the switch might not process packets in the desired diff --git a/P4D2_2018_East/exercises/basic_tunnel/basic_tunnel.p4 b/exercises/basic_tunnel/basic_tunnel.p4 similarity index 99% rename from P4D2_2018_East/exercises/basic_tunnel/basic_tunnel.p4 rename to exercises/basic_tunnel/basic_tunnel.p4 index 156c07778..bc31b4c08 100644 --- a/P4D2_2018_East/exercises/basic_tunnel/basic_tunnel.p4 +++ b/exercises/basic_tunnel/basic_tunnel.p4 @@ -119,7 +119,7 @@ control MyIngress(inout headers hdr, NoAction; } size = 1024; - default_action = NoAction(); + default_action = drop(); } // TODO: declare a new action: myTunnel_forward(egressSpec_t port) diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/myTunnel_header.py b/exercises/basic_tunnel/myTunnel_header.py similarity index 100% rename from P4D2_2017_Fall/exercises/basic_tunnel/myTunnel_header.py rename to exercises/basic_tunnel/myTunnel_header.py diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/receive.py b/exercises/basic_tunnel/receive.py similarity index 100% rename from P4D2_2017_Fall/exercises/basic_tunnel/receive.py rename to exercises/basic_tunnel/receive.py diff --git a/exercises/basic_tunnel/s1-runtime.json b/exercises/basic_tunnel/s1-runtime.json new file mode 100644 index 000000000..2d3feaf2c --- /dev/null +++ b/exercises/basic_tunnel/s1-runtime.json @@ -0,0 +1,71 @@ +{ + "target": "bmv2", + "p4info": "build/basic_tunnel.p4info", + "bmv2_json": "build/basic_tunnel.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.1", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:01:01", + "port": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:02:02:00", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:03:03:00", + "port": 3 + } + }, + + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [1] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 1 + } + }, + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [2] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 2 + } + }, + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [3] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 3 + } + } + ] +} diff --git a/exercises/basic_tunnel/s2-runtime.json b/exercises/basic_tunnel/s2-runtime.json new file mode 100644 index 000000000..62a7c521f --- /dev/null +++ b/exercises/basic_tunnel/s2-runtime.json @@ -0,0 +1,71 @@ +{ + "target": "bmv2", + "p4info": "build/basic_tunnel.p4info", + "bmv2_json": "build/basic_tunnel.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.1", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:01:02:00", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:02:02", + "port": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:03:02:00", + "port": 3 + } + }, + + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [1] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 2 + } + }, + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [2] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 1 + } + }, + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [3] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 3 + } + } + ] +} diff --git a/exercises/basic_tunnel/s3-runtime.json b/exercises/basic_tunnel/s3-runtime.json new file mode 100644 index 000000000..f5e1b8c29 --- /dev/null +++ b/exercises/basic_tunnel/s3-runtime.json @@ -0,0 +1,71 @@ +{ + "target": "bmv2", + "p4info": "build/basic_tunnel.p4info", + "bmv2_json": "build/basic_tunnel.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.1", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:01:03:00", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:02:03:00", + "port": 3 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:03:03", + "port": 1 + } + }, + + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [1] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 2 + } + }, + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [2] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 3 + } + }, + { + "table": "MyIngress.myTunnel_exact", + "match": { + "hdr.myTunnel.dst_id": [3] + }, + "action_name": "MyIngress.myTunnel_forward", + "action_params": { + "port": 1 + } + } + ] +} diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/send.py b/exercises/basic_tunnel/send.py similarity index 100% rename from P4D2_2017_Fall/exercises/basic_tunnel/send.py rename to exercises/basic_tunnel/send.py diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/solution/basic_tunnel.p4 b/exercises/basic_tunnel/solution/basic_tunnel.p4 similarity index 99% rename from P4D2_2017_Fall/exercises/basic_tunnel/solution/basic_tunnel.p4 rename to exercises/basic_tunnel/solution/basic_tunnel.p4 index 83b29dbc5..de1afd204 100644 --- a/P4D2_2017_Fall/exercises/basic_tunnel/solution/basic_tunnel.p4 +++ b/exercises/basic_tunnel/solution/basic_tunnel.p4 @@ -123,7 +123,7 @@ control MyIngress(inout headers hdr, NoAction; } size = 1024; - default_action = NoAction(); + default_action = drop(); } action myTunnel_forward(egressSpec_t port) { diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/topo.pdf b/exercises/basic_tunnel/topo.pdf similarity index 100% rename from P4D2_2017_Fall/exercises/basic_tunnel/topo.pdf rename to exercises/basic_tunnel/topo.pdf diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/topo.png b/exercises/basic_tunnel/topo.png similarity index 100% rename from P4D2_2017_Fall/exercises/basic_tunnel/topo.png rename to exercises/basic_tunnel/topo.png diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/topology.json b/exercises/basic_tunnel/topology.json similarity index 56% rename from P4D2_2017_Fall/exercises/basic_tunnel/topology.json rename to exercises/basic_tunnel/topology.json index e33477cc9..e43e3c0c4 100644 --- a/P4D2_2017_Fall/exercises/basic_tunnel/topology.json +++ b/exercises/basic_tunnel/topology.json @@ -5,9 +5,9 @@ "h3" ], "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } + "s1": { "runtime_json" : "s1-runtime.json" }, + "s2": { "runtime_json" : "s2-runtime.json" }, + "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], diff --git a/exercises/calc/Makefile b/exercises/calc/Makefile new file mode 100644 index 000000000..463ae0757 --- /dev/null +++ b/exercises/calc/Makefile @@ -0,0 +1,5 @@ +BMV2_SWITCH_EXE = simple_switch_grpc +NO_P4 = true +P4C_ARGS = --p4runtime-file $(basename $@).p4info --p4runtime-format text + +include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/calc/README.md b/exercises/calc/README.md similarity index 100% rename from P4D2_2017_Fall/exercises/calc/README.md rename to exercises/calc/README.md diff --git a/P4D2_2017_Fall/exercises/calc/calc.p4 b/exercises/calc/calc.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/calc/calc.p4 rename to exercises/calc/calc.p4 diff --git a/P4D2_2017_Fall/exercises/calc/calc.py b/exercises/calc/calc.py similarity index 100% rename from P4D2_2017_Fall/exercises/calc/calc.py rename to exercises/calc/calc.py diff --git a/exercises/calc/s1-runtime.json b/exercises/calc/s1-runtime.json new file mode 100644 index 000000000..3bd42ce90 --- /dev/null +++ b/exercises/calc/s1-runtime.json @@ -0,0 +1,6 @@ +{ + "target": "bmv2", + "p4info": "build/calc.p4info", + "bmv2_json": "build/calc.json", + "table_entries": [ ] +} diff --git a/P4D2_2017_Fall/exercises/calc/solution/calc.p4 b/exercises/calc/solution/calc.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/calc/solution/calc.p4 rename to exercises/calc/solution/calc.p4 diff --git a/P4D2_2018_East/exercises/calc/topology.json b/exercises/calc/topology.json similarity index 71% rename from P4D2_2018_East/exercises/calc/topology.json rename to exercises/calc/topology.json index 29d7cdf88..4e0d4a395 100644 --- a/P4D2_2018_East/exercises/calc/topology.json +++ b/exercises/calc/topology.json @@ -4,7 +4,7 @@ "h2" ], "switches": { - "s1": { "cli_input" : "s1-commands.txt" } + "s1": { "runtime_json" : "s1-runtime.json" } }, "links": [ ["h1", "s1"], ["h2", "s1"] diff --git a/exercises/ecn/Makefile b/exercises/ecn/Makefile new file mode 100644 index 000000000..463ae0757 --- /dev/null +++ b/exercises/ecn/Makefile @@ -0,0 +1,5 @@ +BMV2_SWITCH_EXE = simple_switch_grpc +NO_P4 = true +P4C_ARGS = --p4runtime-file $(basename $@).p4info --p4runtime-format text + +include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/ecn/README.md b/exercises/ecn/README.md similarity index 98% rename from P4D2_2017_Fall/exercises/ecn/README.md rename to exercises/ecn/README.md index eb30bb6b4..759fb5374 100644 --- a/P4D2_2017_Fall/exercises/ecn/README.md +++ b/exercises/ecn/README.md @@ -40,7 +40,7 @@ network in Mininet to test its behavior. * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc (`10.0..`). * The control plane programs the P4 tables in each switch based on - `sx-commands.txt` + `sx-runtime.json` 2. We want to send a low rate traffic from `h1` to `h2` and a high rate iperf traffic from `h11` to `h22`. The link between `s1` and @@ -164,7 +164,7 @@ There are several ways that problems might manifest: 1. `ecn.p4` fails to compile. In this case, `make` will report the error emitted from the compiler and stop. 2. `ecn.p4` compiles but does not support the control plane rules in - the `sX-commands.txt` files that `make` tries to install using + the `sX-runtime.json` files that `make` tries to install using the BMv2 CLI. In this case, `make` will log the CLI tool output in the `logs` directory. Use these error messages to fix your `ecn.p4` implementation. diff --git a/SIGCOMM_2017/exercises/ecn/ecn.p4 b/exercises/ecn/ecn.p4 similarity index 98% rename from SIGCOMM_2017/exercises/ecn/ecn.p4 rename to exercises/ecn/ecn.p4 index 576169f30..112c652b2 100644 --- a/SIGCOMM_2017/exercises/ecn/ecn.p4 +++ b/exercises/ecn/ecn.p4 @@ -108,10 +108,9 @@ control MyIngress(inout headers hdr, actions = { ipv4_forward; drop; - NoAction; } size = 1024; - default_action = NoAction(); + default_action = drop; } apply { diff --git a/P4D2_2017_Fall/exercises/ecn/receive.py b/exercises/ecn/receive.py similarity index 100% rename from P4D2_2017_Fall/exercises/ecn/receive.py rename to exercises/ecn/receive.py diff --git a/exercises/ecn/s1-runtime.json b/exercises/ecn/s1-runtime.json new file mode 100644 index 000000000..d0774dc0d --- /dev/null +++ b/exercises/ecn/s1-runtime.json @@ -0,0 +1,52 @@ +{ + "target": "bmv2", + "p4info": "build/ecn.p4info", + "bmv2_json": "build/ecn.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.1", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:01:01", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.11", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:01:0b", + "port": 1 + } + }, + + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:02:03:00", + "port": 3 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:03:02:00", + "port": 4 + } + } + ] +} diff --git a/exercises/ecn/s2-runtime.json b/exercises/ecn/s2-runtime.json new file mode 100644 index 000000000..e87b72687 --- /dev/null +++ b/exercises/ecn/s2-runtime.json @@ -0,0 +1,51 @@ +{ + "target": "bmv2", + "p4info": "build/ecn.p4info", + "bmv2_json": "build/ecn.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:02:02", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.22", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:02:16", + "port": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:01:03:00", + "port": 3 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:03:03:00", + "port": 4 + } + } + ] +} diff --git a/exercises/ecn/s3-runtime.json b/exercises/ecn/s3-runtime.json new file mode 100644 index 000000000..f0d3d5ca2 --- /dev/null +++ b/exercises/ecn/s3-runtime.json @@ -0,0 +1,40 @@ +{ + "target": "bmv2", + "p4info": "build/ecn.p4info", + "bmv2_json": "build/ecn.json", + "table_entries": [ + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:03:03", + "port": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:01:04:00", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:02:04:00", + "port": 3 + } + } + ] +} diff --git a/P4D2_2017_Fall/exercises/ecn/send.py b/exercises/ecn/send.py similarity index 100% rename from P4D2_2017_Fall/exercises/ecn/send.py rename to exercises/ecn/send.py diff --git a/P4D2_2017_Fall/exercises/ecn/setup.png b/exercises/ecn/setup.png similarity index 100% rename from P4D2_2017_Fall/exercises/ecn/setup.png rename to exercises/ecn/setup.png diff --git a/P4D2_2017_Fall/exercises/ecn/solution/ecn.p4 b/exercises/ecn/solution/ecn.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/ecn/solution/ecn.p4 rename to exercises/ecn/solution/ecn.p4 diff --git a/P4D2_2017_Fall/exercises/mri/topology.json b/exercises/ecn/topology.json similarity index 64% rename from P4D2_2017_Fall/exercises/mri/topology.json rename to exercises/ecn/topology.json index 2640a2b96..29df53e35 100644 --- a/P4D2_2017_Fall/exercises/mri/topology.json +++ b/exercises/ecn/topology.json @@ -7,9 +7,9 @@ "h22" ], "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } + "s1": { "runtime_json" : "s1-runtime.json" }, + "s2": { "runtime_json" : "s2-runtime.json" }, + "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["h11", "s1"], ["s1", "s2", "0", 0.5], ["s1", "s3"], diff --git a/exercises/load_balance/Makefile b/exercises/load_balance/Makefile new file mode 100644 index 000000000..463ae0757 --- /dev/null +++ b/exercises/load_balance/Makefile @@ -0,0 +1,5 @@ +BMV2_SWITCH_EXE = simple_switch_grpc +NO_P4 = true +P4C_ARGS = --p4runtime-file $(basename $@).p4info --p4runtime-format text + +include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/load_balance/README.md b/exercises/load_balance/README.md similarity index 100% rename from P4D2_2017_Fall/exercises/load_balance/README.md rename to exercises/load_balance/README.md diff --git a/P4D2_2017_Fall/exercises/load_balance/load_balance.p4 b/exercises/load_balance/load_balance.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/load_balance/load_balance.p4 rename to exercises/load_balance/load_balance.p4 diff --git a/P4D2_2017_Fall/exercises/load_balance/receive.py b/exercises/load_balance/receive.py similarity index 100% rename from P4D2_2017_Fall/exercises/load_balance/receive.py rename to exercises/load_balance/receive.py diff --git a/exercises/load_balance/s1-runtime.json b/exercises/load_balance/s1-runtime.json new file mode 100644 index 000000000..b5aebd741 --- /dev/null +++ b/exercises/load_balance/s1-runtime.json @@ -0,0 +1,68 @@ +{ + "target": "bmv2", + "p4info": "build/load_balance.p4info", + "bmv2_json": "build/load_balance.json", + "table_entries": [ + { + "table": "MyIngress.ecmp_group", + "default_action": true, + "action_name": "MyIngress.drop", + "action_params": { } + }, + { + "table": "MyIngress.ecmp_group", + "match": { + "hdr.ipv4.dstAddr": ["10.0.0.1", 32] + }, + "action_name": "MyIngress.set_ecmp_select", + "action_params": { + "ecmp_base": 0, + "ecmp_count": 2 + } + }, + { + "table": "MyIngress.ecmp_nhop", + "match": { + "meta.ecmp_select": 0 + }, + "action_name": "MyIngress.set_nhop", + "action_params": { + "nhop_dmac": "00:00:00:00:01:02", + "nhop_ipv4": "10.0.2.2", + "port" : 2 + } + }, + { + "table": "MyIngress.ecmp_nhop", + "match": { + "meta.ecmp_select": 1 + }, + "action_name": "MyIngress.set_nhop", + "action_params": { + "nhop_dmac": "00:00:00:00:01:03", + "nhop_ipv4": "10.0.3.3", + "port" : 3 + } + }, + { + "table": "MyEgress.send_frame", + "match": { + "standard_metadata.egress_port": 2 + }, + "action_name": "MyEgress.rewrite_mac", + "action_params": { + "smac": "00:00:00:01:02:00" + } + }, + { + "table": "MyEgress.send_frame", + "match": { + "standard_metadata.egress_port": 3 + }, + "action_name": "MyEgress.rewrite_mac", + "action_params": { + "smac": "00:00:00:01:03:00" + } + } + ] +} diff --git a/exercises/load_balance/s2-runtime.json b/exercises/load_balance/s2-runtime.json new file mode 100644 index 000000000..c53ff3989 --- /dev/null +++ b/exercises/load_balance/s2-runtime.json @@ -0,0 +1,46 @@ +{ + "target": "bmv2", + "p4info": "build/load_balance.p4info", + "bmv2_json": "build/load_balance.json", + "table_entries": [ + { + "table": "MyIngress.ecmp_group", + "default_action": true, + "action_name": "MyIngress.drop", + "action_params": { } + }, + { + "table": "MyIngress.ecmp_group", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.set_ecmp_select", + "action_params": { + "ecmp_base": 0, + "ecmp_count": 1 + } + }, + { + "table": "MyIngress.ecmp_nhop", + "match": { + "meta.ecmp_select": 0 + }, + "action_name": "MyIngress.set_nhop", + "action_params": { + "nhop_dmac": "00:00:00:00:02:02", + "nhop_ipv4": "10.0.2.2", + "port" : 1 + } + }, + { + "table": "MyEgress.send_frame", + "match": { + "standard_metadata.egress_port": 1 + }, + "action_name": "MyEgress.rewrite_mac", + "action_params": { + "smac": "00:00:00:02:01:00" + } + } + ] +} diff --git a/exercises/load_balance/s3-runtime.json b/exercises/load_balance/s3-runtime.json new file mode 100644 index 000000000..9292efd95 --- /dev/null +++ b/exercises/load_balance/s3-runtime.json @@ -0,0 +1,46 @@ +{ + "target": "bmv2", + "p4info": "build/load_balance.p4info", + "bmv2_json": "build/load_balance.json", + "table_entries": [ + { + "table": "MyIngress.ecmp_group", + "default_action": true, + "action_name": "MyIngress.drop", + "action_params": { } + }, + { + "table": "MyIngress.ecmp_group", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.set_ecmp_select", + "action_params": { + "ecmp_base": 0, + "ecmp_count": 1 + } + }, + { + "table": "MyIngress.ecmp_nhop", + "match": { + "meta.ecmp_select": 0 + }, + "action_name": "MyIngress.set_nhop", + "action_params": { + "nhop_dmac": "00:00:00:00:03:03", + "nhop_ipv4": "10.0.3.3", + "port" : 1 + } + }, + { + "table": "MyEgress.send_frame", + "match": { + "standard_metadata.egress_port": 1 + }, + "action_name": "MyEgress.rewrite_mac", + "action_params": { + "smac": "00:00:00:03:01:00" + } + } + ] +} diff --git a/P4D2_2017_Fall/exercises/load_balance/send.py b/exercises/load_balance/send.py similarity index 100% rename from P4D2_2017_Fall/exercises/load_balance/send.py rename to exercises/load_balance/send.py diff --git a/P4D2_2017_Fall/exercises/load_balance/solution/load_balance.p4 b/exercises/load_balance/solution/load_balance.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/load_balance/solution/load_balance.p4 rename to exercises/load_balance/solution/load_balance.p4 diff --git a/P4D2_2017_Fall/exercises/load_balance/topology.json b/exercises/load_balance/topology.json similarity index 56% rename from P4D2_2017_Fall/exercises/load_balance/topology.json rename to exercises/load_balance/topology.json index e33477cc9..e43e3c0c4 100644 --- a/P4D2_2017_Fall/exercises/load_balance/topology.json +++ b/exercises/load_balance/topology.json @@ -5,9 +5,9 @@ "h3" ], "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } + "s1": { "runtime_json" : "s1-runtime.json" }, + "s2": { "runtime_json" : "s2-runtime.json" }, + "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], diff --git a/exercises/mri/Makefile b/exercises/mri/Makefile new file mode 100644 index 000000000..463ae0757 --- /dev/null +++ b/exercises/mri/Makefile @@ -0,0 +1,5 @@ +BMV2_SWITCH_EXE = simple_switch_grpc +NO_P4 = true +P4C_ARGS = --p4runtime-file $(basename $@).p4info --p4runtime-format text + +include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/mri/README.md b/exercises/mri/README.md similarity index 98% rename from P4D2_2017_Fall/exercises/mri/README.md rename to exercises/mri/README.md index c38911043..ca670d256 100644 --- a/P4D2_2017_Fall/exercises/mri/README.md +++ b/exercises/mri/README.md @@ -41,7 +41,7 @@ switch in Mininet to test its behavior. * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc (`10.0..`). * The control plane programs the P4 tables in each switch based on - `sx-commands.txt` + `sx-runtime.json` 2. We want to send a low rate traffic from `h1` to `h2` and a high rate iperf traffic from `h11` to `h22`. The link between `s1` and @@ -93,7 +93,7 @@ with parameters supplied by the control plane as part of the rule. In this exercise, the control plane logic has already been implemented. As part of bringing up the Mininet instance, the `make` script will install packet-processing rules in the tables of -each switch. These are defined in the `sX-commands.txt` files, where +each switch. These are defined in the `sX-runtime.json` files, where `X` corresponds to the switch number. ## Step 2: Implement MRI @@ -212,7 +212,7 @@ There are several ways that problems might manifest: 1. `mri.p4` fails to compile. In this case, `make` will report the error emitted from the compiler and stop. 2. `mri.p4` compiles but does not support the control plane rules in -the `sX-commands.txt` files that `make` tries to install using the BMv2 CLI. +the `sX-runtime.json` files that `make` tries to install using the BMv2 CLI. In this case, `make` will log the CLI tool output in the `logs` directory. Use these error messages to fix your `mri.p4` implementation. 3. `mri.p4` compiles, and the control plane rules are installed, but diff --git a/P4D2_2018_East/exercises/mri/mri.p4 b/exercises/mri/mri.p4 similarity index 99% rename from P4D2_2018_East/exercises/mri/mri.p4 rename to exercises/mri/mri.p4 index 15f5c3bc1..bdc238de3 100644 --- a/P4D2_2018_East/exercises/mri/mri.p4 +++ b/exercises/mri/mri.p4 @@ -213,7 +213,7 @@ control MyEgress(inout headers hdr, table swtrace { actions = { - /* TODO: add the correct action */ + add_swtrace; NoAction; } diff --git a/P4D2_2017_Fall/exercises/mri/receive.py b/exercises/mri/receive.py similarity index 100% rename from P4D2_2017_Fall/exercises/mri/receive.py rename to exercises/mri/receive.py diff --git a/exercises/mri/s1-runtime.json b/exercises/mri/s1-runtime.json new file mode 100644 index 000000000..2105f5241 --- /dev/null +++ b/exercises/mri/s1-runtime.json @@ -0,0 +1,60 @@ +{ + "target": "bmv2", + "p4info": "build/mri.p4info", + "bmv2_json": "build/mri.json", + "table_entries": [ + { + "table": "MyEgress.swtrace", + "default_action": true, + "action_name": "MyEgress.add_swtrace", + "action_params": { + "swid": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.1", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:01:01", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.11", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:01:0b", + "port": 1 + } + }, + + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:02:03:00", + "port": 3 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:03:02:00", + "port": 4 + } + } + ] +} diff --git a/exercises/mri/s2-runtime.json b/exercises/mri/s2-runtime.json new file mode 100644 index 000000000..9e9b94541 --- /dev/null +++ b/exercises/mri/s2-runtime.json @@ -0,0 +1,59 @@ +{ + "target": "bmv2", + "p4info": "build/mri.p4info", + "bmv2_json": "build/mri.json", + "table_entries": [ + { + "table": "MyEgress.swtrace", + "default_action": true, + "action_name": "MyEgress.add_swtrace", + "action_params": { + "swid": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.2", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:02:02", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.22", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:02:16", + "port": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:01:03:00", + "port": 3 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:03:03:00", + "port": 4 + } + } + ] +} diff --git a/exercises/mri/s3-runtime.json b/exercises/mri/s3-runtime.json new file mode 100644 index 000000000..c1ceef9d5 --- /dev/null +++ b/exercises/mri/s3-runtime.json @@ -0,0 +1,48 @@ +{ + "target": "bmv2", + "p4info": "build/mri.p4info", + "bmv2_json": "build/mri.json", + "table_entries": [ + { + "table": "MyEgress.swtrace", + "default_action": true, + "action_name": "MyEgress.add_swtrace", + "action_params": { + "swid": 3 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.3.3", 32] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:00:03:03", + "port": 1 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.1.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:01:04:00", + "port": 2 + } + }, + { + "table": "MyIngress.ipv4_lpm", + "match": { + "hdr.ipv4.dstAddr": ["10.0.2.0", 24] + }, + "action_name": "MyIngress.ipv4_forward", + "action_params": { + "dstAddr": "00:00:00:02:04:00", + "port": 3 + } + } + ] +} diff --git a/P4D2_2017_Fall/exercises/mri/send.py b/exercises/mri/send.py similarity index 100% rename from P4D2_2017_Fall/exercises/mri/send.py rename to exercises/mri/send.py diff --git a/P4D2_2017_Fall/exercises/mri/setup.png b/exercises/mri/setup.png similarity index 100% rename from P4D2_2017_Fall/exercises/mri/setup.png rename to exercises/mri/setup.png diff --git a/P4D2_2017_Fall/exercises/mri/solution/mri.p4 b/exercises/mri/solution/mri.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/mri/solution/mri.p4 rename to exercises/mri/solution/mri.p4 diff --git a/P4D2_2018_East/exercises/ecn/topology.json b/exercises/mri/topology.json similarity index 64% rename from P4D2_2018_East/exercises/ecn/topology.json rename to exercises/mri/topology.json index 2640a2b96..29df53e35 100644 --- a/P4D2_2018_East/exercises/ecn/topology.json +++ b/exercises/mri/topology.json @@ -7,9 +7,9 @@ "h22" ], "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } + "s1": { "runtime_json" : "s1-runtime.json" }, + "s2": { "runtime_json" : "s2-runtime.json" }, + "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["h11", "s1"], ["s1", "s2", "0", 0.5], ["s1", "s3"], diff --git a/exercises/p4runtime/Makefile b/exercises/p4runtime/Makefile new file mode 100644 index 000000000..463ae0757 --- /dev/null +++ b/exercises/p4runtime/Makefile @@ -0,0 +1,5 @@ +BMV2_SWITCH_EXE = simple_switch_grpc +NO_P4 = true +P4C_ARGS = --p4runtime-file $(basename $@).p4info --p4runtime-format text + +include ../../utils/Makefile diff --git a/P4D2_2017_Fall/exercises/p4runtime/README.md b/exercises/p4runtime/README.md similarity index 100% rename from P4D2_2017_Fall/exercises/p4runtime/README.md rename to exercises/p4runtime/README.md diff --git a/P4D2_2017_Fall/exercises/p4runtime/advanced_tunnel.p4 b/exercises/p4runtime/advanced_tunnel.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/p4runtime/advanced_tunnel.p4 rename to exercises/p4runtime/advanced_tunnel.p4 diff --git a/P4D2_2018_East/exercises/p4runtime/mycontroller.py b/exercises/p4runtime/mycontroller.py similarity index 69% rename from P4D2_2018_East/exercises/p4runtime/mycontroller.py rename to exercises/p4runtime/mycontroller.py index b9a4d20b3..4b15421b7 100755 --- a/P4D2_2018_East/exercises/p4runtime/mycontroller.py +++ b/exercises/p4runtime/mycontroller.py @@ -1,17 +1,26 @@ #!/usr/bin/env python2 import argparse +import grpc import os +import sys from time import sleep +# Import P4Runtime lib from parent utils dir +# Probably there's a better way of doing this. +sys.path.append( + os.path.join(os.path.dirname(os.path.abspath(__file__)), + '../../utils/')) import p4runtime_lib.bmv2 +from p4runtime_lib.switch import ShutdownAllSwitchConnections import p4runtime_lib.helper SWITCH_TO_HOST_PORT = 1 SWITCH_TO_SWITCH_PORT = 2 + def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, dst_eth_addr, dst_ip_addr): - ''' + """ Installs three rules: 1) An tunnel ingress rule on the ingress switch in the ipv4_lpm table that encapsulates traffic into a tunnel with the specified ID @@ -27,7 +36,7 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, :param dst_eth_addr: the destination IP to match in the ingress rule :param dst_ip_addr: the destination Ethernet address to write in the egress rule - ''' + """ # 1) Tunnel Ingress Rule table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.ipv4_lpm", @@ -79,24 +88,26 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, egress_sw.WriteTableEntry(table_entry) print "Installed egress tunnel rule on %s" % egress_sw.name + def readTableRules(p4info_helper, sw): - ''' + """ Reads the table entries from all tables on the switch. :param p4info_helper: the P4Info helper :param sw: the switch connection - ''' + """ print '\n----- Reading tables rules for %s -----' % sw.name for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry # TODO For extra credit, you can use the p4info_helper to translate - # the IDs the entry to names + # the IDs in the entry to names print entry print '-----' + def printCounter(p4info_helper, sw, counter_name, index): - ''' + """ Reads the specified counter at the specified index from the switch. In our program, the index is the tunnel ID. If the index is 0, it will return all values from the counter. @@ -105,7 +116,7 @@ def printCounter(p4info_helper, sw, counter_name, index): :param sw: the switch connection :param counter_name: the name of the counter from the P4 program :param index: the counter index (in our case, the tunnel ID) - ''' + """ for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): for entity in response.entities: counter = entity.counter_entry @@ -114,42 +125,58 @@ def printCounter(p4info_helper, sw, counter_name, index): counter.data.packet_count, counter.data.byte_count ) +def printGrpcError(e): + print "gRPC Error:", e.details(), + status_code = e.code() + print "(%s)" % status_code.name, + traceback = sys.exc_info()[2] + print "[%s:%d]" % (traceback.tb_frame.f_code.co_filename, traceback.tb_lineno) def main(p4info_file_path, bmv2_file_path): # Instantiate a P4 Runtime helper from the p4info file p4info_helper = p4runtime_lib.helper.P4InfoHelper(p4info_file_path) - # Create a switch connection object for s1 and s2; - # this is backed by a P4 Runtime gRPC connection - s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection('s1', - address='127.0.0.1:50051', - device_id=0) - s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection('s2', - address='127.0.0.1:50052', - device_id=1) - - # Install the P4 program on the switches - s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) - print "Installed P4 Program using SetForwardingPipelineConfig on %s" % s1.name - s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) - print "Installed P4 Program using SetForwardingPipelineConfig on %s" % s2.name - - # Write the rules that tunnel traffic from h1 to h2 - writeTunnelRules(p4info_helper, ingress_sw=s1, egress_sw=s2, tunnel_id=100, - dst_eth_addr="00:00:00:00:02:02", dst_ip_addr="10.0.2.2") - - # Write the rules that tunnel traffic from h2 to h1 - writeTunnelRules(p4info_helper, ingress_sw=s2, egress_sw=s1, tunnel_id=200, - dst_eth_addr="00:00:00:00:01:01", dst_ip_addr="10.0.1.1") - - # TODO Uncomment the following two lines to read table entries from s1 and s2 - #readTableRules(p4info_helper, s1) - #readTableRules(p4info_helper, s2) - - # Print the tunnel counters every 2 seconds try: + # Create a switch connection object for s1 and s2; + # this is backed by a P4 Runtime gRPC connection. + # Also, dump all P4Runtime messages sent to switch to given txt files. + s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection( + name='s1', + address='127.0.0.1:50051', + device_id=0, + proto_dump_file='logs/s1-p4runtime-requests.txt') + s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection( + name='s2', + address='127.0.0.1:50052', + device_id=1, + proto_dump_file='logs/s2-p4runtime-requests.txt') + + # Send master arbitration update message to establish this controller as + # master (required by P4Runtime before performing any other write operation) + s1.MasterArbitrationUpdate() + s2.MasterArbitrationUpdate() + + # Install the P4 program on the switches + s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, + bmv2_json_file_path=bmv2_file_path) + print "Installed P4 Program using SetForwardingPipelineConfig on s1" + s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, + bmv2_json_file_path=bmv2_file_path) + print "Installed P4 Program using SetForwardingPipelineConfig on s2" + + # Write the rules that tunnel traffic from h1 to h2 + writeTunnelRules(p4info_helper, ingress_sw=s1, egress_sw=s2, tunnel_id=100, + dst_eth_addr="00:00:00:00:02:02", dst_ip_addr="10.0.2.2") + + # Write the rules that tunnel traffic from h2 to h1 + writeTunnelRules(p4info_helper, ingress_sw=s2, egress_sw=s1, tunnel_id=200, + dst_eth_addr="00:00:00:00:01:01", dst_ip_addr="10.0.1.1") + + # TODO Uncomment the following two lines to read table entries from s1 and s2 + # readTableRules(p4info_helper, s1) + # readTableRules(p4info_helper, s2) + + # Print the tunnel counters every 2 seconds while True: sleep(2) print '\n----- Reading tunnel counters -----' @@ -157,9 +184,13 @@ def main(p4info_file_path, bmv2_file_path): printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", 100) printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", 200) printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", 200) + except KeyboardInterrupt: print " Shutting down." + except grpc.RpcError as e: + printGrpcError(e) + ShutdownAllSwitchConnections() if __name__ == '__main__': parser = argparse.ArgumentParser(description='P4Runtime Controller') @@ -179,5 +210,4 @@ def main(p4info_file_path, bmv2_file_path): parser.print_help() print "\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json parser.exit(1) - main(args.p4info, args.bmv2_json) diff --git a/P4D2_2018_East/exercises/p4runtime/solution/mycontroller.py b/exercises/p4runtime/solution/mycontroller.py similarity index 88% rename from P4D2_2018_East/exercises/p4runtime/solution/mycontroller.py rename to exercises/p4runtime/solution/mycontroller.py index 283134db5..458b2f5e2 100755 --- a/P4D2_2018_East/exercises/p4runtime/solution/mycontroller.py +++ b/exercises/p4runtime/solution/mycontroller.py @@ -1,22 +1,24 @@ #!/usr/bin/env python2 import argparse import os -from time import sleep - -# NOTE: Appending to the PYTHON_PATH is only required in the `solution` directory. -# It is not required for mycontroller.py in the top-level directory. import sys -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from time import sleep +# Import P4Runtime lib from parent utils dir +# Probably there's a better way of doing this. +sys.path.append( + os.path.join(os.path.dirname(os.path.abspath(__file__)), + '../../utils/')) import p4runtime_lib.bmv2 import p4runtime_lib.helper SWITCH_TO_HOST_PORT = 1 SWITCH_TO_SWITCH_PORT = 2 + def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, dst_eth_addr, dst_ip_addr): - ''' + """ Installs three rules: 1) An tunnel ingress rule on the ingress switch in the ipv4_lpm table that encapsulates traffic into a tunnel with the specified ID @@ -32,7 +34,7 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, :param dst_eth_addr: the destination IP to match in the ingress rule :param dst_ip_addr: the destination Ethernet address to write in the egress rule - ''' + """ # 1) Tunnel Ingress Rule table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.ipv4_lpm", @@ -62,6 +64,8 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, # and you will need to select the port dynamically for each switch based on # your topology. + # TODO build the transit rule + # TODO install the transit rule on the ingress switch table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.myTunnel_exact", match_fields={ @@ -92,13 +96,14 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, egress_sw.WriteTableEntry(table_entry) print "Installed egress tunnel rule on %s" % egress_sw.name + def readTableRules(p4info_helper, sw): - ''' + """ Reads the table entries from all tables on the switch. :param p4info_helper: the P4Info helper :param sw: the switch connection - ''' + """ print '\n----- Reading tables rules for %s -----' % sw.name for response in sw.ReadTableEntries(): for entity in response.entities: @@ -118,8 +123,9 @@ def readTableRules(p4info_helper, sw): print '%r' % p.value, print + def printCounter(p4info_helper, sw, counter_name, index): - ''' + """ Reads the specified counter at the specified index from the switch. In our program, the index is the tunnel ID. If the index is 0, it will return all values from the counter. @@ -128,7 +134,7 @@ def printCounter(p4info_helper, sw, counter_name, index): :param sw: the switch connection :param counter_name: the name of the counter from the P4 program :param index: the counter index (in our case, the tunnel ID) - ''' + """ for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): for entity in response.entities: counter = entity.counter_entry @@ -143,21 +149,31 @@ def main(p4info_file_path, bmv2_file_path): p4info_helper = p4runtime_lib.helper.P4InfoHelper(p4info_file_path) # Create a switch connection object for s1 and s2; - # this is backed by a P4 Runtime gRPC connection - s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection('s1', - address='127.0.0.1:50051', - device_id=0) - s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection('s2', - address='127.0.0.1:50052', - device_id=1) + # this is backed by a P4 Runtime gRPC connection. + # Also, dump all P4Runtime messages sent to switch to given txt files. + s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection( + name='s1', + address='127.0.0.1:50051', + device_id=0, + proto_dump_file='logs/s1-p4runtime-requests.txt') + s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection( + name='s2', + address='127.0.0.1:50052', + device_id=1, + proto_dump_file='logs/s2-p4runtime-requests.txt') + + # Send master arbitration update message to establish this controller as + # master (required by P4Runtime before performing any other write operation) + s1.MasterArbitrationUpdate() + s2.MasterArbitrationUpdate() # Install the P4 program on the switches s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path) - print "Installed P4 Program using SetForwardingPipelineConfig on %s" % s1.name + print "Installed P4 Program using SetForwardingPipelineConfig on s1" s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path) - print "Installed P4 Program using SetForwardingPipelineConfig on %s" % s2.name + print "Installed P4 Program using SetForwardingPipelineConfig on s2" # Write the rules that tunnel traffic from h1 to h2 writeTunnelRules(p4info_helper, ingress_sw=s1, egress_sw=s2, tunnel_id=100, diff --git a/P4D2_2017_Fall/exercises/p4runtime/topology.json b/exercises/p4runtime/topology.json similarity index 100% rename from P4D2_2017_Fall/exercises/p4runtime/topology.json rename to exercises/p4runtime/topology.json diff --git a/exercises/source_routing/Makefile b/exercises/source_routing/Makefile new file mode 100644 index 000000000..463ae0757 --- /dev/null +++ b/exercises/source_routing/Makefile @@ -0,0 +1,5 @@ +BMV2_SWITCH_EXE = simple_switch_grpc +NO_P4 = true +P4C_ARGS = --p4runtime-file $(basename $@).p4info --p4runtime-format text + +include ../../utils/Makefile diff --git a/P4D2_2018_East/exercises/source_routing/README.md b/exercises/source_routing/README.md similarity index 95% rename from P4D2_2018_East/exercises/source_routing/README.md rename to exercises/source_routing/README.md index 7b5f42a93..348e4063d 100644 --- a/P4D2_2018_East/exercises/source_routing/README.md +++ b/exercises/source_routing/README.md @@ -66,7 +66,7 @@ bring up a network in Mininet to test its behavior. The message was not received because each switch is programmed with `source_routing.p4`, which drops all packets on arrival. You can -verify this by looking at `build/logs/s1.log`. Your job is to extend +verify this by looking at `/tmp/p4s.s1.log`. Your job is to extend the P4 code so packets are delivered to their destination. ## Step 2: Implement source routing @@ -127,8 +127,8 @@ There are several ways that problems might manifest: files contain trace messages describing how each switch processes each packet. The output is detailed and can help pinpoint logic errors in your implementation. The - `build/-.pcap` also contains the pcap - of packets on each interface. Use `tcpdump -r -xxx` to + `-_.pcap` files contain pcap captures + of all packets sent and received on each interface. Use `tcpdump -r -xxx` to print the hexdump of the packets. #### Cleaning up Mininet diff --git a/P4D2_2017_Fall/exercises/source_routing/receive.py b/exercises/source_routing/receive.py similarity index 100% rename from P4D2_2017_Fall/exercises/source_routing/receive.py rename to exercises/source_routing/receive.py diff --git a/exercises/source_routing/s1-runtime.json b/exercises/source_routing/s1-runtime.json new file mode 100644 index 000000000..a8ed4c011 --- /dev/null +++ b/exercises/source_routing/s1-runtime.json @@ -0,0 +1,6 @@ +{ + "target": "bmv2", + "p4info": "build/source_routing.p4info", + "bmv2_json": "build/source_routing.json", + "table_entries": [ ] +} diff --git a/exercises/source_routing/s2-runtime.json b/exercises/source_routing/s2-runtime.json new file mode 100644 index 000000000..a8ed4c011 --- /dev/null +++ b/exercises/source_routing/s2-runtime.json @@ -0,0 +1,6 @@ +{ + "target": "bmv2", + "p4info": "build/source_routing.p4info", + "bmv2_json": "build/source_routing.json", + "table_entries": [ ] +} diff --git a/exercises/source_routing/s3-runtime.json b/exercises/source_routing/s3-runtime.json new file mode 100644 index 000000000..a8ed4c011 --- /dev/null +++ b/exercises/source_routing/s3-runtime.json @@ -0,0 +1,6 @@ +{ + "target": "bmv2", + "p4info": "build/source_routing.p4info", + "bmv2_json": "build/source_routing.json", + "table_entries": [ ] +} diff --git a/P4D2_2017_Fall/exercises/source_routing/send.py b/exercises/source_routing/send.py similarity index 100% rename from P4D2_2017_Fall/exercises/source_routing/send.py rename to exercises/source_routing/send.py diff --git a/P4D2_2017_Fall/exercises/source_routing/solution/source_routing.p4 b/exercises/source_routing/solution/source_routing.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/source_routing/solution/source_routing.p4 rename to exercises/source_routing/solution/source_routing.p4 diff --git a/P4D2_2017_Fall/exercises/source_routing/source_routing.p4 b/exercises/source_routing/source_routing.p4 similarity index 100% rename from P4D2_2017_Fall/exercises/source_routing/source_routing.p4 rename to exercises/source_routing/source_routing.p4 diff --git a/P4D2_2017_Fall/exercises/source_routing/topology.json b/exercises/source_routing/topology.json similarity index 56% rename from P4D2_2017_Fall/exercises/source_routing/topology.json rename to exercises/source_routing/topology.json index e33477cc9..e43e3c0c4 100644 --- a/P4D2_2017_Fall/exercises/source_routing/topology.json +++ b/exercises/source_routing/topology.json @@ -5,9 +5,9 @@ "h3" ], "switches": { - "s1": { "cli_input" : "s1-commands.txt" }, - "s2": { "cli_input" : "s2-commands.txt" }, - "s3": { "cli_input" : "s3-commands.txt" } + "s1": { "runtime_json" : "s1-runtime.json" }, + "s2": { "runtime_json" : "s2-runtime.json" }, + "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], diff --git a/p4v1_1/README.md b/p4v1_1/README.md deleted file mode 100644 index 02c316041..000000000 --- a/p4v1_1/README.md +++ /dev/null @@ -1,2 +0,0 @@ -**P4 v1.1 is deprecated, so this directory is obsolete. We recommend that you - write your P4 programs either in P4_14 (P4 v1.0) or in P4_16.** diff --git a/p4v1_1/env.sh b/p4v1_1/env.sh deleted file mode 100644 index aedb3065c..000000000 --- a/p4v1_1/env.sh +++ /dev/null @@ -1,8 +0,0 @@ -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 ------------------ diff --git a/p4v1_1/simple_router/README.md b/p4v1_1/simple_router/README.md deleted file mode 100644 index be6cb8829..000000000 --- a/p4v1_1/simple_router/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# P4 v1.1 Simple Router - -## Description - -This program is a version of the now famous simple_router program, written -according to the P4 v1.1 specification. In addition to the original -simple_router, this version can keep track of the number of IPv4 packets dropped -because of an expired TTL. This was added to illustrate some of P4 v1.1 more -advanced capabilities. -For more information on P4 v1.1, please refer to [p4.org] (http://p4.org/spec/). - -Look at the [P4 program] (p4src/simple_router.p4) and observe some of the P4 -v1.1 additions: -- strong typing in header type definitions and action declarations -- assignment with `=` instead of `modify_field` (extension to the v1.1 spec) -- register indexing -- support for the ternary operator (extension to the v1.1 spec) - -### Running the demo - -We provide a small demo to let you test the program. Before trying to run the -demo, please make sure that your [env.sh] (../env.sh) file is up-to-date. - -To run the demo: -- start the switch in Mininet with `./run_switch.sh` -- in another terminal, populate the table entries with `./add_entries.sh` -- you should now be able to ping h2 from h1 by typing `h1 ping h2` in the - Mininet CLI - -Once you have the basic demo running, you can start sending packets with a TTL -of 1, activate packet drop tracking in the switch and observe the count go -up. To do this: -- activate the tracking with `./register_on_off.sh on` -- send ICMP packets with a TTL of 1 from the Mininet CLI: `h1 ping h2 -t 1`. The - packets are now dropped by the switch, so you should not be able to observe a - reply - -To get the drop count, simply run `./read_register.sh`. diff --git a/p4v1_1/simple_router/add_entries.sh b/p4v1_1/simple_router/add_entries.sh deleted file mode 100755 index 8ce8a9ac3..000000000 --- a/p4v1_1/simple_router/add_entries.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/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 - -CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI - -$CLI_PATH simple_router.json < commands.txt diff --git a/p4v1_1/simple_router/commands.txt b/p4v1_1/simple_router/commands.txt deleted file mode 100644 index 6c959d3e9..000000000 --- a/p4v1_1/simple_router/commands.txt +++ /dev/null @@ -1,10 +0,0 @@ -table_set_default drop_expired do_drop_expired -table_set_default send_frame _drop -table_set_default forward _drop -table_set_default ipv4_lpm _drop -table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00 -table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:01 -table_add forward set_dmac 10.0.0.10 => 00:04:00:00:00:00 -table_add forward set_dmac 10.0.1.10 => 00:04:00:00:00:01 -table_add ipv4_lpm set_nhop 10.0.0.10/32 => 10.0.0.10 1 -table_add ipv4_lpm set_nhop 10.0.1.10/32 => 10.0.1.10 2 diff --git a/p4v1_1/simple_router/p4src/simple_router.p4 b/p4v1_1/simple_router/p4src/simple_router.p4 deleted file mode 100644 index fdcb4a2e2..000000000 --- a/p4v1_1/simple_router/p4src/simple_router.p4 +++ /dev/null @@ -1,188 +0,0 @@ -/* 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 { - bit<48> dstAddr; - bit<48> srcAddr; - bit<16> etherType; - } -} - -header_type ipv4_t { - fields { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> totalLen; - bit<16> identification; - bit<3> flags; - bit<13> fragOffset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdrChecksum; - bit<32> srcAddr; - bit<32> dstAddr; - } -} - -parser start { - return parse_ethernet; -} - -#define ETHERTYPE_IPV4 0x0800 - -header ethernet_t ethernet; - -parser parse_ethernet { - extract(ethernet); - return select(latest.etherType) { - ETHERTYPE_IPV4 : parse_ipv4; - default: ingress; - } -} - -header ipv4_t ipv4; - -field_list ipv4_checksum_list { - ipv4.version; - ipv4.ihl; - ipv4.diffserv; - ipv4.totalLen; - ipv4.identification; - ipv4.flags; - ipv4.fragOffset; - ipv4.ttl; - ipv4.protocol; - ipv4.srcAddr; - ipv4.dstAddr; -} - -field_list_calculation ipv4_checksum { - input { - ipv4_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field ipv4.hdrChecksum { - verify ipv4_checksum; - update ipv4_checksum; -} - -parser parse_ipv4 { - extract(ipv4); - return ingress; -} - - -action _drop() { - drop(); -} - -header_type routing_metadata_t { - fields { - bit<32> nhop_ipv4; - } -} - -metadata routing_metadata_t routing_metadata; - -register drops_register { - width: 32; - static: drop_expired; - instance_count: 16; -} - -register drops_register_enabled { - width: 1; - static: drop_expired; - instance_count: 16; -} - -action do_drop_expired() { - drops_register[0] = drops_register[0] + ((drops_register_enabled[0] == 1) ? (bit<32>)1 : 0); - drop(); -} - -table drop_expired { - actions { do_drop_expired; } - size: 0; -} - -action set_nhop(in bit<32> nhop_ipv4, in bit<9> port) { - routing_metadata.nhop_ipv4 = nhop_ipv4; - standard_metadata.egress_spec = port; - ipv4.ttl = ipv4.ttl - 1; -} - -table ipv4_lpm { - reads { - ipv4.dstAddr : lpm; - } - actions { - set_nhop; - _drop; - } - size: 1024; -} - -action set_dmac(in bit<48> dmac) { - ethernet.dstAddr = dmac; - // modify_field still valid - // modify_field(ethernet.dstAddr, dmac); -} - -table forward { - reads { - routing_metadata.nhop_ipv4 : exact; - } - actions { - set_dmac; - _drop; - } - size: 512; -} - -action rewrite_mac(in bit<48> smac) { - ethernet.srcAddr = smac; -} - -table send_frame { - reads { - standard_metadata.egress_port: exact; - } - actions { - rewrite_mac; - _drop; - } - size: 256; -} - -control ingress { - if(valid(ipv4)) { - if(ipv4.ttl > 1) { - apply(ipv4_lpm); - apply(forward); - } else { - apply(drop_expired); - } - } -} - -control egress { - apply(send_frame); -} diff --git a/p4v1_1/simple_router/read_register.sh b/p4v1_1/simple_router/read_register.sh deleted file mode 100755 index fcf96284f..000000000 --- a/p4v1_1/simple_router/read_register.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/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 - -CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI - -echo "register_read drops_register 0" | $CLI_PATH simple_router.json diff --git a/p4v1_1/simple_router/register_on_off.sh b/p4v1_1/simple_router/register_on_off.sh deleted file mode 100755 index b70b2273f..000000000 --- a/p4v1_1/simple_router/register_on_off.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/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 - -CLI_PATH=$BMV2_PATH/targets/simple_switch/sswitch_CLI - -if [ $# -lt 1 ]; then - echo "Please specify 'on' or 'off' as argument" - exit 1 -fi -if [ $1 = "on" ]; then - echo "Enabling packet drop count" - v=1 -elif [ $1 = "off" ]; then - echo "Disabling packet drop count" - v=0 -else - echo "Invalid argument, must be one of 'on' or 'off'" - exit 1 -fi - -echo "register_write drops_register_enabled 0 $v" | $CLI_PATH simple_router.json - -echo "Checking value..." -echo "register_read drops_register_enabled 0" | $CLI_PATH simple_router.json diff --git a/p4v1_1/simple_router/run_demo.sh b/p4v1_1/simple_router/run_demo.sh deleted file mode 100755 index 289bc3816..000000000 --- a/p4v1_1/simple_router/run_demo.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/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/tools/runtime_CLI.py - -$P4C_BM_SCRIPT p4src/simple_router.p4 --json simple_router.json --p4-v1.1 -sudo python $BMV2_PATH/mininet/1sw_demo.py \ - --behavioral-exe $BMV2_PATH/targets/simple_switch/simple_switch \ - --json simple_router.json diff --git a/P4D2_2017_Fall/utils/Makefile b/utils/Makefile similarity index 100% rename from P4D2_2017_Fall/utils/Makefile rename to utils/Makefile diff --git a/P4D2_2017_Fall/utils/mininet/appcontroller.py b/utils/mininet/appcontroller.py similarity index 100% rename from P4D2_2017_Fall/utils/mininet/appcontroller.py rename to utils/mininet/appcontroller.py diff --git a/P4D2_2017_Fall/utils/mininet/apptopo.py b/utils/mininet/apptopo.py similarity index 100% rename from P4D2_2017_Fall/utils/mininet/apptopo.py rename to utils/mininet/apptopo.py diff --git a/P4D2_2017_Fall/utils/mininet/multi_switch_mininet.py b/utils/mininet/multi_switch_mininet.py similarity index 100% rename from P4D2_2017_Fall/utils/mininet/multi_switch_mininet.py rename to utils/mininet/multi_switch_mininet.py diff --git a/P4D2_2017_Fall/utils/mininet/p4_mininet.py b/utils/mininet/p4_mininet.py similarity index 100% rename from P4D2_2017_Fall/utils/mininet/p4_mininet.py rename to utils/mininet/p4_mininet.py diff --git a/P4D2_2017_Fall/utils/mininet/shortest_path.py b/utils/mininet/shortest_path.py similarity index 100% rename from P4D2_2017_Fall/utils/mininet/shortest_path.py rename to utils/mininet/shortest_path.py diff --git a/P4D2_2017_Fall/utils/mininet/single_switch_mininet.py b/utils/mininet/single_switch_mininet.py similarity index 100% rename from P4D2_2017_Fall/utils/mininet/single_switch_mininet.py rename to utils/mininet/single_switch_mininet.py diff --git a/P4D2_2017_Fall/utils/netstat.py b/utils/netstat.py similarity index 100% rename from P4D2_2017_Fall/utils/netstat.py rename to utils/netstat.py diff --git a/P4D2_2017_Fall/utils/p4_mininet.py b/utils/p4_mininet.py similarity index 100% rename from P4D2_2017_Fall/utils/p4_mininet.py rename to utils/p4_mininet.py diff --git a/P4D2_2017_Fall/utils/p4apprunner.py b/utils/p4apprunner.py similarity index 100% rename from P4D2_2017_Fall/utils/p4apprunner.py rename to utils/p4apprunner.py diff --git a/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/__init__.py b/utils/p4runtime_lib/__init__.py similarity index 100% rename from P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/__init__.py rename to utils/p4runtime_lib/__init__.py diff --git a/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/bmv2.py b/utils/p4runtime_lib/bmv2.py similarity index 100% rename from P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/bmv2.py rename to utils/p4runtime_lib/bmv2.py diff --git a/P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/convert.py b/utils/p4runtime_lib/convert.py similarity index 100% rename from P4D2_2017_Fall/exercises/p4runtime/p4runtime_lib/convert.py rename to utils/p4runtime_lib/convert.py diff --git a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/helper.py b/utils/p4runtime_lib/helper.py similarity index 93% rename from P4D2_2018_East/exercises/p4runtime/p4runtime_lib/helper.py rename to utils/p4runtime_lib/helper.py index 4906567b3..235b8d145 100644 --- a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/helper.py +++ b/utils/p4runtime_lib/helper.py @@ -18,7 +18,7 @@ from p4 import p4runtime_pb2 from p4.config import p4info_pb2 -from p4runtime_lib.convert import encode +from convert import encode class P4InfoHelper(object): def __init__(self, p4_info_filepath): @@ -145,7 +145,7 @@ def get_action_param(self, action_name, name=None, id=None): elif id is not None: if p.id == id: return p - raise AttributeError("action %r has no param %r" % (action_name, name if name is not None else id)) + raise AttributeError("action %r has no param %r, (has: %r)" % (action_name, name if name is not None else id, a.params)) def get_action_param_id(self, action_name, param_name): return self.get_action_param(action_name, name=param_name).id @@ -162,16 +162,26 @@ def get_action_param_pb(self, action_name, param_name, value): def buildTableEntry(self, table_name, - match_fields={}, + match_fields=None, + default_action=False, action_name=None, - action_params={}): + action_params=None, + priority=None): table_entry = p4runtime_pb2.TableEntry() table_entry.table_id = self.get_tables_id(table_name) + + if priority is not None: + table_entry.priority = priority + if match_fields: table_entry.match.extend([ self.get_match_field_pb(table_name, match_field_name, value) for match_field_name, value in match_fields.iteritems() ]) + + if default_action: + table_entry.is_default_action = True + if action_name: action = table_entry.action.action action.action_id = self.get_actions_id(action_name) @@ -180,4 +190,4 @@ def buildTableEntry(self, self.get_action_param_pb(action_name, field_name, value) for field_name, value in action_params.iteritems() ]) - return table_entry \ No newline at end of file + return table_entry diff --git a/utils/p4runtime_lib/simple_controller.py b/utils/p4runtime_lib/simple_controller.py new file mode 100755 index 000000000..4f130ba72 --- /dev/null +++ b/utils/p4runtime_lib/simple_controller.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python2 +# +# Copyright 2017-present Open Networking Foundation +# +# 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. +# +import argparse +import json +import os +import sys + +import bmv2 +import helper + + +def error(msg): + print >> sys.stderr, ' - ERROR! ' + msg + +def info(msg): + print >> sys.stdout, ' - ' + msg + + +class ConfException(Exception): + pass + + +def main(): + parser = argparse.ArgumentParser(description='P4Runtime Simple Controller') + + parser.add_argument('-a', '--p4runtime-server-addr', + help='address and port of the switch\'s P4Runtime server (e.g. 192.168.0.1:50051)', + type=str, action="store", required=True) + parser.add_argument('-d', '--device-id', + help='Internal device ID to use in P4Runtime messages', + type=int, action="store", required=True) + parser.add_argument('-p', '--proto-dump-file', + help='path to file where to dump protobuf messages sent to the switch', + type=str, action="store", required=True) + parser.add_argument("-c", '--runtime-conf-file', + help="path to input runtime configuration file (JSON)", + type=str, action="store", required=True) + + args = parser.parse_args() + + if not os.path.exists(args.runtime_conf_file): + parser.error("File %s does not exist!" % args.runtime_conf_file) + workdir = os.path.dirname(os.path.abspath(args.runtime_conf_file)) + with open(args.runtime_conf_file, 'r') as sw_conf_file: + program_switch(addr=args.p4runtime_server_addr, + device_id=args.device_id, + sw_conf_file=sw_conf_file, + workdir=workdir, + proto_dump_fpath=args.proto_dump_file) + + +def check_switch_conf(sw_conf, workdir): + required_keys = ["p4info"] + files_to_check = ["p4info"] + target_choices = ["bmv2"] + + if "target" not in sw_conf: + raise ConfException("missing key 'target'") + target = sw_conf['target'] + if target not in target_choices: + raise ConfException("unknown target '%s'" % target) + + if target == 'bmv2': + required_keys.append("bmv2_json") + files_to_check.append("bmv2_json") + + for conf_key in required_keys: + if conf_key not in sw_conf or len(sw_conf[conf_key]) == 0: + raise ConfException("missing key '%s' or empty value" % conf_key) + + for conf_key in files_to_check: + real_path = os.path.join(workdir, sw_conf[conf_key]) + if not os.path.exists(real_path): + raise ConfException("file does not exist %s" % real_path) + + +def program_switch(addr, device_id, sw_conf_file, workdir, proto_dump_fpath): + sw_conf = json_load_byteified(sw_conf_file) + try: + check_switch_conf(sw_conf=sw_conf, workdir=workdir) + except ConfException as e: + error("While parsing input runtime configuration: %s" % str(e)) + return + + info('Using P4Info file %s...' % sw_conf['p4info']) + p4info_fpath = os.path.join(workdir, sw_conf['p4info']) + p4info_helper = helper.P4InfoHelper(p4info_fpath) + + target = sw_conf['target'] + + info("Connecting to P4Runtime server on %s (%s)..." % (addr, target)) + + if target == "bmv2": + sw = bmv2.Bmv2SwitchConnection(address=addr, device_id=device_id, + proto_dump_file=proto_dump_fpath) + else: + raise Exception("Don't know how to connect to target %s" % target) + + try: + sw.MasterArbitrationUpdate() + + if target == "bmv2": + info("Setting pipeline config (%s)..." % sw_conf['bmv2_json']) + bmv2_json_fpath = os.path.join(workdir, sw_conf['bmv2_json']) + sw.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, + bmv2_json_file_path=bmv2_json_fpath) + else: + raise Exception("Should not be here") + + if 'table_entries' in sw_conf: + table_entries = sw_conf['table_entries'] + info("Inserting %d table entries..." % len(table_entries)) + for entry in table_entries: + info(tableEntryToString(entry)) + insertTableEntry(sw, entry, p4info_helper) + finally: + sw.shutdown() + + +def insertTableEntry(sw, flow, p4info_helper): + table_name = flow['table'] + match_fields = flow.get('match') # None if not found + action_name = flow['action_name'] + default_action = flow.get('default_action') # None if not found + action_params = flow['action_params'] + priority = flow.get('priority') # None if not found + + table_entry = p4info_helper.buildTableEntry( + table_name=table_name, + match_fields=match_fields, + default_action=default_action, + action_name=action_name, + action_params=action_params, + priority=priority) + + sw.WriteTableEntry(table_entry) + + +# object hook for josn library, use str instead of unicode object +# https://stackoverflow.com/questions/956867/how-to-get-string-objects-instead-of-unicode-from-json +def json_load_byteified(file_handle): + return _byteify(json.load(file_handle, object_hook=_byteify), + ignore_dicts=True) + + +def _byteify(data, ignore_dicts=False): + # if this is a unicode string, return its string representation + if isinstance(data, unicode): + return data.encode('utf-8') + # if this is a list of values, return list of byteified values + if isinstance(data, list): + return [_byteify(item, ignore_dicts=True) for item in data] + # if this is a dictionary, return dictionary of byteified keys and values + # but only if we haven't already byteified it + if isinstance(data, dict) and not ignore_dicts: + return { + _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) + for key, value in data.iteritems() + } + # if it's anything else, return it in its original form + return data + + +def tableEntryToString(flow): + if 'match' in flow: + match_str = ['%s=%s' % (match_name, str(flow['match'][match_name])) for match_name in + flow['match']] + match_str = ', '.join(match_str) + elif 'default_action' in flow and flow['default_action']: + match_str = '(default action)' + else: + match_str = '(any)' + params = ['%s=%s' % (param_name, str(flow['action_params'][param_name])) for param_name in + flow['action_params']] + params = ', '.join(params) + return "%s: %s => %s(%s)" % ( + flow['table'], match_str, flow['action_name'], params) + + +if __name__ == '__main__': + main() diff --git a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/switch.py b/utils/p4runtime_lib/switch.py similarity index 54% rename from P4D2_2018_East/exercises/p4runtime/p4runtime_lib/switch.py rename to utils/p4runtime_lib/switch.py index 3322b8c60..452873e98 100644 --- a/P4D2_2018_East/exercises/p4runtime/p4runtime_lib/switch.py +++ b/utils/p4runtime_lib/switch.py @@ -12,32 +12,70 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from Queue import Queue from abc import abstractmethod +from datetime import datetime import grpc from p4 import p4runtime_pb2 from p4.tmp import p4config_pb2 +MSG_LOG_MAX_LEN = 1024 + +# List of all active connections +connections = [] + +def ShutdownAllSwitchConnections(): + for c in connections: + c.shutdown() + class SwitchConnection(object): - def __init__(self, name, address='127.0.0.1:50051', device_id=0): + + def __init__(self, name=None, address='127.0.0.1:50051', device_id=0, + proto_dump_file=None): self.name = name self.address = address self.device_id = device_id self.p4info = None self.channel = grpc.insecure_channel(self.address) + if proto_dump_file is not None: + interceptor = GrpcRequestLogger(proto_dump_file) + self.channel = grpc.intercept_channel(self.channel, interceptor) self.client_stub = p4runtime_pb2.P4RuntimeStub(self.channel) + self.requests_stream = IterableQueue() + self.stream_msg_resp = self.client_stub.StreamChannel(iter(self.requests_stream)) + self.proto_dump_file = proto_dump_file + connections.append(self) @abstractmethod def buildDeviceConfig(self, **kwargs): return p4config_pb2.P4DeviceConfig() + def shutdown(self): + self.requests_stream.close() + self.stream_msg_resp.cancel() + + def MasterArbitrationUpdate(self, dry_run=False, **kwargs): + request = p4runtime_pb2.StreamMessageRequest() + request.arbitration.device_id = self.device_id + request.arbitration.election_id.high = 0 + request.arbitration.election_id.low = 1 + + if dry_run: + print "P4 Runtime MasterArbitrationUpdate: ", request + else: + self.requests_stream.put(request) + def SetForwardingPipelineConfig(self, p4info, dry_run=False, **kwargs): device_config = self.buildDeviceConfig(**kwargs) request = p4runtime_pb2.SetForwardingPipelineConfigRequest() + request.election_id.low = 1 request.device_id = self.device_id config = request.config + config.p4info.CopyFrom(p4info) config.p4_device_config = device_config.SerializeToString() + request.action = p4runtime_pb2.SetForwardingPipelineConfigRequest.VERIFY_AND_COMMIT if dry_run: print "P4 Runtime SetForwardingPipelineConfig:", request @@ -47,6 +85,7 @@ def SetForwardingPipelineConfig(self, p4info, dry_run=False, **kwargs): def WriteTableEntry(self, table_entry, dry_run=False): request = p4runtime_pb2.WriteRequest() request.device_id = self.device_id + request.election_id.low = 1 update = request.updates.add() update.type = p4runtime_pb2.Update.INSERT update.entity.table_entry.CopyFrom(table_entry) @@ -80,9 +119,48 @@ def ReadCounters(self, counter_id=None, index=None, dry_run=False): else: counter_entry.counter_id = 0 if index is not None: - counter_entry.index = index + counter_entry.index.index = index if dry_run: print "P4 Runtime Read:", request else: for response in self.client_stub.Read(request): yield response + + +class GrpcRequestLogger(grpc.UnaryUnaryClientInterceptor, + grpc.UnaryStreamClientInterceptor): + """Implementation of a gRPC interceptor that logs request to a file""" + + def __init__(self, log_file): + self.log_file = log_file + with open(self.log_file, 'w') as f: + # Clear content if it exists. + f.write("") + + def log_message(self, method_name, body): + with open(self.log_file, 'a') as f: + ts = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] + msg = str(body) + f.write("\n[%s] %s\n---\n" % (ts, method_name)) + if len(msg) < MSG_LOG_MAX_LEN: + f.write(str(body)) + else: + f.write("Message too long (%d bytes)! Skipping log...\n" % len(msg)) + f.write('---\n') + + def intercept_unary_unary(self, continuation, client_call_details, request): + self.log_message(client_call_details.method, request) + return continuation(client_call_details, request) + + def intercept_unary_stream(self, continuation, client_call_details, request): + self.log_message(client_call_details.method, request) + return continuation(client_call_details, request) + +class IterableQueue(Queue): + _sentinel = object() + + def __iter__(self): + return iter(self.get, self._sentinel) + + def close(self): + self.put(self._sentinel) diff --git a/P4D2_2017_Fall/utils/p4runtime_switch.py b/utils/p4runtime_switch.py similarity index 92% rename from P4D2_2017_Fall/utils/p4runtime_switch.py rename to utils/p4runtime_switch.py index df919ed20..44ecbf5a4 100644 --- a/P4D2_2017_Fall/utils/p4runtime_switch.py +++ b/utils/p4runtime_switch.py @@ -27,9 +27,11 @@ class P4RuntimeSwitch(P4Switch): "BMv2 switch with gRPC support" next_grpc_port = 50051 + next_thrift_port = 9090 def __init__(self, name, sw_path = None, json_path = None, grpc_port = None, + thrift_port = None, pcap_dump = False, log_console = False, verbose = False, @@ -57,6 +59,12 @@ def __init__(self, name, sw_path = None, json_path = None, self.grpc_port = P4RuntimeSwitch.next_grpc_port P4RuntimeSwitch.next_grpc_port += 1 + if thrift_port is not None: + self.thrift_port = thrift_port + else: + self.thrift_port = P4RuntimeSwitch.next_thrift_port + P4RuntimeSwitch.next_thrift_port += 1 + if check_listening_on_port(self.grpc_port): error('%s cannot bind port %d because it is bound by another process\n' % (self.name, self.grpc_port)) exit(1) @@ -104,6 +112,8 @@ def start(self, controllers): args.append("--debugger") if self.log_console: args.append("--log-console") + if self.thrift_port: + args.append('--thrift-port ' + str(self.thrift_port)) if self.grpc_port: args.append("-- --grpc-server-addr 0.0.0.0:" + str(self.grpc_port)) cmd = ' '.join(args) diff --git a/Teaching/utils/run_exercise.py b/utils/run_exercise.py similarity index 87% rename from Teaching/utils/run_exercise.py rename to utils/run_exercise.py index 84997d702..5fd3cc832 100755 --- a/Teaching/utils/run_exercise.py +++ b/utils/run_exercise.py @@ -30,6 +30,7 @@ from mininet.cli import CLI from p4runtime_switch import P4RuntimeSwitch +import p4runtime_lib.simple_controller def configureP4Switch(**switch_args): """ Helper class that is called by mininet to initialize @@ -264,30 +265,51 @@ def create_network(self): switch = switchClass, controller = None) + def program_switch_p4runtime(self, sw_name, sw_dict): + """ This method will use P4Runtime to program the switch using the + content of the runtime JSON file as input. + """ + sw_obj = self.net.get(sw_name) + grpc_port = sw_obj.grpc_port + device_id = sw_obj.device_id + runtime_json = sw_dict['runtime_json'] + self.logger('Configuring switch %s using P4Runtime with file %s' % (sw_name, runtime_json)) + with open(runtime_json, 'r') as sw_conf_file: + outfile = '%s/%s-p4runtime-requests.txt' %(self.log_dir, sw_name) + p4runtime_lib.simple_controller.program_switch( + addr='127.0.0.1:%d' % grpc_port, + device_id=device_id, + sw_conf_file=sw_conf_file, + workdir=os.getcwd(), + proto_dump_fpath=outfile) + + def program_switch_cli(self, sw_name, sw_dict): + """ This method will start up the CLI and use the contents of the + command files as input. + """ + cli = 'simple_switch_CLI' + # get the port for this particular switch's thrift server + sw_obj = self.net.get(sw_name) + thrift_port = sw_obj.thrift_port + + cli_input_commands = sw_dict['cli_input'] + self.logger('Configuring switch %s with file %s' % (sw_name, cli_input_commands)) + with open(cli_input_commands, 'r') as fin: + cli_outfile = '%s/%s_cli_output.log'%(self.log_dir, sw_name) + with open(cli_outfile, 'w') as fout: + subprocess.Popen([cli, '--thrift-port', str(thrift_port)], + stdin=fin, stdout=fout) def program_switches(self): - """ If any command files were provided for the switches, - this method will start up the CLI on each switch and use the - contents of the command files as input. - - Assumes: - - A mininet instance is stored as self.net and self.net.start() has - been called. + """ This method will program each switch using the BMv2 CLI and/or + P4Runtime, depending if any command or runtime JSON files were + provided for the switches. """ - cli = 'simple_switch_CLI' for sw_name, sw_dict in self.switches.iteritems(): - if 'cli_input' not in sw_dict: continue - # get the port for this particular switch's thrift server - sw_obj = self.net.get(sw_name) - thrift_port = sw_obj.thrift_port - - cli_input_commands = sw_dict['cli_input'] - self.logger('Configuring switch %s with file %s' % (sw_name, cli_input_commands)) - with open(cli_input_commands, 'r') as fin: - cli_outfile = '%s/%s_cli_output.log'%(self.log_dir, sw_name) - with open(cli_outfile, 'w') as fout: - subprocess.Popen([cli, '--thrift-port', str(thrift_port)], - stdin=fin, stdout=fout) + if 'cli_input' in sw_dict: + self.program_switch_cli(sw_name, sw_dict) + if 'runtime_json' in sw_dict: + self.program_switch_p4runtime(sw_name, sw_dict) def program_hosts(self): """ Adds static ARP entries and default routes to each mininet host. diff --git a/P4D2_2018_East/vm/Vagrantfile b/vm/Vagrantfile similarity index 96% rename from P4D2_2018_East/vm/Vagrantfile rename to vm/Vagrantfile index 46792ee55..011a4c94c 100644 --- a/P4D2_2018_East/vm/Vagrantfile +++ b/vm/Vagrantfile @@ -2,7 +2,7 @@ # vi: set ft=ruby : Vagrant.configure(2) do |config| - config.vm.box = "bento/ubuntu-16.04" + config.vm.box = "fso/xenial64-desktop" config.vm.define "p4-tutorial" do |tutorial| end config.vm.provider "virtualbox" do |vb| diff --git a/P4D2_2017_Fall/vm/p4-logo.png b/vm/p4-logo.png similarity index 100% rename from P4D2_2017_Fall/vm/p4-logo.png rename to vm/p4-logo.png diff --git a/P4D2_2017_Fall/vm/p4.vim b/vm/p4.vim similarity index 100% rename from P4D2_2017_Fall/vm/p4.vim rename to vm/p4.vim diff --git a/P4D2_2017_Fall/vm/p4_16-mode.el b/vm/p4_16-mode.el similarity index 100% rename from P4D2_2017_Fall/vm/p4_16-mode.el rename to vm/p4_16-mode.el diff --git a/P4D2_2018_East/vm/root-bootstrap.sh b/vm/root-bootstrap.sh similarity index 100% rename from P4D2_2018_East/vm/root-bootstrap.sh rename to vm/root-bootstrap.sh diff --git a/P4D2_2018_East/vm/user-bootstrap.sh b/vm/user-bootstrap.sh similarity index 93% rename from P4D2_2018_East/vm/user-bootstrap.sh rename to vm/user-bootstrap.sh index aa8074e3c..c33784a73 100644 --- a/P4D2_2018_East/vm/user-bootstrap.sh +++ b/vm/user-bootstrap.sh @@ -5,9 +5,9 @@ set -x # Exit on errors. set -e -BMV2_COMMIT="39abe290b4143e829b8f983965fcdc711e3c450c" -PI_COMMIT="afd5831393824228246ea01b26da2f93d38fd20c" -P4C_COMMIT="80f8970b5ec8e57c4a3611da343461b5b0a8dda3" +BMV2_COMMIT="7e25eeb19d01eee1a8e982dc7ee90ee438c10a05" +PI_COMMIT="219b3d67299ec09b49f433d7341049256ab5f512" +P4C_COMMIT="48a57a6ae4f96961b74bd13f6bdeac5add7bb815" PROTOBUF_COMMIT="v3.2.0" GRPC_COMMIT="v1.3.2" @@ -32,7 +32,10 @@ make -j${NUM_CORES} sudo make install sudo ldconfig unset CFLAGS CXXFLAGS LDFLAGS -cd .. +# force install python module +cd python +sudo python setup.py install +cd ../.. # gRPC git clone https://github.com/grpc/grpc.git @@ -86,7 +89,7 @@ sudo ldconfig # Simple_switch_grpc target cd targets/simple_switch_grpc ./autogen.sh -./configure +./configure --with-thrift make -j${NUM_CORES} sudo make install sudo ldconfig diff --git a/workshop_05_2016/ecmp/.starter_code/commands.txt b/workshop_05_2016/ecmp/.starter_code/commands.txt deleted file mode 100644 index e9965d6be..000000000 --- a/workshop_05_2016/ecmp/.starter_code/commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default send_frame _drop -table_set_default forward _drop -table_set_default ipv4_lpm _drop -table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00 -table_add forward set_dmac 10.0.1.1 => 00:04:00:00:00:00 -table_add ipv4_lpm set_nhop 10.0.0.1/32 => 10.0.1.1 1 diff --git a/workshop_05_2016/ecmp/.starter_code/p4src/simple_router.p4 b/workshop_05_2016/ecmp/.starter_code/p4src/simple_router.p4 deleted file mode 100644 index 0b450f740..000000000 --- a/workshop_05_2016/ecmp/.starter_code/p4src/simple_router.p4 +++ /dev/null @@ -1,190 +0,0 @@ -/* 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_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; - } -} - -header_type tcp_t { - fields { - srcPort : 16; - dstPort : 16; - seqNo : 32; - ackNo : 32; - dataOffset : 4; - res : 3; - ecn : 3; - ctrl : 6; - window : 16; - checksum : 16; - urgentPtr : 16; - } -} - -parser start { - return parse_ethernet; -} - -#define ETHERTYPE_IPV4 0x0800 - -header ethernet_t ethernet; - -parser parse_ethernet { - extract(ethernet); - return select(latest.etherType) { - ETHERTYPE_IPV4 : parse_ipv4; - default: ingress; - } -} - -header ipv4_t ipv4; - -field_list ipv4_checksum_list { - ipv4.version; - ipv4.ihl; - ipv4.diffserv; - ipv4.totalLen; - ipv4.identification; - ipv4.flags; - ipv4.fragOffset; - ipv4.ttl; - ipv4.protocol; - ipv4.srcAddr; - ipv4.dstAddr; -} - -field_list_calculation ipv4_checksum { - input { - ipv4_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field ipv4.hdrChecksum { - verify ipv4_checksum; - update ipv4_checksum; -} - -#define IP_PROTOCOLS_TCP 6 - -parser parse_ipv4 { - extract(ipv4); - return select(latest.protocol) { - IP_PROTOCOLS_TCP : parse_tcp; - default: ingress; - } -} - -header tcp_t tcp; - -parser parse_tcp { - extract(tcp); - return ingress; -} - - -action _drop() { - drop(); -} - -header_type routing_metadata_t { - fields { - nhop_ipv4 : 32; - // TODO: if you need extra metadata for ECMP, define it here - } -} - -metadata routing_metadata_t routing_metadata; - -action set_nhop(nhop_ipv4, port) { - modify_field(routing_metadata.nhop_ipv4, nhop_ipv4); - modify_field(standard_metadata.egress_spec, port); - add_to_field(ipv4.ttl, -1); -} - -table ipv4_lpm { - reads { - ipv4.dstAddr : lpm; - } - actions { - set_nhop; - _drop; - } - size: 1024; -} - -action set_dmac(dmac) { - modify_field(ethernet.dstAddr, dmac); -} - -table forward { - reads { - routing_metadata.nhop_ipv4 : exact; - } - actions { - set_dmac; - _drop; - } - size: 512; -} - -action rewrite_mac(smac) { - modify_field(ethernet.srcAddr, smac); -} - -table send_frame { - reads { - standard_metadata.egress_port: exact; - } - actions { - rewrite_mac; - _drop; - } - size: 256; -} - -control ingress { - if(valid(ipv4) and ipv4.ttl > 0) { - // TODO: implement ECMP here - apply(ipv4_lpm); - apply(forward); - } -} - -control egress { - apply(send_frame); -} diff --git a/workshop_05_2016/ecmp/README.md b/workshop_05_2016/ecmp/README.md deleted file mode 100644 index e620b8330..000000000 --- a/workshop_05_2016/ecmp/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Implementing ECMP on top of simple_router.p4 - -## Introduction - -simple_router.p4 is a very simple P4 program which does L3 routing. All the P4 -code can be found in the [p4src/simple_router.p4](p4src/simple_router.p4) -file. In this exercise we will try to build ECMP on top of the starter code. We -will be assuming the following network topology: - -``` - --------------------------------- nhop-0 10.0.1.1 - | 00:04:00:00:00:00 - 1 - 00:aa:bb:00:00:00 - | --------- 3--sw - | - 2 - 00:aa:bb:00:00:01 - | - --------------------------------- nhop-1 10.0.2.1 - 00:04:00:00:00:01 -``` - -Note that we do not assign IPv4 addresses to the 3 switch interfaces, we do not -need to for this exercise. -We will be sending test packets on interface `3` of the switch. These packets -will have destination IP `10.0.0.1`. We will assume that both `nhop-0` and -`nhop-1` have a path to `10.0.0.1`, which is the final destination of our test -packets. - -## Running the starter code - -*Before starting make sure that you run `sudo ./veth_setup.sh` to create the -veth pairs required for the demo.* - -To compile and run the starter code, simply use `./run_demo.sh`. The -[run_demo.sh](run_demo.sh) script will run the P4 compiler (for bmv2), start the -switch and populate the tables using the CLI commands from -[commands.txt](commands.txt). - -When the switch is running, you can send test packets with `sudo -./run_test.py`. Note that this script will take a few seconds to complete. The -test sends a few hundred identical TCP packets through the switch, in bursts, -on port 3. If you take a look at the P4 code and at commands.txt, you will see -that each TCP packet is forwarded out of port 1; since we do not have ECMP -working yet. - -## What you need to do - -1. In this exercise, you need to update the provided [P4 -program](p4src/simple_router.p4) to perform ECMP. When you are done, each -incoming TCP test packet should be forwarded to either port 1 or port 2, based -on the result of a crc16 hash computation performed on the TCP 5-tuple -(`ipv4.srcAddr`, `ipv4.dstAddr`, `ipv4.protocol`, `tcp.srcPort`, -`tcp.dstPort`). You will need to refer to the [P4 -spec](http://p4.org/wp-content/uploads/2015/04/p4-latest.pdf) to familiarize -yourself with the P4 constructs you will need. - -2. Once you are done with the P4 code, you will need to update -[commands.txt](commands.txt) to configure your new tables. - -3. After that you can run the above test again. Once again, you will observe -that all packets go to the same egress port. Don't panic :)! This is because all -packets are identical and therefore are forwarded in the same way, If you add -`--random-dport` when running `sudo ./run_test.py`, you should observe an even -distribution for the ports. This option assigns a random destination port to -each test TCP packet (the 5-tuple is different, so the hash is likely to be -different). - -## Hints and directions - -1. You can easily check the syntax of your P4 program with `p4-validate `. - -2. There are 2 major ways of implementing ECMP on top of simple_router.p4. The -first one requires 2 tables and the use of the -`modify_field_with_hash_based_offset` primitive. The second one uses a single -table with an action profile. You can read about -`modify_field_with_hash_based_offset` and action profiles in the [P4 -spec](http://p4.org/wp-content/uploads/2015/04/p4-latest.pdf). - -3. If you choose to use the first way (with 2 tables), your first table will -match on the destination IP address and be in charge of computing an index -(using `modify_field_with_hash_based_offset`), while the second table will match -on this computed index to obtain the outgoing interface. This is a high level -view of what needs to be implemented in P4. -``` -T1 -IP_prefix_1 ---> "random" index in [0, 1] using modify_field_with_hash_based_offset -IP_prefix_2 ---> "random" index in [2, 4] ... -... - -T2 -index(0) ---> nhop A -index(1) ---> nhop B -index(2) ---> nhop C -index(3) ---> nhop D -index(4) ---> nhop E -``` -Remember that `T1` and `T2`'s entries will come from your modified commands.txt. diff --git a/workshop_05_2016/ecmp/commands.txt b/workshop_05_2016/ecmp/commands.txt deleted file mode 100644 index e9965d6be..000000000 --- a/workshop_05_2016/ecmp/commands.txt +++ /dev/null @@ -1,6 +0,0 @@ -table_set_default send_frame _drop -table_set_default forward _drop -table_set_default ipv4_lpm _drop -table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00 -table_add forward set_dmac 10.0.1.1 => 00:04:00:00:00:00 -table_add ipv4_lpm set_nhop 10.0.0.1/32 => 10.0.1.1 1 diff --git a/workshop_05_2016/ecmp/p4src/simple_router.p4 b/workshop_05_2016/ecmp/p4src/simple_router.p4 deleted file mode 100644 index 0b450f740..000000000 --- a/workshop_05_2016/ecmp/p4src/simple_router.p4 +++ /dev/null @@ -1,190 +0,0 @@ -/* 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_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; - } -} - -header_type tcp_t { - fields { - srcPort : 16; - dstPort : 16; - seqNo : 32; - ackNo : 32; - dataOffset : 4; - res : 3; - ecn : 3; - ctrl : 6; - window : 16; - checksum : 16; - urgentPtr : 16; - } -} - -parser start { - return parse_ethernet; -} - -#define ETHERTYPE_IPV4 0x0800 - -header ethernet_t ethernet; - -parser parse_ethernet { - extract(ethernet); - return select(latest.etherType) { - ETHERTYPE_IPV4 : parse_ipv4; - default: ingress; - } -} - -header ipv4_t ipv4; - -field_list ipv4_checksum_list { - ipv4.version; - ipv4.ihl; - ipv4.diffserv; - ipv4.totalLen; - ipv4.identification; - ipv4.flags; - ipv4.fragOffset; - ipv4.ttl; - ipv4.protocol; - ipv4.srcAddr; - ipv4.dstAddr; -} - -field_list_calculation ipv4_checksum { - input { - ipv4_checksum_list; - } - algorithm : csum16; - output_width : 16; -} - -calculated_field ipv4.hdrChecksum { - verify ipv4_checksum; - update ipv4_checksum; -} - -#define IP_PROTOCOLS_TCP 6 - -parser parse_ipv4 { - extract(ipv4); - return select(latest.protocol) { - IP_PROTOCOLS_TCP : parse_tcp; - default: ingress; - } -} - -header tcp_t tcp; - -parser parse_tcp { - extract(tcp); - return ingress; -} - - -action _drop() { - drop(); -} - -header_type routing_metadata_t { - fields { - nhop_ipv4 : 32; - // TODO: if you need extra metadata for ECMP, define it here - } -} - -metadata routing_metadata_t routing_metadata; - -action set_nhop(nhop_ipv4, port) { - modify_field(routing_metadata.nhop_ipv4, nhop_ipv4); - modify_field(standard_metadata.egress_spec, port); - add_to_field(ipv4.ttl, -1); -} - -table ipv4_lpm { - reads { - ipv4.dstAddr : lpm; - } - actions { - set_nhop; - _drop; - } - size: 1024; -} - -action set_dmac(dmac) { - modify_field(ethernet.dstAddr, dmac); -} - -table forward { - reads { - routing_metadata.nhop_ipv4 : exact; - } - actions { - set_dmac; - _drop; - } - size: 512; -} - -action rewrite_mac(smac) { - modify_field(ethernet.srcAddr, smac); -} - -table send_frame { - reads { - standard_metadata.egress_port: exact; - } - actions { - rewrite_mac; - _drop; - } - size: 256; -} - -control ingress { - if(valid(ipv4) and ipv4.ttl > 0) { - // TODO: implement ECMP here - apply(ipv4_lpm); - apply(forward); - } -} - -control egress { - apply(send_frame); -} diff --git a/workshop_05_2016/ecmp/run_demo.sh b/workshop_05_2016/ecmp/run_demo.sh deleted file mode 100755 index 7b5246cb8..000000000 --- a/workshop_05_2016/ecmp/run_demo.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/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/tools/runtime_CLI.py - -# 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/simple_router.p4 --json simple_router.json -if [ $? -ne 0 ]; then -echo "p4 compilation failed" -exit 1 -fi -# This gets root permissions, and gives libtool the opportunity to "warm-up" -sudo $SWITCH_PATH >/dev/null 2>&1 -sudo $SWITCH_PATH simple_router.json \ - -i 0@veth0 -i 1@veth2 -i 2@veth4 -i 3@veth6 -i 4@veth8 \ - --log-console \ - --pcap & -sleep 2 -echo "**************************************" -echo "Sending commands to switch through CLI" -echo "**************************************" -$CLI_PATH --json simple_router.json < commands.txt -echo "READY!!!" -fg diff --git a/workshop_05_2016/ecmp/run_test.py b/workshop_05_2016/ecmp/run_test.py deleted file mode 100755 index 81f73fed9..000000000 --- a/workshop_05_2016/ecmp/run_test.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env python - -# 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. - -import time - -NUM_PACKETS = 500 - -import random -import argparse - -import threading -from scapy.all import sniff -from scapy.all import Ether, IP, IPv6, TCP - -parser = argparse.ArgumentParser(description='run_test.py') -parser.add_argument('--random-dport', - help='Use a random TCP dest port for each packet', - action="store_true", default=False) -args = parser.parse_args() - -class PacketQueue: - def __init__(self): - self.pkts = [] - self.lock = threading.Lock() - self.ifaces = set() - - def add_iface(self, iface): - self.ifaces.add(iface) - - def get(self): - self.lock.acquire() - if not self.pkts: - self.lock.release() - return None, None - pkt = self.pkts.pop(0) - self.lock.release() - return pkt - - def add(self, iface, pkt): - if iface not in self.ifaces: - return - self.lock.acquire() - self.pkts.append( (iface, pkt) ) - self.lock.release() - -queue = PacketQueue() - -def pkt_handler(pkt, iface): - if IPv6 in pkt: - return - queue.add(iface, pkt) - -class SnifferThread(threading.Thread): - def __init__(self, iface, handler = pkt_handler): - threading.Thread.__init__(self) - self.iface = iface - self.handler = handler - - def run(self): - sniff( - iface = self.iface, - prn = lambda x: self.handler(x, self.iface) - ) - -class PacketDelay: - def __init__(self, bsize, bdelay, imin, imax, num_pkts = 100): - self.bsize = bsize - self.bdelay = bdelay - self.imin = imin - self.imax = imax - self.num_pkts = num_pkts - self.current = 1 - - def __iter__(self): - return self - - def next(self): - if self.num_pkts <= 0: - raise StopIteration - self.num_pkts -= 1 - if self.current == self.bsize: - self.current = 1 - return random.randint(self.imin, self.imax) - else: - self.current += 1 - return self.bdelay - - -pkt = Ether()/IP(dst='10.0.0.1', ttl=64)/TCP() - -port_map = { - 1: "veth3", - 2: "veth5", - 3: "veth7" -} - -iface_map = {} -for p, i in port_map.items(): - iface_map[i] = p - -queue.add_iface("veth3") -queue.add_iface("veth5") - -for p, iface in port_map.items(): - t = SnifferThread(iface) - t.daemon = True - t.start() - -import socket - -send_socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, - socket.htons(0x03)) -send_socket.bind((port_map[3], 0)) - -delays = PacketDelay(10, 5, 25, 100, NUM_PACKETS) -ports = [] -print "Sending", NUM_PACKETS, "packets ..." -for d in delays: - # sendp is too slow... - # sendp(pkt, iface=port_map[3], verbose=0) - if args.random_dport: - pkt["TCP"].dport = random.randint(1025, 65535) - send_socket.send(str(pkt)) - time.sleep(d / 1000.) -time.sleep(1) -iface, pkt = queue.get() -while pkt: - ports.append(iface_map[iface]) - iface, pkt = queue.get() -print ports -print "DISTRIBUTION..." -for p in port_map: - c = ports.count(p) - print "port {}: {:>3} [ {:>5}% ]".format(p, c, 100. * c / NUM_PACKETS) diff --git a/workshop_05_2016/ecmp/solution_1.tar.gz b/workshop_05_2016/ecmp/solution_1.tar.gz deleted file mode 100644 index 7f243e902..000000000 Binary files a/workshop_05_2016/ecmp/solution_1.tar.gz and /dev/null differ diff --git a/workshop_05_2016/ecmp/solution_2.tar.gz b/workshop_05_2016/ecmp/solution_2.tar.gz deleted file mode 100644 index 5c2506eaa..000000000 Binary files a/workshop_05_2016/ecmp/solution_2.tar.gz and /dev/null differ diff --git a/workshop_05_2016/ecmp/veth_setup.sh b/workshop_05_2016/ecmp/veth_setup.sh deleted file mode 100755 index 40835412c..000000000 --- a/workshop_05_2016/ecmp/veth_setup.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/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