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
This commit is contained in:
sibanez12 2019-08-14 03:39:06 -07:00 committed by Nate Foster
parent b5c82700b8
commit 76a9067dea
69 changed files with 3015 additions and 246 deletions

View File

@ -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`

View File

@ -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

View File

@ -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:
![pod-topo](./pod-topo/pod-topo.png)
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)!

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

View 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
}
}
]
}

View 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
}
}
]
}

View 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
}
}
]
}

View 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
}
}
]
}

View 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"]
]
}

View File

@ -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"]
]
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View 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"]
]
}

View File

@ -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

View File

@ -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
}
},

View File

@ -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
}
},

View File

@ -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
}
},

View File

@ -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"]
]
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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"]
]
}

View 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

View 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.
![topology](./firewall-topo.png)
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
View 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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

View 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;

View 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
}
}
]
}

View 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
}
}
]
}

View 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
}
}
]
}

View 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
}
}
]
}

View 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"]
]
}

View 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;

View 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

View 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.
![topology](./link-monitor-topo.png)
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?

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

View 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;

View 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
}
}
]
}

View 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
}
}
]
}

View 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
}
}
]
}

View 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
}
}
]
}

View 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"]
]
}

View 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)

View 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
View 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()

View 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;

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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"]
]
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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"]
]
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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"]
]
}

View File

@ -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

View File

@ -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,

View File

@ -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"]
]
}

View File

@ -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)

View File

@ -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:

View File

@ -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):

View File

@ -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

View File

@ -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/