SIGCOMM 2019 Tutorial Edits (#272)
* Updated the utils/run_exercise.py to allow exercises to customize host configuration from the topology.json file. Now hosts and `ping` each other in the basic exercise. Other Linux utilities should work as well (e.g. iperf). ``` mininet> h1 ping h2 PING 10.0.2.2 (10.0.2.2) 56(84) bytes of data. 64 bytes from 10.0.2.2: icmp_seq=1 ttl=62 time=3.11 ms 64 bytes from 10.0.2.2: icmp_seq=2 ttl=62 time=2.34 ms 64 bytes from 10.0.2.2: icmp_seq=3 ttl=62 time=2.15 ms ^C --- 10.0.2.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 2.153/2.540/3.118/0.416 ms mininet> pingall *** Ping: testing ping reachability h1 -> h2 h3 h2 -> h1 h3 h3 -> h1 h2 *** Results: 0% dropped (6/6 received) ``` Only updated basic exercise, still need to update other exercises. Also, updated the root-bootstrap.sh because I was running into issues with latest version of vagrant. * Accidentially added the solution to the basic exercise in the previous commit. Undoing that here ... * Updated the topology.json file and table entries for the basic_tunnel exercise. * Updated P4Runtime exercise with new topology and table entries. * Fixed MAC addresses in P4Runtime exercise. It is working now. * Fixed MAC addresses in P4Runtime exercise starter code * Updated ECN exercise to use new topology.json file. Updated the table entries / MAC addresses as well. * Updated the topology.json file and table entries for the MRI exercise. * Updated source_routing exercise with new topology file and verified correct functionality. * Updated load_balance exercise with new topology. * Moved basic exercise triangle topology into a separate folder * Added new topology for the basic exercise: a single pod of a fat-tree. * Updated Makefiles and run_exercise.py to allow exercises to configure each switch with a different P4 program. This is mainly for the firewall exercise. * Updated Makefiles of project to work with new utils/Makefile * Updated load_balance and p4runtime exercise Makefiles * Initial commit of the firewall exercise, which is a simple stateful firewall that uses a bloom filter. Need to update README files * Initial commit of the path_monitor exercise. It is working but still need to update the README and figure out what we want the tutorial attendees to implement. * Updated README file in firewall exercise. Also removed the bits from the starter code that we want the tutorial attendees to implement * Renamed path_monitor exercise to link_monitor * Updated the README in the link_monitor exercise and removed the bits from the starter code that we want the tutorial attendees to implement. * Updated README for the firewall exercise * Adding pod-topo.png image to basic exercise * Added firewall-topo.png image to firewall exercise * Added link-monitor-topo.png to link_monitor exercise * Updated README files to point to topology images * Updated top-level README to point to new exercises. * Fixed link for VM dependencies script in README * Updated bmv2/pi/p4c commits * Updated README files for exercises to fix some typos and added a note about the V1Model architecture. * Added a note about food for thought in the link_monitor README * Updated the firewall.p4 program to use two register arrays rather than a single one. This is to make the design more portable to high line rate devices which can only support a single access to each register array. * Minor fix to firewall exercise to get rid of compiler warning. * Updated comment in firewall exercise. * Minor (typo) fixes in the firewall ReadMe * More info in firewall exercise ReadMe step 2 * Updated firewall.p4 to reuse direction variable * More testing steps, small fixes in firewall exercise Readme * Added food for thought to firewall Readme * Cosmetic fixes to firewall ReadMe * Made a few updates to the basic exercise README and added more details to the link_monitor exercise README. Also added a command to install grip when provisioning the VM. This could be useful for rendering the markdown README files offline. * Updated top level README so it can be merged into the master branch. * Moved cmd to install grip from root-bootstrap to user-bootstrap
15
README.md
@ -21,6 +21,10 @@ you get started with P4 programming, organized into several modules:
|
||||
* [Calculator](./exercises/other/calc)
|
||||
* [Load Balancing](./exercises/load_balance)
|
||||
|
||||
5. Stateful Packet Processing
|
||||
* [Firewall](./exercises/firewall)
|
||||
* [Link Monitoring](./exercises/link_monitor)
|
||||
|
||||
## Presentation
|
||||
|
||||
The slides are available [online](http://bit.ly/p4d2-2018-spring) and
|
||||
@ -31,9 +35,10 @@ which contains various examples that you can refer to.
|
||||
|
||||
## Obtaining required software
|
||||
|
||||
If you are starting this tutorial at the Spring 2018 P4 Developer Day,
|
||||
If you are starting this tutorial at one of the proctored tutorial events,
|
||||
then we've already provided you with a virtual machine that has all of
|
||||
the required software installed.
|
||||
the required software installed. Ask an instructor for a USB stick with
|
||||
the VM image.
|
||||
|
||||
Otherwise, to complete the exercises, you will need to either build a
|
||||
virtual machine or install several dependencies.
|
||||
@ -47,11 +52,11 @@ To build the virtual machine:
|
||||
- When the machine reboots, you should have a graphical desktop machine with the required
|
||||
software pre-installed.
|
||||
|
||||
*Note: Before running the `vagrant up` command, make sure you have enabled virtualization in your environment; otherwise you may get a "VT-x is disabled in the BIOS for both all CPU modes" error. Check [this](https://stackoverflow.com/questions/33304393/vt-x-is-disabled-in-the-bios-for-both-all-cpu-modes-verr-vmx-msr-all-vmx-disabl) for enabling it in virtualbox and/or BIOS for different system configurations.
|
||||
*Note*: Before running the `vagrant up` command, make sure you have enabled virtualization in your environment; otherwise you may get a "VT-x is disabled in the BIOS for both all CPU modes" error. Check [this](https://stackoverflow.com/questions/33304393/vt-x-is-disabled-in-the-bios-for-both-all-cpu-modes-verr-vmx-msr-all-vmx-disabl) for enabling it in virtualbox and/or BIOS for different system configurations.
|
||||
|
||||
You will need the script to execute to completion before you can see the `p4` login on your virtual machine's GUI. In some cases, the `vagrant up` command brings up only the default `vagrant` login with the password `vagrant`. Dependencies may or may not have been installed for you to proceed with running P4 programs. Please refer the existing issues to help fix your problem or create a new one if your specific problem isn't addressed there.*
|
||||
You will need the script to execute to completion before you can see the `p4` login on your virtual machine's GUI. In some cases, the `vagrant up` command brings up only the default `vagrant` login with the password `vagrant`. Dependencies may or may not have been installed for you to proceed with running P4 programs. Please refer the [existing issues](https://github.com/p4lang/tutorials/issues) to help fix your problem or create a new one if your specific problem isn't addressed there.
|
||||
|
||||
To install dependencies by hand, please reference the [vm](../vm) installation scripts.
|
||||
To install dependencies by hand, please reference the [vm](./vm) installation scripts.
|
||||
They contain the dependencies, versions, and installation procedure.
|
||||
You should be able to run them directly on an Ubuntu 16.04 machine:
|
||||
- `sudo ./root-bootstrap.sh`
|
||||
|
@ -1,5 +1,4 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
NO_P4 = true
|
||||
P4C_ARGS = --p4runtime-files $(basename $@).p4.p4info.txt
|
||||
TOPO = pod-topo/topology.json
|
||||
|
||||
include ../../utils/Makefile
|
||||
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 97 B |
@ -17,6 +17,17 @@ 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.
|
||||
|
||||
We will use the following topology for this exercise. It is a single
|
||||
pod of a fat-tree topology and henceforth referred to as pod-topo:
|
||||

|
||||
|
||||
Our P4 program will be written for the V1Model architecture implemented
|
||||
on P4.org's bmv2 software switch. The architecture file for the V1Model
|
||||
can be found at: /usr/local/share/p4c/p4include/v1model.p4. This file
|
||||
desribes the interfaces of the P4 programmable elements in the architecture,
|
||||
the supported externs, as well as the architecture's standard metadata
|
||||
fields. We encourage you to take a look at it.
|
||||
|
||||
> **Spoiler alert:** There is a reference solution in the `solution`
|
||||
> sub-directory. Feel free to compare your implementation to the
|
||||
> reference.
|
||||
@ -36,27 +47,18 @@ up a switch in Mininet to test its behavior.
|
||||
```
|
||||
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`.
|
||||
* start the pod-topo in Mininet and configure all switches with
|
||||
the appropriate P4 program + table entries, and
|
||||
* configure all hosts with the commands listed in
|
||||
[pod-topo/topology.json](./pod-topo/topology.json)
|
||||
|
||||
2. You should now see a Mininet command prompt. Open two terminals
|
||||
for `h1` and `h2`, respectively:
|
||||
2. You should now see a Mininet command prompt. Try to ping between
|
||||
hosts in the topology:
|
||||
```bash
|
||||
mininet> xterm h1 h2
|
||||
mininet> h1 ping h2
|
||||
mininet> pingall
|
||||
```
|
||||
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.
|
||||
3. Type `exit` to leave each xterm and the Mininet command line.
|
||||
Then, to stop mininet:
|
||||
```bash
|
||||
make stop
|
||||
@ -66,7 +68,7 @@ server. In `h2`'s xterm, start the server:
|
||||
make clean
|
||||
```
|
||||
|
||||
The message was not received because each switch is programmed
|
||||
The ping failed 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.
|
||||
|
||||
@ -77,7 +79,7 @@ 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
|
||||
In this exercise, we have already implemented 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-runtime.json` files, where
|
||||
@ -86,7 +88,7 @@ each switch. These are defined in the `sX-runtime.json` files, where
|
||||
**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
|
||||
file `build/basic.p4.p4info.txt` 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.
|
||||
|
||||
@ -120,20 +122,22 @@ A complete `basic.p4` will contain the following components:
|
||||
|
||||
## Step 3: Run your solution
|
||||
|
||||
Follow the instructions from Step 1. This time, your message from
|
||||
`h1` should be delivered to `h2`.
|
||||
Follow the instructions from Step 1. This time, you should be able to
|
||||
sucessfully ping between any two hosts in the topology.
|
||||
|
||||
### 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?
|
||||
The "test suite" for your solution---sending pings between hosts in the
|
||||
topology---is not very robust. What else should you test to be confident
|
||||
that you implementation is correct?
|
||||
|
||||
> 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 respond to ARP requests?
|
||||
- How would you enhance your program to support traceroute?
|
||||
- How would you enhance your program to support next hops?
|
||||
- Is this program enough to replace a router? What's missing?
|
||||
|
||||
@ -152,7 +156,7 @@ 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.<switch-name>.log` files contain detailed logs
|
||||
way. The `logs/sX.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.
|
||||
|
||||
@ -166,9 +170,3 @@ these instances:
|
||||
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)!
|
||||
|
||||
|
BIN
exercises/basic/pod-topo/pod-topo.png
Normal file
After Width: | Height: | Size: 130 KiB |
57
exercises/basic/pod-topo/s1-runtime.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/basic.p4.p4info.txt",
|
||||
"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": "08:00:00:00:01:11",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:22",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:00",
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
57
exercises/basic/pod-topo/s2-runtime.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/basic.p4.p4info.txt",
|
||||
"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": "08:00:00:00:03:00",
|
||||
"port": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:03:33",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:44",
|
||||
"port": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
57
exercises/basic/pod-topo/s3-runtime.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/basic.p4.p4info.txt",
|
||||
"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": "08:00:00:00:01:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
57
exercises/basic/pod-topo/s4-runtime.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/basic.p4.p4info.txt",
|
||||
"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": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
26
exercises/basic/pod-topo/topology.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.20 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
|
||||
"commands":["route add default gw 10.0.3.30 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]},
|
||||
"h4": {"ip": "10.0.4.4/24", "mac": "08:00:00:00:04:44",
|
||||
"commands":["route add default gw 10.0.4.40 dev eth0",
|
||||
"arp -i eth0 -s 10.0.4.40 08:00:00:00:04:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"s1": { "runtime_json" : "pod-topo/s1-runtime.json" },
|
||||
"s2": { "runtime_json" : "pod-topo/s2-runtime.json" },
|
||||
"s3": { "runtime_json" : "pod-topo/s3-runtime.json" },
|
||||
"s4": { "runtime_json" : "pod-topo/s4-runtime.json" }
|
||||
},
|
||||
"links": [
|
||||
["h1", "s1-p1"], ["h2", "s1-p2"], ["s1-p3", "s3-p1"], ["s1-p4", "s4-p2"],
|
||||
["h3", "s2-p1"], ["h4", "s2-p2"], ["s2-p3", "s4-p1"], ["s2-p4", "s3-p2"]
|
||||
]
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"hosts": [
|
||||
"h1",
|
||||
"h2",
|
||||
"h3"
|
||||
],
|
||||
"switches": {
|
||||
"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"],
|
||||
["s3", "s2"], ["s2", "h2"], ["s3", "h3"]
|
||||
]
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:01:01",
|
||||
"dstAddr": "08:00:00:00:01:11",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -27,7 +27,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:02:02:00",
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -38,7 +38,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:03:03:00",
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 3
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:01:02:00",
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -27,7 +27,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:02:02",
|
||||
"dstAddr": "08:00:00:00:02:22",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -38,7 +38,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:03:03:00",
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 3
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:01:03:00",
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -27,7 +27,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:02:03:00",
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
@ -38,7 +38,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:03:03",
|
||||
"dstAddr": "08:00:00:00:03:33",
|
||||
"port": 1
|
||||
}
|
||||
}
|
22
exercises/basic/triangle-topo/topology.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.20 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
|
||||
"commands":["route add default gw 10.0.3.30 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"s1": { "runtime_json" : "triangle-topo/s1-runtime.json" },
|
||||
"s2": { "runtime_json" : "triangle-topo/s2-runtime.json" },
|
||||
"s3": { "runtime_json" : "triangle-topo/s3-runtime.json" }
|
||||
},
|
||||
"links": [
|
||||
["h1", "s1-p1"], ["s1-p2", "s2-p2"], ["s1-p3", "s3-p2"],
|
||||
["s3-p3", "s2-p3"], ["h2", "s2-p1"], ["h3", "s3-p1"]
|
||||
]
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
NO_P4 = true
|
||||
P4C_ARGS = --p4runtime-files $(basename $@).p4.p4info.txt
|
||||
|
||||
include ../../utils/Makefile
|
||||
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 67 B |
@ -10,7 +10,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:01:01",
|
||||
"dstAddr": "08:00:00:00:01:11",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:02:02:00",
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -32,7 +32,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:03:03:00",
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
|
@ -10,7 +10,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:01:02:00",
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:02:02",
|
||||
"dstAddr": "08:00:00:00:02:22",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -32,7 +32,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:03:02:00",
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
|
@ -10,7 +10,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:01:03:00",
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:02:03:00",
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
@ -32,7 +32,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:03:03",
|
||||
"dstAddr": "08:00:00:00:03:33",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
|
@ -1,16 +1,22 @@
|
||||
{
|
||||
"hosts": [
|
||||
"h1",
|
||||
"h2",
|
||||
"h3"
|
||||
],
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.20 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
|
||||
"commands":["route add default gw 10.0.3.30 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"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"],
|
||||
["s3", "s2"], ["s2", "h2"], ["s3", "h3"]
|
||||
["h1", "s1-p1"], ["s1-p2", "s2-p2"], ["s1-p3", "s3-p2"],
|
||||
["s3-p3", "s2-p3"], ["h2", "s2-p1"], ["h3", "s3-p1"]
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
NO_P4 = true
|
||||
P4C_ARGS = --p4runtime-files $(basename $@).p4.p4info.txt
|
||||
|
||||
include ../../utils/Makefile
|
||||
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 67 B |
@ -10,7 +10,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:01:01",
|
||||
"dstAddr": "08:00:00:00:01:01",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:01:0b",
|
||||
"dstAddr": "08:00:00:00:01:11",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -33,7 +33,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:02:03:00",
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
@ -44,7 +44,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:03:02:00",
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:02:02",
|
||||
"dstAddr": "08:00:00:00:02:02",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:02:16",
|
||||
"dstAddr": "08:00:00:00:02:22",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -32,7 +32,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:01:03:00",
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
@ -43,7 +43,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:03:03:00",
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:03:03",
|
||||
"dstAddr": "08:00:00:00:03:03",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:01:04:00",
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -32,7 +32,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:02:04:00",
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 3
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,28 @@
|
||||
{
|
||||
"hosts": [
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h11",
|
||||
"h22"
|
||||
],
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/31", "mac": "08:00:00:00:01:01",
|
||||
"commands":["route add default gw 10.0.1.0 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.0 08:00:00:00:01:00"]},
|
||||
"h11": {"ip": "10.0.1.11/31", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/31", "mac": "08:00:00:00:02:02",
|
||||
"commands":["route add default gw 10.0.2.3 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.3 08:00:00:00:02:00"]},
|
||||
"h22": {"ip": "10.0.2.22/31", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.23 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.23 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/31", "mac": "08:00:00:00:03:03",
|
||||
"commands":["route add default gw 10.0.3.2 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.2 08:00:00:00:03:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"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"],
|
||||
["s3", "s2"], ["s2", "h2"], ["s2", "h22"], ["s3", "h3"]
|
||||
["h1", "s1-p2"], ["h11", "s1-p1"], ["s1-p3", "s2-p3", "0", 0.5], ["s1-p4", "s3-p2"],
|
||||
["s3-p3", "s2-p4"], ["h2", "s2-p2"], ["h22", "s2-p1"], ["h3", "s3-p1"]
|
||||
]
|
||||
}
|
||||
|
5
exercises/firewall/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
TOPO = pod-topo/topology.json
|
||||
DEFAULT_PROG = basic.p4
|
||||
|
||||
include ../../utils/Makefile
|
After Width: | Height: | Size: 121 B |
215
exercises/firewall/README.md
Normal file
@ -0,0 +1,215 @@
|
||||
# Implementing A Basic Stateful Firewall
|
||||
|
||||
## Introduction
|
||||
|
||||
The objective of this exercise is to write a P4 program that
|
||||
implements a simple stateful firewall. To do this, we will use
|
||||
a bloom filter. This exercise builds upon the basic exercise
|
||||
so be sure to complete that one before trying this one.
|
||||
|
||||
We will use the pod-topology for this exercise, which consists of
|
||||
four hosts connected to four switches, which are wired up as they
|
||||
would be in a single pod of a fat tree topology.
|
||||
|
||||

|
||||
|
||||
Switch s1 will be configured with a P4 program that implements a
|
||||
simple stateful firewall (`firewall.p4`), the rest of the switches will run the
|
||||
basic IPv4 router program (`basic.p4`) from the previous exercise.
|
||||
|
||||
The firewall on s1 should have the following functionality:
|
||||
* Hosts h1 and h2 are on the internal network and can always
|
||||
connect to one another.
|
||||
* Hosts h1 and h2 can freely connect to h3 and h4 on the
|
||||
external network.
|
||||
* Hosts h3 and h4 can only reply to connections once they have been
|
||||
established from either h1 or h2, but cannot initiate new
|
||||
connections to hosts on the internal network.
|
||||
|
||||
**Note**: This stateful firewall is implemented 100% in the dataplane
|
||||
using a simple bloom filter. Thus there is some probability of
|
||||
hash collisions that would let unwanted flows to pass through.
|
||||
|
||||
Our P4 program will be written for the V1Model architecture implemented
|
||||
on P4.org's bmv2 software switch. The architecture file for the V1Model
|
||||
can be found at: /usr/local/share/p4c/p4include/v1model.p4. This file
|
||||
desribes the interfaces of the P4 programmable elements in the architecture,
|
||||
the supported externs, as well as the architecture's standard metadata
|
||||
fields. We encourage you to take a look at it.
|
||||
|
||||
> **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,
|
||||
`firewall.p4`. Your job will be to extend this skeleton program to
|
||||
properly implement the firewall.
|
||||
|
||||
Before that, let's compile the incomplete `firewall.p4` and bring
|
||||
up a switch in Mininet to test its behavior.
|
||||
|
||||
1. In your shell, run:
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
This will:
|
||||
* compile `firewall.p4`, and
|
||||
* start the pod-topo in Mininet and configure all switches with
|
||||
the appropriate P4 program + table entries, and
|
||||
* configure all hosts with the commands listed in
|
||||
[pod-topo/topology.json](./pod-topo/topology.json)
|
||||
|
||||
2. You should now see a Mininet command prompt. Try to run some iperf
|
||||
TCP flows between the hosts. TCP flows within the internal
|
||||
network should work:
|
||||
```bash
|
||||
mininet> iperf h1 h2
|
||||
```
|
||||
|
||||
TCP flows from hosts in the internal network to the outside hosts
|
||||
should also work:
|
||||
```bash
|
||||
mininet> iperf h1 h3
|
||||
```
|
||||
|
||||
TCP flows from the outside hosts to hosts inside the
|
||||
internal network should NOT work. However, since the firewall is not
|
||||
implemented yet, the following should work:
|
||||
```bash
|
||||
mininet> iperf h3 h1
|
||||
```
|
||||
|
||||
3. Type `exit` to leave the Mininet command line.
|
||||
Then, to stop mininet:
|
||||
```bash
|
||||
make stop
|
||||
```
|
||||
And to delete all pcaps, build files, and logs:
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
|
||||
### 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` command will install packet-processing rules in the tables of
|
||||
each switch. These are defined in the `sX-runtime.json` files, where
|
||||
`X` corresponds to the switch number.
|
||||
|
||||
**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/firewall.p4.p4info.txt` 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 Firewall
|
||||
|
||||
The `firewall.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.
|
||||
|
||||
**High-level Approach:** We will use a bloom filter with two hash functions
|
||||
to check if a packet coming into the internal network is a part of
|
||||
an already established TCP connection. We will use two different register
|
||||
arrays for the bloom filter, each to be updated by a hash function.
|
||||
Using different register arrays makes our design amenable to high-speed
|
||||
P4 targets that typically allow only one access to a register array per packet.
|
||||
|
||||
A complete `firewall.p4` will contain the following components:
|
||||
|
||||
1. Header type definitions for Ethernet (`ethernet_t`), IPv4 (`ipv4_t`) and TCP (`tcp_t`).
|
||||
2. Parsers for Ethernet, IPv4 and TCP that populate `ethernet_t`, `ipv4_t` and `tcp_t` fields.
|
||||
3. An action to drop a packet, using `mark_to_drop()`.
|
||||
4. An action (called `compute_hashes`) to compute the bloom filter's two hashes using hash
|
||||
algorithms `crc16` and `crc32`. The hashes will be computed on the packet 5-tuple consisting
|
||||
of IPv4 source and destination addresses, source and destination port numbers and
|
||||
the IPv4 protocol type.
|
||||
5. An action (`ipv4_forward`) and a table (`ipv4_lpm`) that will perform basic
|
||||
IPv4 forwarding (adopted from `basic.p4`).
|
||||
6. An action (called `set_direction`) that will simply set a one-bit direction variable
|
||||
as per the action's parameter.
|
||||
7. A table (called `check_ports`) that will read the ingress and egress port of a packet
|
||||
(after IPv4 forwarding) and invoke `set_direction`. The direction will be set to `1`,
|
||||
if the packet is incoming into the internal network. Otherwise, the direction will be set to `0`.
|
||||
To achieve this, the file `pod-topo/s1-runtime.json` contains the appropriate control plane
|
||||
entries for the `check_ports` table.
|
||||
8. A control that will:
|
||||
1. First apply the table `ipv4_lpm` if the packet has a valid IPv4 header.
|
||||
2. Then if the TCP header is valid, apply the `check_ports` table to determine the direction.
|
||||
3. Apply the `compute_hashes` action to compute the two hash values which are the bit positions
|
||||
in the two register arrays of the bloom filter (`reg_pos_one` and `reg_pos_two`).
|
||||
When the direction is `1` i.e. the packet is incoming into the internal network,
|
||||
`compute_hashes` will be invoked by swapping the source and destination IPv4 addresses
|
||||
and the source and destination ports. This is to check against bloom filter's set bits
|
||||
when the TCP connection was initially made from the internal network.
|
||||
4. **TODO:** If the TCP packet is going out of the internal network and is a SYN packet,
|
||||
set both the bloom filter arrays at the computed bit positions (`reg_pos_one` and `reg_pos_two`).
|
||||
Else, if the TCP packet is entering the internal network,
|
||||
read both the bloom filter arrays at the computed bit positions and drop the packet if
|
||||
either is not set.
|
||||
9. A deparser that emits the Ethernet, IPv4 and TCP headers in the right order.
|
||||
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, the `iperf` flow between
|
||||
h3 and h1 should be blocked by the firewall.
|
||||
|
||||
### Food for thought
|
||||
|
||||
You may have noticed that in this simple stateful firewall, we are adding
|
||||
new TCP connections to the bloom filter (based on outgoing SYN packets).
|
||||
However, we are not removing them in case of TCP connection teardown
|
||||
(FIN packets). How would you implement the removal of TCP connections that are
|
||||
no longer active?
|
||||
|
||||
Things to consider:
|
||||
- Can we simply set the bloom filter array bits to `0` on
|
||||
receiving a FIN packet? What happens when there is one hash collision in
|
||||
the bloom filter arrays between two _active_ TCP connections?
|
||||
- How can we modify our bloom filter structure so that the deletion
|
||||
operation can be properly supported?
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
There are several problems that might manifest as you develop your program:
|
||||
|
||||
1. `firewall.p4` might fail to compile. In this case, `make run` will
|
||||
report the error emitted from the compiler and halt.
|
||||
|
||||
2. `firewall.p4` might compile but fail to support the control plane
|
||||
rules in the `s1-runtime.json` file 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
|
||||
`firewall.p4` implementation.
|
||||
|
||||
3. `firewall.p4` might compile, and the control plane rules might be
|
||||
installed, but the switch might not process packets in the desired
|
||||
way. The `logs/sX.log` files contain detailed logs that describe
|
||||
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
|
||||
```
|
||||
|
176
exercises/firewall/basic.p4
Normal file
@ -0,0 +1,176 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
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(standard_metadata);
|
||||
}
|
||||
|
||||
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 = drop();
|
||||
}
|
||||
|
||||
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;
|
BIN
exercises/firewall/firewall-topo.png
Normal file
After Width: | Height: | Size: 171 KiB |
276
exercises/firewall/firewall.p4
Normal file
@ -0,0 +1,276 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
/* CONSTANTS */
|
||||
|
||||
const bit<16> TYPE_IPV4 = 0x800;
|
||||
const bit<8> TYPE_TCP = 6;
|
||||
|
||||
#define BLOOM_FILTER_ENTRIES 4096
|
||||
#define BLOOM_FILTER_BIT_WIDTH 1
|
||||
|
||||
/*************************************************************************
|
||||
*********************** 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;
|
||||
}
|
||||
|
||||
header tcp_t{
|
||||
bit<16> srcPort;
|
||||
bit<16> dstPort;
|
||||
bit<32> seqNo;
|
||||
bit<32> ackNo;
|
||||
bit<4> dataOffset;
|
||||
bit<4> res;
|
||||
bit<1> cwr;
|
||||
bit<1> ece;
|
||||
bit<1> urg;
|
||||
bit<1> ack;
|
||||
bit<1> psh;
|
||||
bit<1> rst;
|
||||
bit<1> syn;
|
||||
bit<1> fin;
|
||||
bit<16> window;
|
||||
bit<16> checksum;
|
||||
bit<16> urgentPtr;
|
||||
}
|
||||
|
||||
struct metadata {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
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) {
|
||||
TYPE_IPV4: parse_ipv4;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4 {
|
||||
packet.extract(hdr.ipv4);
|
||||
transition select(hdr.ipv4.protocol){
|
||||
TYPE_TCP: tcp;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state 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) {
|
||||
|
||||
register<bit<BLOOM_FILTER_BIT_WIDTH>>(BLOOM_FILTER_ENTRIES) bloom_filter_1;
|
||||
register<bit<BLOOM_FILTER_BIT_WIDTH>>(BLOOM_FILTER_ENTRIES) bloom_filter_2;
|
||||
bit<32> reg_pos_one; bit<32> reg_pos_two;
|
||||
bit<1> reg_val_one; bit<1> reg_val_two;
|
||||
bit<1> direction;
|
||||
|
||||
action drop() {
|
||||
mark_to_drop(standard_metadata);
|
||||
}
|
||||
|
||||
action compute_hashes(ip4Addr_t ipAddr1, ip4Addr_t ipAddr2, bit<16> port1, bit<16> port2){
|
||||
//Get register position
|
||||
hash(reg_pos_one, HashAlgorithm.crc16, (bit<32>)0, {ipAddr1,
|
||||
ipAddr2,
|
||||
port1,
|
||||
port2,
|
||||
hdr.ipv4.protocol},
|
||||
(bit<32>)BLOOM_FILTER_ENTRIES);
|
||||
|
||||
hash(reg_pos_two, HashAlgorithm.crc32, (bit<32>)0, {ipAddr1,
|
||||
ipAddr2,
|
||||
port1,
|
||||
port2,
|
||||
hdr.ipv4.protocol},
|
||||
(bit<32>)BLOOM_FILTER_ENTRIES);
|
||||
}
|
||||
|
||||
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 = drop();
|
||||
}
|
||||
|
||||
action set_direction(bit<1> dir) {
|
||||
direction = dir;
|
||||
}
|
||||
|
||||
table check_ports {
|
||||
key = {
|
||||
standard_metadata.ingress_port: exact;
|
||||
standard_metadata.egress_spec: exact;
|
||||
}
|
||||
actions = {
|
||||
set_direction;
|
||||
NoAction;
|
||||
}
|
||||
size = 1024;
|
||||
default_action = NoAction();
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.ipv4.isValid()){
|
||||
ipv4_lpm.apply();
|
||||
if (hdr.tcp.isValid()){
|
||||
direction = 0; // default
|
||||
if (check_ports.apply().hit) {
|
||||
// test and set the bloom filter
|
||||
if (direction == 0) {
|
||||
compute_hashes(hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.tcp.srcPort, hdr.tcp.dstPort);
|
||||
}
|
||||
else {
|
||||
compute_hashes(hdr.ipv4.dstAddr, hdr.ipv4.srcAddr, hdr.tcp.dstPort, hdr.tcp.srcPort);
|
||||
}
|
||||
// Packet comes from internal network
|
||||
if (direction == 0){
|
||||
// TODO: this packet is part of an outgoing TCP connection.
|
||||
// We need to set the bloom filter if this is a SYN packet
|
||||
// E.g. bloom_filter_1.write(<index>, <value>);
|
||||
}
|
||||
// Packet comes from outside
|
||||
else if (direction == 1){
|
||||
// TODO: this packet is part of an incomming TCP connection.
|
||||
// We need to check if this packet is allowed to pass by reading the bloom filter
|
||||
// E.g. bloom_filter_1.read(<value>, <index>);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** 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);
|
||||
packet.emit(hdr.tcp);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
145
exercises/firewall/pod-topo/s1-runtime.json
Normal file
@ -0,0 +1,145 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/firewall.p4.p4info.txt",
|
||||
"bmv2_json": "build/firewall.json",
|
||||
"table_entries": [
|
||||
{
|
||||
"table": "MyIngress.check_ports",
|
||||
"match": {
|
||||
"standard_metadata.ingress_port": 1,
|
||||
"standard_metadata.egress_spec": 3
|
||||
},
|
||||
"action_name": "MyIngress.set_direction",
|
||||
"action_params": {
|
||||
"dir": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.check_ports",
|
||||
"match": {
|
||||
"standard_metadata.ingress_port": 1,
|
||||
"standard_metadata.egress_spec": 4
|
||||
},
|
||||
"action_name": "MyIngress.set_direction",
|
||||
"action_params": {
|
||||
"dir": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.check_ports",
|
||||
"match": {
|
||||
"standard_metadata.ingress_port": 2,
|
||||
"standard_metadata.egress_spec": 3
|
||||
},
|
||||
"action_name": "MyIngress.set_direction",
|
||||
"action_params": {
|
||||
"dir": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.check_ports",
|
||||
"match": {
|
||||
"standard_metadata.ingress_port": 2,
|
||||
"standard_metadata.egress_spec": 4
|
||||
},
|
||||
"action_name": "MyIngress.set_direction",
|
||||
"action_params": {
|
||||
"dir": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.check_ports",
|
||||
"match": {
|
||||
"standard_metadata.ingress_port": 3,
|
||||
"standard_metadata.egress_spec": 1
|
||||
},
|
||||
"action_name": "MyIngress.set_direction",
|
||||
"action_params": {
|
||||
"dir": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.check_ports",
|
||||
"match": {
|
||||
"standard_metadata.ingress_port": 3,
|
||||
"standard_metadata.egress_spec": 2
|
||||
},
|
||||
"action_name": "MyIngress.set_direction",
|
||||
"action_params": {
|
||||
"dir": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.check_ports",
|
||||
"match": {
|
||||
"standard_metadata.ingress_port": 4,
|
||||
"standard_metadata.egress_spec": 1
|
||||
},
|
||||
"action_name": "MyIngress.set_direction",
|
||||
"action_params": {
|
||||
"dir": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.check_ports",
|
||||
"match": {
|
||||
"standard_metadata.ingress_port": 4,
|
||||
"standard_metadata.egress_spec": 2
|
||||
},
|
||||
"action_name": "MyIngress.set_direction",
|
||||
"action_params": {
|
||||
"dir": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "08:00:00:00:01:11",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:22",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:00",
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
57
exercises/firewall/pod-topo/s2-runtime.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/basic.p4.p4info.txt",
|
||||
"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": "08:00:00:00:03:00",
|
||||
"port": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:03:33",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:44",
|
||||
"port": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
57
exercises/firewall/pod-topo/s3-runtime.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/basic.p4.p4info.txt",
|
||||
"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": "08:00:00:00:01:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
57
exercises/firewall/pod-topo/s4-runtime.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/basic.p4.p4info.txt",
|
||||
"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": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
27
exercises/firewall/pod-topo/topology.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.20 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
|
||||
"commands":["route add default gw 10.0.3.30 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]},
|
||||
"h4": {"ip": "10.0.4.4/24", "mac": "08:00:00:00:04:44",
|
||||
"commands":["route add default gw 10.0.4.40 dev eth0",
|
||||
"arp -i eth0 -s 10.0.4.40 08:00:00:00:04:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"s1": { "runtime_json" : "pod-topo/s1-runtime.json",
|
||||
"program" : "build/firewall.json" },
|
||||
"s2": { "runtime_json" : "pod-topo/s2-runtime.json" },
|
||||
"s3": { "runtime_json" : "pod-topo/s3-runtime.json" },
|
||||
"s4": { "runtime_json" : "pod-topo/s4-runtime.json" }
|
||||
},
|
||||
"links": [
|
||||
["h1", "s1-p1"], ["h2", "s1-p2"], ["s1-p3", "s3-p1"], ["s1-p4", "s4-p2"],
|
||||
["h3", "s2-p1"], ["h4", "s2-p2"], ["s2-p3", "s4-p1"], ["s2-p4", "s3-p2"]
|
||||
]
|
||||
}
|
282
exercises/firewall/solution/firewall.p4
Normal file
@ -0,0 +1,282 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
/* CONSTANTS */
|
||||
|
||||
const bit<16> TYPE_IPV4 = 0x800;
|
||||
const bit<8> TYPE_TCP = 6;
|
||||
|
||||
#define BLOOM_FILTER_ENTRIES 4096
|
||||
#define BLOOM_FILTER_BIT_WIDTH 1
|
||||
|
||||
/*************************************************************************
|
||||
*********************** 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;
|
||||
}
|
||||
|
||||
header tcp_t{
|
||||
bit<16> srcPort;
|
||||
bit<16> dstPort;
|
||||
bit<32> seqNo;
|
||||
bit<32> ackNo;
|
||||
bit<4> dataOffset;
|
||||
bit<4> res;
|
||||
bit<1> cwr;
|
||||
bit<1> ece;
|
||||
bit<1> urg;
|
||||
bit<1> ack;
|
||||
bit<1> psh;
|
||||
bit<1> rst;
|
||||
bit<1> syn;
|
||||
bit<1> fin;
|
||||
bit<16> window;
|
||||
bit<16> checksum;
|
||||
bit<16> urgentPtr;
|
||||
}
|
||||
|
||||
struct metadata {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
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) {
|
||||
TYPE_IPV4: parse_ipv4;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4 {
|
||||
packet.extract(hdr.ipv4);
|
||||
transition select(hdr.ipv4.protocol){
|
||||
TYPE_TCP: tcp;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state 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) {
|
||||
|
||||
register<bit<BLOOM_FILTER_BIT_WIDTH>>(BLOOM_FILTER_ENTRIES) bloom_filter_1;
|
||||
register<bit<BLOOM_FILTER_BIT_WIDTH>>(BLOOM_FILTER_ENTRIES) bloom_filter_2;
|
||||
bit<32> reg_pos_one; bit<32> reg_pos_two;
|
||||
bit<1> reg_val_one; bit<1> reg_val_two;
|
||||
bit<1> direction;
|
||||
|
||||
action drop() {
|
||||
mark_to_drop(standard_metadata);
|
||||
}
|
||||
|
||||
action compute_hashes(ip4Addr_t ipAddr1, ip4Addr_t ipAddr2, bit<16> port1, bit<16> port2){
|
||||
//Get register position
|
||||
hash(reg_pos_one, HashAlgorithm.crc16, (bit<32>)0, {ipAddr1,
|
||||
ipAddr2,
|
||||
port1,
|
||||
port2,
|
||||
hdr.ipv4.protocol},
|
||||
(bit<32>)BLOOM_FILTER_ENTRIES);
|
||||
|
||||
hash(reg_pos_two, HashAlgorithm.crc32, (bit<32>)0, {ipAddr1,
|
||||
ipAddr2,
|
||||
port1,
|
||||
port2,
|
||||
hdr.ipv4.protocol},
|
||||
(bit<32>)BLOOM_FILTER_ENTRIES);
|
||||
}
|
||||
|
||||
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 = drop();
|
||||
}
|
||||
|
||||
action set_direction(bit<1> dir) {
|
||||
direction = dir;
|
||||
}
|
||||
|
||||
table check_ports {
|
||||
key = {
|
||||
standard_metadata.ingress_port: exact;
|
||||
standard_metadata.egress_spec: exact;
|
||||
}
|
||||
actions = {
|
||||
set_direction;
|
||||
NoAction;
|
||||
}
|
||||
size = 1024;
|
||||
default_action = NoAction();
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.ipv4.isValid()){
|
||||
ipv4_lpm.apply();
|
||||
if (hdr.tcp.isValid()){
|
||||
direction = 0; // default
|
||||
if (check_ports.apply().hit) {
|
||||
// test and set the bloom filter
|
||||
if (direction == 0) {
|
||||
compute_hashes(hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.tcp.srcPort, hdr.tcp.dstPort);
|
||||
}
|
||||
else {
|
||||
compute_hashes(hdr.ipv4.dstAddr, hdr.ipv4.srcAddr, hdr.tcp.dstPort, hdr.tcp.srcPort);
|
||||
}
|
||||
// Packet comes from internal network
|
||||
if (direction == 0){
|
||||
// If there is a syn we update the bloom filter and add the entry
|
||||
if (hdr.tcp.syn == 1){
|
||||
bloom_filter_1.write(reg_pos_one, 1);
|
||||
bloom_filter_2.write(reg_pos_two, 1);
|
||||
}
|
||||
}
|
||||
// Packet comes from outside
|
||||
else if (direction == 1){
|
||||
// Read bloom filter cells to check if there are 1's
|
||||
bloom_filter_1.read(reg_val_one, reg_pos_one);
|
||||
bloom_filter_2.read(reg_val_two, reg_pos_two);
|
||||
// only allow flow to pass if both entries are set
|
||||
if (reg_val_one != 1 || reg_val_two != 1){
|
||||
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 {
|
||||
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;
|
4
exercises/link_monitor/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
TOPO = pod-topo/topology.json
|
||||
|
||||
include ../../utils/Makefile
|
After Width: | Height: | Size: 97 B |
227
exercises/link_monitor/README.md
Normal file
@ -0,0 +1,227 @@
|
||||
# Implementing Link Monitoring
|
||||
|
||||
## Introduction
|
||||
|
||||
The objective of this exercise is to write a P4 program that enables
|
||||
a host to monitor the utilization of all links in the network. This
|
||||
exercise builds upon the basic IPv4 forwarding exercise so be sure
|
||||
to complete that one before attempting this one. Specifically, we
|
||||
will modify the basic P4 program to process a source routed probe
|
||||
packet such that it is able to pick up the egress link utilization
|
||||
at each hop and deliver it to a host for monitoring purposes.
|
||||
|
||||
Our probe packet will contain the following three header types:
|
||||
```
|
||||
// Top-level probe header, indicates how many hops this probe
|
||||
// packet has traversed so far.
|
||||
header probe_t {
|
||||
bit<8> hop_cnt;
|
||||
}
|
||||
|
||||
// The data added to the probe by each switch at each hop.
|
||||
header probe_data_t {
|
||||
bit<1> bos;
|
||||
bit<7> swid;
|
||||
bit<8> port;
|
||||
bit<32> byte_cnt;
|
||||
time_t last_time;
|
||||
time_t cur_time;
|
||||
}
|
||||
|
||||
// Indicates the egress port the switch should send this probe
|
||||
// packet out of. There is one of these headers for each hop.
|
||||
header probe_fwd_t {
|
||||
bit<8> egress_spec;
|
||||
}
|
||||
```
|
||||
|
||||
We will use the pod-topology for this exercise, which consists of
|
||||
four hosts connected to four switches that are wired up as they
|
||||
would be in a single pod of a fat tree topology.
|
||||
|
||||

|
||||
|
||||
In order to monitor the link utilization our switch will maintain
|
||||
two register arrays:
|
||||
* `byte_cnt_reg` - counts the number of bytes transmitted out of
|
||||
each port since the last probe packet was transmitted out of
|
||||
the port.
|
||||
* `last_time_reg` - stores the last time that a probe packet was
|
||||
transmitted out of each port.
|
||||
|
||||
Our P4 program will be written for the V1Model architecture implemented
|
||||
on P4.org's bmv2 software switch. The architecture file for the V1Model
|
||||
can be found at: /usr/local/share/p4c/p4include/v1model.p4. This file
|
||||
desribes the interfaces of the P4 programmable elements in the architecture,
|
||||
the supported externs, as well as the architecture's standard metadata
|
||||
fields. We encourage you to take a look at it.
|
||||
|
||||
> **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 contains a skeleton P4 program,
|
||||
`link_monitor.p4`, which implements basic IPv4 forwarding, as well
|
||||
as source routing of the probe packets. Your job will be to
|
||||
extend this skeleton program to fill out the fields in the probe
|
||||
packet.
|
||||
|
||||
Before that, let's compile and test the incomplete `link_monitor.p4`
|
||||
program:
|
||||
|
||||
1. In your shell, run:
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
This will:
|
||||
* compile `link_monitor.p4`, and
|
||||
* start the pod-topo in Mininet and configure all switches with
|
||||
the `link_monitor.p4` program + table entries, and
|
||||
* configure all hosts with the commands listed in
|
||||
[pod-topo/topology.json](./pod-topo/topology.json)
|
||||
|
||||
2. You should now see a Mininet command prompt. Open two terminals
|
||||
on `h1`:
|
||||
```bash
|
||||
mininet> xterm h1 h1
|
||||
```
|
||||
3. In one of the xterms run the `send.py` script to start sending
|
||||
probe packets every second. Each of these probe packets takes the
|
||||
path indicated in link-monitor-topo.png.
|
||||
```bash
|
||||
./send.py
|
||||
```
|
||||
4. In the other terminal run the `receive.py` script to start
|
||||
receiving and parsing the probe packets. This allows us to monitor
|
||||
the link utilization within the network.
|
||||
```bash
|
||||
./receive.py
|
||||
```
|
||||
The reported link utilization and the switch port numbers will
|
||||
always be 0 because the probe fields have not been filled out yet.
|
||||
|
||||
5. Run an iperf flow between h1 and h4:
|
||||
```bash
|
||||
mininet> iperf h1 h4
|
||||
```
|
||||
6. 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 measured link utilizations will not agree with what iperf reports
|
||||
because the probe packet fields have not been populated yet. Your
|
||||
goal is to fill out the probe packet fields so that the two
|
||||
measurements agree.
|
||||
|
||||
### 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 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-runtime.json` files, where
|
||||
`X` corresponds to the switch number.
|
||||
|
||||
**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/link_monitor.p4.p4info.txt` 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 Link Monitoring Logic
|
||||
|
||||
The `link_monitor.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.
|
||||
|
||||
Here are a few more details about the design:
|
||||
|
||||
**Parser**
|
||||
* The parser has been extended support parsing of the source routed probe packets.
|
||||
The parser is the most complicated part of the design so spend a bit of time
|
||||
reading over it. Note that it does not contain any TODO comments so there is
|
||||
nothing you need to change here.
|
||||
* To parse the probe packets, we use the `hdr.probe.hop_cnt` to determine how many
|
||||
hops the packet has traversed prior to reaching the switch. If this is the first
|
||||
hop then there will not be any `probe_data` in the packet so we skip that state
|
||||
and transition directly to the `parse_probe_fwd` state. In the `parse_probe_fwd`
|
||||
state, we use the `hdr.probe.hop_cnt` field to figure out which `egress_spec`
|
||||
header field to use to perform forwarding and we save that port value into a
|
||||
metadata field which is subsequently used to perform forwarding.
|
||||
|
||||
**Ingress Control**
|
||||
* The ingress control block looks very similar to the `basic` exercise. The only
|
||||
difference is that the `apply` block contains another condition to forward probe
|
||||
packets using the `egress_spec` field extracted by the parser. It also increments
|
||||
the `hdr.probe.hop_cnt` field.
|
||||
|
||||
**Egress Control**
|
||||
* This is where the interesting stateful processing occurs. It uses the
|
||||
`byte_cnt_reg` register to count the number of bytes that have passed through each
|
||||
port since the last probe packet passed through the port.
|
||||
* It adds a new `probe_data` header to the packet and filld out the `bos`
|
||||
(bottom of stack) field, as well as the `swid` (switch ID) field.
|
||||
* TODO: your job is to fill out the rest of the probe packet fields in order to
|
||||
ensure that you can properly measure link utilization.
|
||||
|
||||
**Deparser**
|
||||
* Simply emits all headers in the correct order.
|
||||
* Note that emitting a header stack will only emit the headers within the stack
|
||||
that are actually marked as valid.
|
||||
|
||||
## Step 3: Run your solution
|
||||
|
||||
Follow the instructions from Step 1. This time, the measured link
|
||||
utilizations should agree with what `iperf` reports.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
There are several problems that might manifest as you develop your program:
|
||||
|
||||
1. `link_monitor.p4` might fail to compile. In this case, `make run` will
|
||||
report the error emitted from the compiler and halt.
|
||||
|
||||
2. `link_monitor.p4` might compile but fail to support the control plane
|
||||
rules in the `s1-runtime.json` through `s4-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 `link_monitor.p4` implementation.
|
||||
|
||||
3. `link_monitor.p4` might compile, and the control plane rules might be
|
||||
installed, but the switch might not process packets in the desired
|
||||
way. The `logs/sX.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
|
||||
```
|
||||
|
||||
### Food For Thought
|
||||
|
||||
Now that you've implemented this basic monitoring framework can you
|
||||
think of ways to leverage this information about link utilization
|
||||
within the core of the network? For instance, how might you use this
|
||||
data, either at the hosts or at the switches, to make real-time
|
||||
load-balancing decisions?
|
||||
|
BIN
exercises/link_monitor/link-monitor-topo.png
Normal file
After Width: | Height: | Size: 251 KiB |
299
exercises/link_monitor/link_monitor.p4
Normal file
@ -0,0 +1,299 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
const bit<16> TYPE_IPV4 = 0x800;
|
||||
const bit<16> TYPE_PROBE = 0x812;
|
||||
|
||||
#define MAX_HOPS 10
|
||||
#define MAX_PORTS 8
|
||||
|
||||
/*************************************************************************
|
||||
*********************** H E A D E R S ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
typedef bit<9> egressSpec_t;
|
||||
typedef bit<48> macAddr_t;
|
||||
typedef bit<32> ip4Addr_t;
|
||||
|
||||
typedef bit<48> time_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;
|
||||
}
|
||||
|
||||
// Top-level probe header, indicates how many hops this probe
|
||||
// packet has traversed so far.
|
||||
header probe_t {
|
||||
bit<8> hop_cnt;
|
||||
}
|
||||
|
||||
// The data added to the probe by each switch at each hop.
|
||||
header probe_data_t {
|
||||
bit<1> bos;
|
||||
bit<7> swid;
|
||||
bit<8> port;
|
||||
bit<32> byte_cnt;
|
||||
time_t last_time;
|
||||
time_t cur_time;
|
||||
}
|
||||
|
||||
// Indicates the egress port the switch should send this probe
|
||||
// packet out of. There is one of these headers for each hop.
|
||||
header probe_fwd_t {
|
||||
bit<8> egress_spec;
|
||||
}
|
||||
|
||||
struct parser_metadata_t {
|
||||
bit<8> remaining;
|
||||
}
|
||||
|
||||
struct metadata {
|
||||
bit<8> egress_spec;
|
||||
parser_metadata_t parser_metadata;
|
||||
}
|
||||
|
||||
struct headers {
|
||||
ethernet_t ethernet;
|
||||
ipv4_t ipv4;
|
||||
probe_t probe;
|
||||
probe_data_t[MAX_HOPS] probe_data;
|
||||
probe_fwd_t[MAX_HOPS] probe_fwd;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** 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;
|
||||
TYPE_PROBE: parse_probe;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4 {
|
||||
packet.extract(hdr.ipv4);
|
||||
transition accept;
|
||||
}
|
||||
|
||||
state parse_probe {
|
||||
packet.extract(hdr.probe);
|
||||
meta.parser_metadata.remaining = hdr.probe.hop_cnt + 1;
|
||||
transition select(hdr.probe.hop_cnt) {
|
||||
0: parse_probe_fwd;
|
||||
default: parse_probe_data;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_probe_data {
|
||||
packet.extract(hdr.probe_data.next);
|
||||
transition select(hdr.probe_data.last.bos) {
|
||||
1: parse_probe_fwd;
|
||||
default: parse_probe_data;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_probe_fwd {
|
||||
packet.extract(hdr.probe_fwd.next);
|
||||
meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1;
|
||||
// extract the forwarding data
|
||||
meta.egress_spec = hdr.probe_fwd.last.egress_spec;
|
||||
transition select(meta.parser_metadata.remaining) {
|
||||
0: accept;
|
||||
default: parse_probe_fwd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************ 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(standard_metadata);
|
||||
}
|
||||
|
||||
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 = drop();
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.ipv4.isValid()) {
|
||||
ipv4_lpm.apply();
|
||||
}
|
||||
else if (hdr.probe.isValid()) {
|
||||
standard_metadata.egress_spec = (bit<9>)meta.egress_spec;
|
||||
hdr.probe.hop_cnt = hdr.probe.hop_cnt + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** 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) {
|
||||
|
||||
// count the number of bytes seen since the last probe
|
||||
register<bit<32>>(MAX_PORTS) byte_cnt_reg;
|
||||
// remember the time of the last probe
|
||||
register<time_t>(MAX_PORTS) last_time_reg;
|
||||
|
||||
action set_swid(bit<7> swid) {
|
||||
hdr.probe_data[0].swid = swid;
|
||||
}
|
||||
|
||||
table swid {
|
||||
actions = {
|
||||
set_swid;
|
||||
NoAction;
|
||||
}
|
||||
default_action = NoAction();
|
||||
}
|
||||
|
||||
apply {
|
||||
bit<32> byte_cnt;
|
||||
bit<32> new_byte_cnt;
|
||||
time_t last_time;
|
||||
time_t cur_time = standard_metadata.egress_global_timestamp;
|
||||
// increment byte cnt for this packet's port
|
||||
byte_cnt_reg.read(byte_cnt, (bit<32>)standard_metadata.egress_port);
|
||||
byte_cnt = byte_cnt + standard_metadata.packet_length;
|
||||
// reset the byte count when a probe packet passes through
|
||||
new_byte_cnt = (hdr.probe.isValid()) ? 0 : byte_cnt;
|
||||
byte_cnt_reg.write((bit<32>)standard_metadata.egress_port, new_byte_cnt);
|
||||
|
||||
if (hdr.probe.isValid()) {
|
||||
// fill out probe fields
|
||||
hdr.probe_data.push_front(1);
|
||||
hdr.probe_data[0].setValid();
|
||||
if (hdr.probe.hop_cnt == 1) {
|
||||
hdr.probe_data[0].bos = 1;
|
||||
}
|
||||
else {
|
||||
hdr.probe_data[0].bos = 0;
|
||||
}
|
||||
// set switch ID field
|
||||
swid.apply();
|
||||
// TODO: fill out the rest of the probe packet fields
|
||||
// hdr.probe_data[0].port = ...
|
||||
// hdr.probe_data[0].byte_cnt = ...
|
||||
// TODO: read / update the last_time_reg
|
||||
// last_time_reg.read(<val>, <index>);
|
||||
// last_time_reg.write(<index>, <val>);
|
||||
// hdr.probe_data[0].last_time = ...
|
||||
// hdr.probe_data[0].cur_time = ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************* 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.probe);
|
||||
packet.emit(hdr.probe_data);
|
||||
packet.emit(hdr.probe_fwd);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
65
exercises/link_monitor/pod-topo/s1-runtime.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/link_monitor.p4.p4info.txt",
|
||||
"bmv2_json": "build/link_monitor.json",
|
||||
"table_entries": [
|
||||
{
|
||||
"table": "MyEgress.swid",
|
||||
"default_action": true,
|
||||
"action_name": "MyEgress.set_swid",
|
||||
"action_params": {
|
||||
"swid": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "08:00:00:00:01:11",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:22",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:00",
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
65
exercises/link_monitor/pod-topo/s2-runtime.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/link_monitor.p4.p4info.txt",
|
||||
"bmv2_json": "build/link_monitor.json",
|
||||
"table_entries": [
|
||||
{
|
||||
"table": "MyEgress.swid",
|
||||
"default_action": true,
|
||||
"action_name": "MyEgress.set_swid",
|
||||
"action_params": {
|
||||
"swid": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "08:00:00:00:03:00",
|
||||
"port": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:03:33",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:04:44",
|
||||
"port": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
65
exercises/link_monitor/pod-topo/s3-runtime.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/link_monitor.p4.p4info.txt",
|
||||
"bmv2_json": "build/link_monitor.json",
|
||||
"table_entries": [
|
||||
{
|
||||
"table": "MyEgress.swid",
|
||||
"default_action": true,
|
||||
"action_name": "MyEgress.set_swid",
|
||||
"action_params": {
|
||||
"swid": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "08:00:00:00:01:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
65
exercises/link_monitor/pod-topo/s4-runtime.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/link_monitor.p4.p4info.txt",
|
||||
"bmv2_json": "build/link_monitor.json",
|
||||
"table_entries": [
|
||||
{
|
||||
"table": "MyEgress.swid",
|
||||
"default_action": true,
|
||||
"action_name": "MyEgress.set_swid",
|
||||
"action_params": {
|
||||
"swid": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.2.2", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.3.3", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.ipv4_lpm",
|
||||
"match": {
|
||||
"hdr.ipv4.dstAddr": ["10.0.4.4", 32]
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
26
exercises/link_monitor/pod-topo/topology.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.20 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
|
||||
"commands":["route add default gw 10.0.3.30 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]},
|
||||
"h4": {"ip": "10.0.4.4/24", "mac": "08:00:00:00:04:44",
|
||||
"commands":["route add default gw 10.0.4.40 dev eth0",
|
||||
"arp -i eth0 -s 10.0.4.40 08:00:00:00:04:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"s1": { "runtime_json" : "pod-topo/s1-runtime.json" },
|
||||
"s2": { "runtime_json" : "pod-topo/s2-runtime.json" },
|
||||
"s3": { "runtime_json" : "pod-topo/s3-runtime.json" },
|
||||
"s4": { "runtime_json" : "pod-topo/s4-runtime.json" }
|
||||
},
|
||||
"links": [
|
||||
["h1", "s1-p1"], ["h2", "s1-p2"], ["s1-p3", "s3-p1"], ["s1-p4", "s4-p2"],
|
||||
["h3", "s2-p1"], ["h4", "s2-p2"], ["s2-p3", "s4-p1"], ["s2-p4", "s3-p2"]
|
||||
]
|
||||
}
|
25
exercises/link_monitor/probe_hdrs.py
Executable file
@ -0,0 +1,25 @@
|
||||
from scapy.all import *
|
||||
|
||||
TYPE_PROBE = 0x812
|
||||
|
||||
class Probe(Packet):
|
||||
fields_desc = [ ByteField("hop_cnt", 0)]
|
||||
|
||||
class ProbeData(Packet):
|
||||
fields_desc = [ BitField("bos", 0, 1),
|
||||
BitField("swid", 0, 7),
|
||||
ByteField("port", 0),
|
||||
IntField("byte_cnt", 0),
|
||||
BitField("last_time", 0, 48),
|
||||
BitField("cur_time", 0, 48)]
|
||||
|
||||
class ProbeFwd(Packet):
|
||||
fields_desc = [ ByteField("egress_spec", 0)]
|
||||
|
||||
bind_layers(Ether, Probe, type=TYPE_PROBE)
|
||||
bind_layers(Probe, ProbeFwd, hop_cnt=0)
|
||||
bind_layers(Probe, ProbeData)
|
||||
bind_layers(ProbeData, ProbeData, bos=0)
|
||||
bind_layers(ProbeData, ProbeFwd, bos=1)
|
||||
bind_layers(ProbeFwd, ProbeFwd)
|
||||
|
26
exercises/link_monitor/receive.py
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from probe_hdrs import *
|
||||
|
||||
def expand(x):
|
||||
yield x
|
||||
while x.payload:
|
||||
x = x.payload
|
||||
yield x
|
||||
|
||||
def handle_pkt(pkt):
|
||||
if ProbeData in pkt:
|
||||
data_layers = [l for l in expand(pkt) if l.name=='ProbeData']
|
||||
print ""
|
||||
for sw in data_layers:
|
||||
utilization = 0 if sw.cur_time == sw.last_time else 8.0*sw.byte_cnt/(sw.cur_time - sw.last_time)
|
||||
print "Switch {} - Port {}: {} Mbps".format(sw.swid, sw.port, utilization)
|
||||
|
||||
def main():
|
||||
iface = 'eth0'
|
||||
print "sniffing on {}".format(iface)
|
||||
sniff(iface = iface,
|
||||
prn = lambda x: handle_pkt(x))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
28
exercises/link_monitor/send.py
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import time
|
||||
from probe_hdrs import *
|
||||
|
||||
def main():
|
||||
|
||||
probe_pkt = Ether(dst='ff:ff:ff:ff:ff:ff', src=get_if_hwaddr('eth0')) / \
|
||||
Probe(hop_cnt=0) / \
|
||||
ProbeFwd(egress_spec=4) / \
|
||||
ProbeFwd(egress_spec=1) / \
|
||||
ProbeFwd(egress_spec=4) / \
|
||||
ProbeFwd(egress_spec=1) / \
|
||||
ProbeFwd(egress_spec=3) / \
|
||||
ProbeFwd(egress_spec=2) / \
|
||||
ProbeFwd(egress_spec=3) / \
|
||||
ProbeFwd(egress_spec=2) / \
|
||||
ProbeFwd(egress_spec=1)
|
||||
|
||||
while True:
|
||||
try:
|
||||
sendp(probe_pkt, iface='eth0')
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
298
exercises/link_monitor/solution/link_monitor.p4
Normal file
@ -0,0 +1,298 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
const bit<16> TYPE_IPV4 = 0x800;
|
||||
const bit<16> TYPE_PROBE = 0x812;
|
||||
|
||||
#define MAX_HOPS 10
|
||||
#define MAX_PORTS 8
|
||||
|
||||
/*************************************************************************
|
||||
*********************** H E A D E R S ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
typedef bit<9> egressSpec_t;
|
||||
typedef bit<48> macAddr_t;
|
||||
typedef bit<32> ip4Addr_t;
|
||||
|
||||
typedef bit<48> time_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;
|
||||
}
|
||||
|
||||
// Top-level probe header, indicates how many hops this probe
|
||||
// packet has traversed so far.
|
||||
header probe_t {
|
||||
bit<8> hop_cnt;
|
||||
}
|
||||
|
||||
// The data added to the probe by each switch at each hop.
|
||||
header probe_data_t {
|
||||
bit<1> bos;
|
||||
bit<7> swid;
|
||||
bit<8> port;
|
||||
bit<32> byte_cnt;
|
||||
time_t last_time;
|
||||
time_t cur_time;
|
||||
}
|
||||
|
||||
// Indicates the egress port the switch should send this probe
|
||||
// packet out of. There is one of these headers for each hop.
|
||||
header probe_fwd_t {
|
||||
bit<8> egress_spec;
|
||||
}
|
||||
|
||||
struct parser_metadata_t {
|
||||
bit<8> remaining;
|
||||
}
|
||||
|
||||
struct metadata {
|
||||
bit<8> egress_spec;
|
||||
parser_metadata_t parser_metadata;
|
||||
}
|
||||
|
||||
struct headers {
|
||||
ethernet_t ethernet;
|
||||
ipv4_t ipv4;
|
||||
probe_t probe;
|
||||
probe_data_t[MAX_HOPS] probe_data;
|
||||
probe_fwd_t[MAX_HOPS] probe_fwd;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** 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;
|
||||
TYPE_PROBE: parse_probe;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4 {
|
||||
packet.extract(hdr.ipv4);
|
||||
transition accept;
|
||||
}
|
||||
|
||||
state parse_probe {
|
||||
packet.extract(hdr.probe);
|
||||
meta.parser_metadata.remaining = hdr.probe.hop_cnt + 1;
|
||||
transition select(hdr.probe.hop_cnt) {
|
||||
0: parse_probe_fwd;
|
||||
default: parse_probe_data;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_probe_data {
|
||||
packet.extract(hdr.probe_data.next);
|
||||
transition select(hdr.probe_data.last.bos) {
|
||||
1: parse_probe_fwd;
|
||||
default: parse_probe_data;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_probe_fwd {
|
||||
packet.extract(hdr.probe_fwd.next);
|
||||
meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1;
|
||||
// extract the forwarding data
|
||||
meta.egress_spec = hdr.probe_fwd.last.egress_spec;
|
||||
transition select(meta.parser_metadata.remaining) {
|
||||
0: accept;
|
||||
default: parse_probe_fwd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************ 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(standard_metadata);
|
||||
}
|
||||
|
||||
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 = drop();
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.ipv4.isValid()) {
|
||||
ipv4_lpm.apply();
|
||||
}
|
||||
else if (hdr.probe.isValid()) {
|
||||
standard_metadata.egress_spec = (bit<9>)meta.egress_spec;
|
||||
hdr.probe.hop_cnt = hdr.probe.hop_cnt + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** 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) {
|
||||
|
||||
// count the number of bytes seen since the last probe
|
||||
register<bit<32>>(MAX_PORTS) byte_cnt_reg;
|
||||
// remember the time of the last probe
|
||||
register<time_t>(MAX_PORTS) last_time_reg;
|
||||
|
||||
action set_swid(bit<7> swid) {
|
||||
hdr.probe_data[0].swid = swid;
|
||||
}
|
||||
|
||||
table swid {
|
||||
actions = {
|
||||
set_swid;
|
||||
NoAction;
|
||||
}
|
||||
default_action = NoAction();
|
||||
}
|
||||
|
||||
apply {
|
||||
bit<32> byte_cnt;
|
||||
bit<32> new_byte_cnt;
|
||||
time_t last_time;
|
||||
time_t cur_time = standard_metadata.egress_global_timestamp;
|
||||
// increment byte cnt for this packet's port
|
||||
byte_cnt_reg.read(byte_cnt, (bit<32>)standard_metadata.egress_port);
|
||||
byte_cnt = byte_cnt + standard_metadata.packet_length;
|
||||
// reset the byte count when a probe packet passes through
|
||||
new_byte_cnt = (hdr.probe.isValid()) ? 0 : byte_cnt;
|
||||
byte_cnt_reg.write((bit<32>)standard_metadata.egress_port, new_byte_cnt);
|
||||
|
||||
if (hdr.probe.isValid()) {
|
||||
// fill out probe fields
|
||||
hdr.probe_data.push_front(1);
|
||||
hdr.probe_data[0].setValid();
|
||||
if (hdr.probe.hop_cnt == 1) {
|
||||
hdr.probe_data[0].bos = 1;
|
||||
}
|
||||
else {
|
||||
hdr.probe_data[0].bos = 0;
|
||||
}
|
||||
// set switch ID field
|
||||
swid.apply();
|
||||
hdr.probe_data[0].port = (bit<8>)standard_metadata.egress_port;
|
||||
hdr.probe_data[0].byte_cnt = byte_cnt;
|
||||
// read / update the last_time_reg
|
||||
last_time_reg.read(last_time, (bit<32>)standard_metadata.egress_port);
|
||||
last_time_reg.write((bit<32>)standard_metadata.egress_port, cur_time);
|
||||
hdr.probe_data[0].last_time = last_time;
|
||||
hdr.probe_data[0].cur_time = cur_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************* 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.probe);
|
||||
packet.emit(hdr.probe_data);
|
||||
packet.emit(hdr.probe_fwd);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
@ -1,5 +1,3 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
NO_P4 = true
|
||||
P4C_ARGS = --p4runtime-files $(basename $@).p4.p4info.txt
|
||||
|
||||
include ../../utils/Makefile
|
||||
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 67 B |
@ -27,7 +27,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.set_nhop",
|
||||
"action_params": {
|
||||
"nhop_dmac": "00:00:00:00:02:02",
|
||||
"nhop_dmac": "08:00:00:00:02:02",
|
||||
"nhop_ipv4": "10.0.2.2",
|
||||
"port" : 1
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.set_nhop",
|
||||
"action_params": {
|
||||
"nhop_dmac": "00:00:00:00:03:03",
|
||||
"nhop_dmac": "08:00:00:00:03:03",
|
||||
"nhop_ipv4": "10.0.3.3",
|
||||
"port" : 1
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
{
|
||||
"hosts": [
|
||||
"h1",
|
||||
"h2",
|
||||
"h3"
|
||||
],
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:01",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:02",
|
||||
"commands":["route add default gw 10.0.2.20 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:03",
|
||||
"commands":["route add default gw 10.0.3.30 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"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"],
|
||||
["s3", "s2"], ["s2", "h2"], ["s3", "h3"]
|
||||
["h1", "s1-p1"], ["s1-p2", "s2-p2"], ["s1-p3", "s3-p2"],
|
||||
["s3-p3", "s2-p3"], ["h2", "s2-p1"], ["h3", "s3-p1"]
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
NO_P4 = true
|
||||
P4C_ARGS = --p4runtime-files $(basename $@).p4.p4info.txt
|
||||
|
||||
include ../../utils/Makefile
|
||||
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 67 B |
@ -18,7 +18,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:01:01",
|
||||
"dstAddr": "08:00:00:00:01:01",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -29,7 +29,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:01:0b",
|
||||
"dstAddr": "08:00:00:00:01:11",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -41,7 +41,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:02:03:00",
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
@ -52,7 +52,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:03:02:00",
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:02:02",
|
||||
"dstAddr": "08:00:00:00:02:02",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -29,7 +29,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:02:16",
|
||||
"dstAddr": "08:00:00:00:02:22",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -40,7 +40,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:01:03:00",
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
@ -51,7 +51,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:03:03:00",
|
||||
"dstAddr": "08:00:00:00:03:00",
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:00:03:03",
|
||||
"dstAddr": "08:00:00:00:03:03",
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
@ -29,7 +29,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:01:04:00",
|
||||
"dstAddr": "08:00:00:00:01:00",
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
@ -40,7 +40,7 @@
|
||||
},
|
||||
"action_name": "MyIngress.ipv4_forward",
|
||||
"action_params": {
|
||||
"dstAddr": "00:00:00:02:04:00",
|
||||
"dstAddr": "08:00:00:00:02:00",
|
||||
"port": 3
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,28 @@
|
||||
{
|
||||
"hosts": [
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h11",
|
||||
"h22"
|
||||
],
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/31", "mac": "08:00:00:00:01:01",
|
||||
"commands":["route add default gw 10.0.1.0 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.0 08:00:00:00:01:00"]},
|
||||
"h11": {"ip": "10.0.1.11/31", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/31", "mac": "08:00:00:00:02:02",
|
||||
"commands":["route add default gw 10.0.2.3 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.3 08:00:00:00:02:00"]},
|
||||
"h22": {"ip": "10.0.2.22/31", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.23 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.23 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/31", "mac": "08:00:00:00:03:03",
|
||||
"commands":["route add default gw 10.0.3.2 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.2 08:00:00:00:03:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"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"],
|
||||
["s3", "s2"], ["s2", "h2"], ["s2", "h22"], ["s3", "h3"]
|
||||
["h1", "s1-p2"], ["h11", "s1-p1"], ["s1-p3", "s2-p3", "0", 0.5], ["s1-p4", "s3-p2"],
|
||||
["s3-p3", "s2-p4"], ["h2", "s2-p2"], ["h22", "s2-p1"], ["h3", "s3-p1"]
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
NO_P4 = true
|
||||
P4C_ARGS = --p4runtime-files $(basename $@).p4.p4info.txt
|
||||
|
||||
include ../../utils/Makefile
|
||||
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 67 B |
@ -160,11 +160,11 @@ def main(p4info_file_path, bmv2_file_path):
|
||||
|
||||
# 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")
|
||||
dst_eth_addr="08:00:00:00:02:22", 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")
|
||||
dst_eth_addr="08:00:00:00:01:11", 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)
|
||||
|
@ -185,11 +185,11 @@ def main(p4info_file_path, bmv2_file_path):
|
||||
|
||||
# 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")
|
||||
dst_eth_addr="08:00:00:00:02:22", 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")
|
||||
dst_eth_addr="08:00:00:00:01:11", 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)
|
||||
|
@ -1,16 +1,22 @@
|
||||
{
|
||||
"hosts": [
|
||||
"h1",
|
||||
"h2",
|
||||
"h3"
|
||||
],
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.20 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
|
||||
"commands":["route add default gw 10.0.3.30 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"s1": {},
|
||||
"s2": {},
|
||||
"s3": {}
|
||||
},
|
||||
"links": [
|
||||
["h1", "s1"], ["s1", "s2"], ["s1", "s3"],
|
||||
["s3", "s2"], ["s2", "h2"], ["s3", "h3"]
|
||||
["h1", "s1-p1"], ["s1-p2", "s2-p2"], ["s1-p3", "s3-p2"],
|
||||
["s3-p3", "s2-p3"], ["h2", "s2-p1"], ["h3", "s3-p1"]
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
NO_P4 = true
|
||||
P4C_ARGS = --p4runtime-files $(basename $@).p4.p4info.txt
|
||||
|
||||
include ../../utils/Makefile
|
||||
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 67 B |
@ -49,7 +49,7 @@ bind_layers(SourceRoute, SourceRoute, bos=0)
|
||||
bind_layers(SourceRoute, SourceRoutingTail, bos=1)
|
||||
|
||||
def main():
|
||||
iface = 'h2-eth0'
|
||||
iface = 'eth0'
|
||||
print "sniffing on %s" % iface
|
||||
sys.stdout.flush()
|
||||
sniff(filter="udp and port 4321", iface = iface,
|
||||
|
@ -1,16 +1,22 @@
|
||||
{
|
||||
"hosts": [
|
||||
"h1",
|
||||
"h2",
|
||||
"h3"
|
||||
],
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
|
||||
"commands":["route add default gw 10.0.1.10 dev eth0",
|
||||
"arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
|
||||
"h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
|
||||
"commands":["route add default gw 10.0.2.20 dev eth0",
|
||||
"arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
|
||||
"h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
|
||||
"commands":["route add default gw 10.0.3.30 dev eth0",
|
||||
"arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]}
|
||||
},
|
||||
"switches": {
|
||||
"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"],
|
||||
["s3", "s2"], ["s2", "h2"], ["s3", "h3"]
|
||||
["h1", "s1-p1"], ["s1-p2", "s2-p2"], ["s1-p3", "s3-p2"],
|
||||
["s3-p3", "s2-p3"], ["h2", "s2-p1"], ["h3", "s3-p1"]
|
||||
]
|
||||
}
|
||||
|
@ -2,18 +2,26 @@ BUILD_DIR = build
|
||||
PCAP_DIR = pcaps
|
||||
LOG_DIR = logs
|
||||
|
||||
TOPO = topology.json
|
||||
P4C = p4c-bm2-ss
|
||||
P4C_ARGS += --p4runtime-files $(BUILD_DIR)/$(basename $@).p4.p4info.txt
|
||||
|
||||
RUN_SCRIPT = ../../utils/run_exercise.py
|
||||
|
||||
source := $(wildcard *.p4)
|
||||
outfile := $(source:.p4=.json)
|
||||
ifndef TOPO
|
||||
TOPO = topology.json
|
||||
endif
|
||||
|
||||
compiled_json := $(BUILD_DIR)/$(outfile)
|
||||
source = $(wildcard *.p4)
|
||||
compiled_json := $(source:.p4=.json)
|
||||
|
||||
ifndef DEFAULT_PROG
|
||||
DEFAULT_PROG = $(wildcard *.p4)
|
||||
endif
|
||||
DEFAULT_JSON = $(BUILD_DIR)/$(DEFAULT_PROG:.p4=.json)
|
||||
|
||||
# Define NO_P4 to start BMv2 without a program
|
||||
ifndef NO_P4
|
||||
run_args += -j $(compiled_json)
|
||||
run_args += -j $(DEFAULT_JSON)
|
||||
endif
|
||||
|
||||
# Set BMV2_SWITCH_EXE to override the BMv2 target
|
||||
@ -31,8 +39,8 @@ stop:
|
||||
|
||||
build: dirs $(compiled_json)
|
||||
|
||||
$(BUILD_DIR)/%.json: %.p4
|
||||
$(P4C) --p4v 16 $(P4C_ARGS) -o $@ $<
|
||||
%.json: %.p4
|
||||
$(P4C) --p4v 16 $(P4C_ARGS) -o $(BUILD_DIR)/$@ $<
|
||||
|
||||
dirs:
|
||||
mkdir -p $(BUILD_DIR) $(PCAP_DIR) $(LOG_DIR)
|
||||
|
@ -48,7 +48,7 @@ class P4RuntimeSwitch(P4Switch):
|
||||
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")
|
||||
error("Invalid JSON file: {}\n".format(json_path))
|
||||
exit(1)
|
||||
self.json_path = json_path
|
||||
else:
|
||||
|
@ -66,65 +66,57 @@ def configureP4Switch(**switch_args):
|
||||
|
||||
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):
|
||||
def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts):
|
||||
Topo.__init__(self, **opts)
|
||||
host_links = []
|
||||
switch_links = []
|
||||
self.sw_port_mapping = {}
|
||||
|
||||
# assumes host always comes first for host<-->switch links
|
||||
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 sw, params in switches.iteritems():
|
||||
if "program" in params:
|
||||
switchClass = configureP4Switch(
|
||||
sw_path=bmv2_exe,
|
||||
json_path=params["program"],
|
||||
log_console=True,
|
||||
pcap_dump=pcap_dir)
|
||||
else:
|
||||
# add default switch
|
||||
switchClass = None
|
||||
self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw), cls=switchClass)
|
||||
|
||||
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,
|
||||
sw_name, sw_port = self.parse_switch_node(link['node2'])
|
||||
host_ip = hosts[host_name]['ip']
|
||||
host_mac = hosts[host_name]['mac']
|
||||
self.addHost(host_name, ip=host_ip, mac=host_mac)
|
||||
self.addLink(host_name, sw_name,
|
||||
delay=link['latency'], bw=link['bandwidth'],
|
||||
addr1=host_mac, addr2=host_mac)
|
||||
self.addSwitchPort(host_sw, host_name)
|
||||
port2=sw_port)
|
||||
|
||||
for link in switch_links:
|
||||
self.addLink(link['node1'], link['node2'],
|
||||
sw1_name, sw1_port = self.parse_switch_node(link['node1'])
|
||||
sw2_name, sw2_port = self.parse_switch_node(link['node2'])
|
||||
self.addLink(sw1_name, sw2_name,
|
||||
port1=sw1_port, port2=sw2_port,
|
||||
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
|
||||
def parse_switch_node(self, node):
|
||||
assert(len(node.split('-')) == 2)
|
||||
sw_name, sw_port = node.split('-')
|
||||
try:
|
||||
sw_port = int(sw_port[1])
|
||||
except:
|
||||
raise Exception('Invalid switch node in topology file: {}'.format(node))
|
||||
return sw_name, sw_port
|
||||
|
||||
|
||||
class ExerciseRunner:
|
||||
@ -134,8 +126,8 @@ class ExerciseRunner:
|
||||
pcap_dir : string // directory for mininet switch pcap files
|
||||
quiet : bool // determines if we print logger messages
|
||||
|
||||
hosts : list<string> // list of mininet host names
|
||||
switches : dict<string, dict> // mininet host names and their associated properties
|
||||
hosts : dict<string, dict> // mininet host names and their associated properties
|
||||
switches : dict<string, dict> // mininet switch names and their associated properties
|
||||
links : list<dict> // list of mininet link properties
|
||||
|
||||
switch_json : string // json of the compiled p4 example
|
||||
@ -149,7 +141,7 @@ class ExerciseRunner:
|
||||
if not self.quiet:
|
||||
print(' '.join(items))
|
||||
|
||||
def formatLatency(self, l):
|
||||
def format_latency(self, l):
|
||||
""" Helper method for parsing link latencies from the topology json. """
|
||||
if isinstance(l, (str, unicode)):
|
||||
return l
|
||||
@ -232,7 +224,7 @@ class ExerciseRunner:
|
||||
'bandwidth':None
|
||||
}
|
||||
if len(link) > 2:
|
||||
link_dict['latency'] = self.formatLatency(link[2])
|
||||
link_dict['latency'] = self.format_latency(link[2])
|
||||
if len(link) > 3:
|
||||
link_dict['bandwidth'] = link[3]
|
||||
|
||||
@ -251,18 +243,18 @@ class ExerciseRunner:
|
||||
"""
|
||||
self.logger("Building mininet topology.")
|
||||
|
||||
self.topo = ExerciseTopo(self.hosts, self.switches.keys(), self.links, self.log_dir)
|
||||
defaultSwitchClass = configureP4Switch(
|
||||
sw_path=self.bmv2_exe,
|
||||
json_path=self.switch_json,
|
||||
log_console=True,
|
||||
pcap_dump=self.pcap_dir)
|
||||
|
||||
switchClass = configureP4Switch(
|
||||
sw_path=self.bmv2_exe,
|
||||
json_path=self.switch_json,
|
||||
log_console=True,
|
||||
pcap_dump=self.pcap_dir)
|
||||
self.topo = ExerciseTopo(self.hosts, self.switches, self.links, self.log_dir, self.bmv2_exe, self.pcap_dir)
|
||||
|
||||
self.net = Mininet(topo = self.topo,
|
||||
link = TCLink,
|
||||
host = P4Host,
|
||||
switch = switchClass,
|
||||
switch = defaultSwitchClass,
|
||||
controller = None)
|
||||
|
||||
def program_switch_p4runtime(self, sw_name, sw_dict):
|
||||
@ -312,30 +304,13 @@ class ExerciseRunner:
|
||||
self.program_switch_p4runtime(sw_name, sw_dict)
|
||||
|
||||
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.
|
||||
""" Execute any commands provided in the topology.json file on each Mininet host
|
||||
"""
|
||||
for host_name in self.topo.hosts():
|
||||
for host_name, host_info in self.hosts.items():
|
||||
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)
|
||||
if "commands" in host_info:
|
||||
for cmd in host_info["commands"]:
|
||||
h.cmd(cmd)
|
||||
|
||||
|
||||
def do_net_cli(self):
|
||||
|
@ -73,10 +73,10 @@ chmod 440 /etc/sudoers.d/99_p4
|
||||
usermod -aG vboxsf p4
|
||||
|
||||
cd /usr/share/lubuntu/wallpapers/
|
||||
cp ~/p4-logo.png .
|
||||
cp /home/vagrant/p4-logo.png .
|
||||
rm lubuntu-default-wallpaper.png
|
||||
ln -s p4-logo.png lubuntu-default-wallpaper.png
|
||||
rm ~/p4-logo.png
|
||||
rm /home/vagrant/p4-logo.png
|
||||
cd ~
|
||||
sed -i s@#background=@background=/usr/share/lubuntu/wallpapers/1604-lubuntu-default-wallpaper.png@ /etc/lightdm/lightdm-gtk-greeter.conf
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
set -xe
|
||||
|
||||
#Src
|
||||
BMV2_COMMIT="884e01b531c6fd078cc2438a40258ecae011a65b" # Apr 24, 2019
|
||||
PI_COMMIT="19de33e83bae7b737a3f8a1c9507c6e84173d96f" # Apr 24, 2019
|
||||
P4C_COMMIT="61409c890c58d14ec7d6790f263eb44f393e542a" # Apr 24, 2019
|
||||
BMV2_COMMIT="b447ac4c0cfd83e5e72a3cc6120251c1e91128ab" # August 10, 2019
|
||||
PI_COMMIT="41358da0ff32c94fa13179b9cee0ab597c9ccbcc" # August 10, 2019
|
||||
P4C_COMMIT="69e132d0d663e3408d740aaf8ed534ecefc88810" # August 10, 2019
|
||||
PROTOBUF_COMMIT="v3.2.0"
|
||||
GRPC_COMMIT="v1.3.2"
|
||||
|
||||
@ -112,6 +112,8 @@ sudo pip install crcmod
|
||||
git clone https://github.com/p4lang/tutorials
|
||||
sudo mv tutorials /home/p4
|
||||
sudo chown -R p4:p4 /home/p4/tutorials
|
||||
# Install grip for offline markdown rendering
|
||||
sudo pip install grip
|
||||
|
||||
# --- Emacs --- #
|
||||
sudo cp p4_16-mode.el /usr/share/emacs/site-lisp/
|
||||
|