Sigcomm 17 (#52)
This problem contains the tutorial exercises and solutions presented at SIGCOMM '17.
This commit is contained in:
239
SIGCOMM_2017/exercises/mri/README.md
Normal file
239
SIGCOMM_2017/exercises/mri/README.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Implementing MRI
|
||||
|
||||
## Introduction
|
||||
|
||||
The objective of this tutorial is to extend basic L3 forwarding with a
|
||||
scaled-down version of In-Band Network Telemetry (INT), which we call
|
||||
Multi-Hop Route Inspection (MRI).
|
||||
|
||||
MRI allows users to track the path and the length of queues that every
|
||||
packet travels through. To support this functionality, you will need
|
||||
to write a P4 program that appends an ID and queue length to the
|
||||
header stack of every packet. At the destination, the sequence of
|
||||
switch IDs correspond to the path, and each ID is followed by the
|
||||
queue length of the port at switch.
|
||||
|
||||
As before, we have already defined the control plane rules, so you
|
||||
only need to implement the data plane logic of your P4 program.
|
||||
|
||||
> **Spoiler alert:** There is a reference solution in the `solution`
|
||||
> sub-directory. Feel free to compare your implementation to the reference.
|
||||
|
||||
## Step 1: Run the (incomplete) starter code
|
||||
|
||||
The directory with this README also contains a skeleton P4 program,
|
||||
`mri.p4`, which initially implements L3 forwarding. Your job (in the
|
||||
next step) will be to extend it to properly prepend the MRI custom
|
||||
headers.
|
||||
|
||||
Before that, let's compile the incomplete `mri.p4` and bring up a
|
||||
switch in Mininet to test its behavior.
|
||||
|
||||
1. In your shell, run:
|
||||
```bash
|
||||
./run.sh
|
||||
```
|
||||
This will:
|
||||
* compile `mri.p4`, and
|
||||
* start a Mininet instance with three switches (`s1`, `s2`, `s3`) configured
|
||||
in a triangle. There are 5 hosts. `h1` and `h11` are connected to `s1`.
|
||||
`h2` and `h22` are connected to `s2` and `h3` is connected to `s3`.
|
||||
* The hosts are assigned IPs of `10.0.1.10`, `10.0.2.10`, etc
|
||||
(`10.0.<Switchid>.<hostID>`).
|
||||
* The control plane programs the P4 tables in each switch based on
|
||||
`sx-commands.txt`
|
||||
|
||||
2. We want to send a low rate traffic from `h1` to `h2` and a high
|
||||
rate iperf traffic from `h11` to `h22`. The link between `s1` and
|
||||
`s2` is common between the flows and is a bottleneck because we
|
||||
reduced its bandwidth to 512kbps in p4app.json. Therefore, if we
|
||||
capture packets at `h2`, we should see high queue size for that
|
||||
link.
|
||||
|
||||
3. You should now see a Mininet command prompt. Open four terminals
|
||||
for `h1`, `h11`, `h2`, `h22`, respectively:
|
||||
```bash
|
||||
mininet> xterm h1 h11 h2 h22
|
||||
```
|
||||
3. In `h2`'s xterm, start the server that captures packets:
|
||||
```bash
|
||||
./receive.py
|
||||
```
|
||||
4. in `h22`'s xterm, start the iperf UDP server:
|
||||
```bash
|
||||
iperf -s -u
|
||||
```
|
||||
|
||||
5. In `h1`'s xterm, send one packet per second to `h2` using send.py
|
||||
say for 30 seconds:
|
||||
```bash
|
||||
./send.py 10.0.2.2 "P4 is cool" 30
|
||||
```
|
||||
The message "P4 is cool" should be received in `h2`'s xterm,
|
||||
6. In `h11`'s xterm, start iperf client sending for 15 seconds
|
||||
```bash
|
||||
h11 iperf -c 10.0.2.22 -t 15 -u
|
||||
```
|
||||
7. At `h2`, the MRI header has no hop info (`count=0`)
|
||||
8. type `exit` to close each xterm window
|
||||
|
||||
You should see the message received at host `h2`, but without any
|
||||
information about the path the message took. Your job is to extend
|
||||
the code in `mri.p4` to implement the MRI logic to record the path.
|
||||
|
||||
### A note about the control plane
|
||||
|
||||
P4 programs define a packet-processing pipeline, but the rules
|
||||
governing packet processing are inserted into the pipeline by the
|
||||
control plane. When a rule matches a packet, its action is invoked
|
||||
with parameters supplied by the control plane as part of the rule.
|
||||
|
||||
In this exercise, the control plane logic has already been
|
||||
implemented. As part of bringing up the Mininet instance, the
|
||||
`run.sh` script will install packet-processing rules in the tables of
|
||||
each switch. These are defined in the `sX-commands.txt` files, where
|
||||
`X` corresponds to the switch number.
|
||||
|
||||
## Step 2: Implement MRI
|
||||
|
||||
The `mri.p4` file contains a skeleton P4 program with key pieces of
|
||||
logic replaced by `TODO` comments. These should guide your
|
||||
implementation---replace each `TODO` with logic implementing the
|
||||
missing piece.
|
||||
|
||||
MRI will require two custom headers. The first header, `mri_t`,
|
||||
contains a single field `count`, which indicates the number of switch
|
||||
IDs that follow. The second header, `switch_t`, contains switch ID and
|
||||
Queue depth fields of each switch hop the packet goes through.
|
||||
|
||||
One of the biggest challenges in implementing MRI is handling the
|
||||
recursive logic for parsing these two headers. We will use a
|
||||
`parser_metadata` field, `remaining`, to keep track of how many
|
||||
`switch_t` headers we need to parse. In the `parse_mri` state, this
|
||||
field should be set to `hdr.mri.count`. In the `parse_swtrace` state,
|
||||
this field should be decremented. The `parse_swtrace` state will
|
||||
transition to itself until `remaining` is 0.
|
||||
|
||||
The MRI custom headers will be carried inside an IP Options
|
||||
header. The IP Options header contains a field, `option`, which
|
||||
indicates the type of the option. We will use a special type 31 to
|
||||
indicate the presence of the MRI headers.
|
||||
|
||||
Beyond the parser logic, you will add a table in egress, `swtrace` to
|
||||
store the switch ID and queue depth, and actions that increment the
|
||||
`count` field, and append a `switch_t` header.
|
||||
|
||||
A complete `mri.p4` will contain the following components:
|
||||
|
||||
1. Header type definitions for Ethernet (`ethernet_t`), IPv4 (`ipv4_t`),
|
||||
IP Options (`ipv4_option_t`), MRI (`mri_t`), and Switch (`switch_t`).
|
||||
2. Parsers for Ethernet, IPv4, IP Options, MRI, and Switch that will
|
||||
populate `ethernet_t`, `ipv4_t`, `ipv4_option_t`, `mri_t`, and
|
||||
`switch_t`.
|
||||
3. An action to drop a packet, using `mark_to_drop()`.
|
||||
4. An action (called `ipv4_forward`), which will:
|
||||
1. Set the egress port for the next hop.
|
||||
2. Update the ethernet destination address with the address of
|
||||
the next hop.
|
||||
3. Update the ethernet source address with the address of the switch.
|
||||
4. Decrement the TTL.
|
||||
5. An ingress control that:
|
||||
1. Defines a table that will read an IPv4 destination address, and
|
||||
invoke either `drop` or `ipv4_forward`.
|
||||
2. An `apply` block that applies the table.
|
||||
6. At egress, an action (called `add_swtrace`) that will add the
|
||||
switch ID and queue depth.
|
||||
8. An egress control that applies a table (`swtrace`) to store the
|
||||
switch ID and queue depth, and calls `add_swtrace`.
|
||||
9. A deparser that selects the order in which fields inserted into the outgoing
|
||||
packet.
|
||||
10. A `package` instantiation supplied with the parser, control,
|
||||
checksum verification and recomputation and deparser.
|
||||
|
||||
## Step 3: Run your solution
|
||||
|
||||
Follow the instructions from Step 1. This time, when your message
|
||||
from `h1` is delivered to `h2`, you should see the seqeunce of
|
||||
switches through which the packet traveled plus the corresponding
|
||||
queue depths. The expected output will look like the following,
|
||||
which shows the MRI header, with a `count` of 2, and switch ids
|
||||
(`swids`) 2 and 1. The queue depth at the common link (from s1 to
|
||||
s2) is high.
|
||||
|
||||
```
|
||||
got a packet
|
||||
###[ Ethernet ]###
|
||||
dst = 00:04:00:02:00:02
|
||||
src = f2:ed:e6:df:4e:fa
|
||||
type = 0x800
|
||||
###[ IP ]###
|
||||
version = 4L
|
||||
ihl = 10L
|
||||
tos = 0x0
|
||||
len = 42
|
||||
id = 1
|
||||
flags =
|
||||
frag = 0L
|
||||
ttl = 62
|
||||
proto = udp
|
||||
chksum = 0x60c0
|
||||
src = 10.0.1.1
|
||||
dst = 10.0.2.2
|
||||
\options \
|
||||
|###[ MRI ]###
|
||||
| copy_flag = 0L
|
||||
| optclass = control
|
||||
| option = 31L
|
||||
| length = 20
|
||||
| count = 2
|
||||
| \swtraces \
|
||||
| |###[ SwitchTrace ]###
|
||||
| | swid = 2
|
||||
| | qdepth = 0
|
||||
| |###[ SwitchTrace ]###
|
||||
| | swid = 1
|
||||
| | qdepth = 17
|
||||
###[ Raw ]###
|
||||
load = '\x04\xd2'
|
||||
###[ Padding ]###
|
||||
load = '\x10\xe1\x00\x12\x1c{P4 is cool'
|
||||
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
There are several ways that problems might manifest:
|
||||
|
||||
1. `mri.p4` fails to compile. In this case, `run.sh` will report the
|
||||
error emitted from the compiler and stop.
|
||||
2. `mri.p4` compiles but does not support the control plane rules in
|
||||
the `sX-commands.txt` files that `run.sh` tries to install using the BMv2 CLI.
|
||||
In this case, `run.sh` will report these errors to `stderr`. Use these error
|
||||
messages to fix your `mri.p4` implementation.
|
||||
3. `mri.p4` compiles, and the control plane rules are installed, but
|
||||
the switch does not process packets in the desired way. The
|
||||
`build/logs/<switch-name>.log` files contain trace messages describing
|
||||
how each switch processes each packet. The output is detailed and can
|
||||
help pinpoint logic errors in your implementation. The
|
||||
`build/<switch-name>-<interface-name>.pcap` also contains the pcap of
|
||||
packets on each interface. Use `tcpdump -r <filename> -xxx` to print
|
||||
the hexdump of the packets.
|
||||
4. `mri.p4` compiles and all rules are installed. Packets go through
|
||||
and the logs show that the queue length is always 0. Then either
|
||||
reduce the link bandwidth in `p4app.json`.
|
||||
|
||||
#### Cleaning up Mininet
|
||||
|
||||
In the latter two cases above, `run.sh` may leave a Mininet instance
|
||||
running in the background. Use the following command to clean up
|
||||
these instances:
|
||||
|
||||
```bash
|
||||
mn -c
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Congratulations, your implementation works! Move on to [Source
|
||||
Routing](../source_routing).
|
||||
|
||||
280
SIGCOMM_2017/exercises/mri/mri.p4
Normal file
280
SIGCOMM_2017/exercises/mri/mri.p4
Normal file
@@ -0,0 +1,280 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
const bit<8> UDP_PROTOCOL = 0x11;
|
||||
const bit<16> TYPE_IPV4 = 0x800;
|
||||
const bit<5> IPV4_OPTION_MRI = 31;
|
||||
|
||||
#define MAX_HOPS 9
|
||||
|
||||
/*************************************************************************
|
||||
*********************** H E A D E R S ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
typedef bit<9> egressSpec_t;
|
||||
typedef bit<48> macAddr_t;
|
||||
typedef bit<32> ip4Addr_t;
|
||||
typedef bit<32> switchID_t;
|
||||
typedef bit<32> qdepth_t;
|
||||
|
||||
header ethernet_t {
|
||||
macAddr_t dstAddr;
|
||||
macAddr_t srcAddr;
|
||||
bit<16> etherType;
|
||||
}
|
||||
|
||||
header ipv4_t {
|
||||
bit<4> version;
|
||||
bit<4> ihl;
|
||||
bit<8> diffserv;
|
||||
bit<16> totalLen;
|
||||
bit<16> identification;
|
||||
bit<3> flags;
|
||||
bit<13> fragOffset;
|
||||
bit<8> ttl;
|
||||
bit<8> protocol;
|
||||
bit<16> hdrChecksum;
|
||||
ip4Addr_t srcAddr;
|
||||
ip4Addr_t dstAddr;
|
||||
}
|
||||
|
||||
header ipv4_option_t {
|
||||
bit<1> copyFlag;
|
||||
bit<2> optClass;
|
||||
bit<5> option;
|
||||
bit<8> optionLength;
|
||||
}
|
||||
|
||||
header mri_t {
|
||||
bit<16> count;
|
||||
}
|
||||
|
||||
header switch_t {
|
||||
switchID_t swid;
|
||||
qdepth_t qdepth;
|
||||
}
|
||||
|
||||
struct ingress_metadata_t {
|
||||
bit<16> count;
|
||||
}
|
||||
|
||||
struct parser_metadata_t {
|
||||
bit<16> remaining;
|
||||
}
|
||||
|
||||
struct metadata {
|
||||
ingress_metadata_t ingress_metadata;
|
||||
parser_metadata_t parser_metadata;
|
||||
}
|
||||
|
||||
struct headers {
|
||||
ethernet_t ethernet;
|
||||
ipv4_t ipv4;
|
||||
ipv4_option_t ipv4_option;
|
||||
mri_t mri;
|
||||
switch_t[MAX_HOPS] swtraces;
|
||||
}
|
||||
|
||||
error { IPHeaderTooShort }
|
||||
|
||||
/*************************************************************************
|
||||
*********************** P A R S E R ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
parser MyParser(packet_in packet,
|
||||
out headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
|
||||
state start {
|
||||
transition parse_ethernet;
|
||||
}
|
||||
|
||||
state parse_ethernet {
|
||||
packet.extract(hdr.ethernet);
|
||||
transition select(hdr.ethernet.etherType) {
|
||||
TYPE_IPV4: parse_ipv4;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4 {
|
||||
packet.extract(hdr.ipv4);
|
||||
verify(hdr.ipv4.ihl >= 5, error.IPHeaderTooShort);
|
||||
transition select(hdr.ipv4.ihl) {
|
||||
5 : accept;
|
||||
default : parse_ipv4_option;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4_option {
|
||||
/*
|
||||
* TODO: Add logic to:
|
||||
* - Extract the ipv4_option header.
|
||||
* - If value is equal to IPV4_OPTION_MRI, transition to parse_mri.
|
||||
* - Otherwise, accept.
|
||||
*/
|
||||
transition accept;
|
||||
}
|
||||
|
||||
state parse_mri {
|
||||
/*
|
||||
* TODO: Add logic to:
|
||||
* - Extract hdr.mri.
|
||||
* - Set meta.parser_metadata.remaining to hdr.mri.count
|
||||
* - Select on the value of meta.parser_metadata.remaining
|
||||
* - If the value is equal to 0, accept.
|
||||
* - Otherwise, transition to parse_swid.
|
||||
*/
|
||||
transition accept;
|
||||
}
|
||||
|
||||
state parse_swtrace {
|
||||
/*
|
||||
* TODO: Add logic to:
|
||||
* - Extract hdr.swtraces.next.
|
||||
* - Decrement meta.parser_metadata.remaining by 1
|
||||
* - Select on the value of meta.parser_metadata.remaining
|
||||
* - If the value is equal to 0, accept.
|
||||
* - Otherwise, transition to parse_swtrace.
|
||||
*/
|
||||
transition accept;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
************ C H E C K S U M V E R I F I C A T I O N *************
|
||||
*************************************************************************/
|
||||
|
||||
control MyVerifyChecksum(in headers hdr, inout metadata meta) {
|
||||
apply { }
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
************** I N G R E S S P R O C E S S I N G *******************
|
||||
*************************************************************************/
|
||||
|
||||
control MyIngress(inout headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
action drop() {
|
||||
mark_to_drop();
|
||||
}
|
||||
|
||||
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
|
||||
standard_metadata.egress_spec = port;
|
||||
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
|
||||
hdr.ethernet.dstAddr = dstAddr;
|
||||
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
|
||||
}
|
||||
|
||||
table ipv4_lpm {
|
||||
key = {
|
||||
hdr.ipv4.dstAddr: lpm;
|
||||
}
|
||||
actions = {
|
||||
ipv4_forward;
|
||||
drop;
|
||||
NoAction;
|
||||
}
|
||||
size = 1024;
|
||||
default_action = NoAction();
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.ipv4.isValid()) {
|
||||
ipv4_lpm.apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** E G R E S S P R O C E S S I N G *******************
|
||||
*************************************************************************/
|
||||
|
||||
control MyEgress(inout headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
action add_swtrace(switchID_t swid) {
|
||||
/*
|
||||
* TODO: add logic to:
|
||||
- Increment hdr.mri.count by 1
|
||||
- Add a new swtrace header by calling push_front(1) on hdr.swtraces.
|
||||
- Set hdr.swtraces[0].swid to the id paremeter
|
||||
- Set hdr.swtraces[0].qdepth to (qdepth_t)standard_metadata.deq_qdepth
|
||||
- Incremement hdr.ipv4.ihl by 2
|
||||
- Incrememtn hdr.ipv4_option.optionLength by 8
|
||||
*/
|
||||
}
|
||||
|
||||
table swtrace {
|
||||
actions = {
|
||||
/* TODO: add the correct action */
|
||||
NoAction;
|
||||
}
|
||||
|
||||
default_action = NoAction();
|
||||
}
|
||||
|
||||
apply {
|
||||
/*
|
||||
* TODO: add logic to:
|
||||
* - If hdr.mri is valid:
|
||||
* - Apply table swtrace
|
||||
*/
|
||||
swtrace.apply();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************* C H E C K S U M C O M P U T A T I O N **************
|
||||
*************************************************************************/
|
||||
|
||||
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
|
||||
apply {
|
||||
update_checksum(
|
||||
hdr.ipv4.isValid(),
|
||||
{ hdr.ipv4.version,
|
||||
hdr.ipv4.ihl,
|
||||
hdr.ipv4.diffserv,
|
||||
hdr.ipv4.totalLen,
|
||||
hdr.ipv4.identification,
|
||||
hdr.ipv4.flags,
|
||||
hdr.ipv4.fragOffset,
|
||||
hdr.ipv4.ttl,
|
||||
hdr.ipv4.protocol,
|
||||
hdr.ipv4.srcAddr,
|
||||
hdr.ipv4.dstAddr },
|
||||
hdr.ipv4.hdrChecksum,
|
||||
HashAlgorithm.csum16);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** D E P A R S E R *******************************
|
||||
*************************************************************************/
|
||||
|
||||
control MyDeparser(packet_out packet, in headers hdr) {
|
||||
apply {
|
||||
packet.emit(hdr.ethernet);
|
||||
packet.emit(hdr.ipv4);
|
||||
|
||||
/* TODO: emit ipv4_option, mri and swtraces headers */
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
||||
37
SIGCOMM_2017/exercises/mri/p4app.json
Normal file
37
SIGCOMM_2017/exercises/mri/p4app.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"program": "mri.p4",
|
||||
"language": "p4-16",
|
||||
"targets": {
|
||||
"multiswitch": {
|
||||
"auto-control-plane": true,
|
||||
"cli": true,
|
||||
"pcap_dump": true,
|
||||
"bmv2_log": true,
|
||||
"links": [["h1", "s1"], ["h11", "s1"], ["s1", "s2", "0", 0.5], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s2", "h22"], ["s3", "h3"]],
|
||||
"hosts": {
|
||||
"h1": {
|
||||
},
|
||||
"h2": {
|
||||
},
|
||||
"h3": {
|
||||
},
|
||||
"h11": {
|
||||
},
|
||||
"h22": {
|
||||
}
|
||||
|
||||
},
|
||||
"switches": {
|
||||
"s1": {
|
||||
"entries": "s1-commands.txt"
|
||||
},
|
||||
"s2": {
|
||||
"entries": "s2-commands.txt"
|
||||
},
|
||||
"s3": {
|
||||
"entries": "s3-commands.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
SIGCOMM_2017/exercises/mri/receive.py
Executable file
57
SIGCOMM_2017/exercises/mri/receive.py
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import struct
|
||||
|
||||
from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr
|
||||
from scapy.all import Packet, IPOption
|
||||
from scapy.all import PacketListField, ShortField, IntField, LongField, BitField, FieldListField, FieldLenField
|
||||
from scapy.all import IP, UDP, Raw
|
||||
from scapy.layers.inet import _IPOption_HDR
|
||||
|
||||
def get_if():
|
||||
ifs=get_if_list()
|
||||
iface=None
|
||||
for i in get_if_list():
|
||||
if "eth0" in i:
|
||||
iface=i
|
||||
break;
|
||||
if not iface:
|
||||
print "Cannot find eth0 interface"
|
||||
exit(1)
|
||||
return iface
|
||||
|
||||
class SwitchTrace(Packet):
|
||||
fields_desc = [ IntField("swid", 0),
|
||||
IntField("qdepth", 0)]
|
||||
def extract_padding(self, p):
|
||||
return "", p
|
||||
|
||||
class IPOption_MRI(IPOption):
|
||||
name = "MRI"
|
||||
option = 31
|
||||
fields_desc = [ _IPOption_HDR,
|
||||
FieldLenField("length", None, fmt="B",
|
||||
length_of="swtraces",
|
||||
adjust=lambda pkt,l:l*2+4),
|
||||
ShortField("count", 0),
|
||||
PacketListField("swtraces",
|
||||
[],
|
||||
SwitchTrace,
|
||||
count_from=lambda pkt:(pkt.count*1)) ]
|
||||
|
||||
def handle_pkt(pkt):
|
||||
print "got a packet"
|
||||
pkt.show2()
|
||||
# hexdump(pkt)
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def main():
|
||||
iface = 'h2-eth0'
|
||||
print "sniffing on %s" % iface
|
||||
sys.stdout.flush()
|
||||
sniff(filter="udp and port 4321", iface = iface,
|
||||
prn = lambda x: handle_pkt(x))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
5
SIGCOMM_2017/exercises/mri/run.sh
Executable file
5
SIGCOMM_2017/exercises/mri/run.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
P4APPRUNNER=../../utils/p4apprunner.py
|
||||
mkdir -p build
|
||||
tar -czf build/p4app.tgz * --exclude='build'
|
||||
#cd build
|
||||
sudo python $P4APPRUNNER p4app.tgz --build-dir ./build
|
||||
6
SIGCOMM_2017/exercises/mri/s1-commands.txt
Normal file
6
SIGCOMM_2017/exercises/mri/s1-commands.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
table_set_default ipv4_lpm drop
|
||||
table_set_default swtrace add_swtrace 1
|
||||
table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1
|
||||
table_add ipv4_lpm ipv4_forward 10.0.1.11/32 => 00:00:00:00:01:0b 2
|
||||
table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:03:00 3
|
||||
table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:02:00 4
|
||||
6
SIGCOMM_2017/exercises/mri/s2-commands.txt
Normal file
6
SIGCOMM_2017/exercises/mri/s2-commands.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
table_set_default ipv4_lpm drop
|
||||
table_set_default swtrace add_swtrace 2
|
||||
table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1
|
||||
table_add ipv4_lpm ipv4_forward 10.0.2.22/32 => 00:00:00:00:02:16 2
|
||||
table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:03:00 3
|
||||
table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:03:00 4
|
||||
5
SIGCOMM_2017/exercises/mri/s3-commands.txt
Normal file
5
SIGCOMM_2017/exercises/mri/s3-commands.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
table_set_default ipv4_lpm drop
|
||||
table_set_default swtrace add_swtrace 3
|
||||
table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:01 1
|
||||
table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:04:00 2
|
||||
table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:02:04:00 3
|
||||
78
SIGCOMM_2017/exercises/mri/send.py
Executable file
78
SIGCOMM_2017/exercises/mri/send.py
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import socket
|
||||
import random
|
||||
import struct
|
||||
|
||||
from scapy.all import sendp, send, hexdump, get_if_list, get_if_hwaddr
|
||||
from scapy.all import Packet, IPOption
|
||||
from scapy.all import Ether, IP, UDP
|
||||
from scapy.all import IntField, FieldListField, FieldLenField, ShortField, PacketListField
|
||||
from scapy.layers.inet import _IPOption_HDR
|
||||
|
||||
from time import sleep
|
||||
|
||||
def get_if():
|
||||
ifs=get_if_list()
|
||||
iface=None # "h1-eth0"
|
||||
for i in get_if_list():
|
||||
if "eth0" in i:
|
||||
iface=i
|
||||
break;
|
||||
if not iface:
|
||||
print "Cannot find eth0 interface"
|
||||
exit(1)
|
||||
return iface
|
||||
|
||||
class SwitchTrace(Packet):
|
||||
fields_desc = [ IntField("swid", 0),
|
||||
IntField("qdepth", 0)]
|
||||
def extract_padding(self, p):
|
||||
return "", p
|
||||
|
||||
class IPOption_MRI(IPOption):
|
||||
name = "MRI"
|
||||
option = 31
|
||||
fields_desc = [ _IPOption_HDR,
|
||||
FieldLenField("length", None, fmt="B",
|
||||
length_of="swtraces",
|
||||
adjust=lambda pkt,l:l*2+4),
|
||||
ShortField("count", 0),
|
||||
PacketListField("swtraces",
|
||||
[],
|
||||
SwitchTrace,
|
||||
count_from=lambda pkt:(pkt.count*1)) ]
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
if len(sys.argv)<3:
|
||||
print 'pass 2 arguments: <destination> "<message>"'
|
||||
exit(1)
|
||||
|
||||
addr = socket.gethostbyname(sys.argv[1])
|
||||
iface = get_if()
|
||||
|
||||
pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(
|
||||
dst=addr, options = IPOption_MRI(count=0,
|
||||
swtraces=[])) / UDP(
|
||||
dport=4321, sport=1234) / sys.argv[2]
|
||||
|
||||
# pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(
|
||||
# dst=addr, options = IPOption_MRI(count=2,
|
||||
# swtraces=[SwitchTrace(swid=0,qdepth=0), SwitchTrace(swid=1,qdepth=0)])) / UDP(
|
||||
# dport=4321, sport=1234) / sys.argv[2]
|
||||
pkt.show2()
|
||||
#hexdump(pkt)
|
||||
try:
|
||||
for i in range(int(sys.argv[3])):
|
||||
sendp(pkt, iface=iface)
|
||||
sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
267
SIGCOMM_2017/exercises/mri/solution/mri.p4
Normal file
267
SIGCOMM_2017/exercises/mri/solution/mri.p4
Normal file
@@ -0,0 +1,267 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
const bit<8> UDP_PROTOCOL = 0x11;
|
||||
const bit<16> TYPE_IPV4 = 0x800;
|
||||
const bit<5> IPV4_OPTION_MRI = 31;
|
||||
|
||||
#define MAX_HOPS 9
|
||||
|
||||
/*************************************************************************
|
||||
*********************** H E A D E R S ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
typedef bit<9> egressSpec_t;
|
||||
typedef bit<48> macAddr_t;
|
||||
typedef bit<32> ip4Addr_t;
|
||||
typedef bit<32> switchID_t;
|
||||
typedef bit<32> qdepth_t;
|
||||
|
||||
header ethernet_t {
|
||||
macAddr_t dstAddr;
|
||||
macAddr_t srcAddr;
|
||||
bit<16> etherType;
|
||||
}
|
||||
|
||||
header ipv4_t {
|
||||
bit<4> version;
|
||||
bit<4> ihl;
|
||||
bit<8> diffserv;
|
||||
bit<16> totalLen;
|
||||
bit<16> identification;
|
||||
bit<3> flags;
|
||||
bit<13> fragOffset;
|
||||
bit<8> ttl;
|
||||
bit<8> protocol;
|
||||
bit<16> hdrChecksum;
|
||||
ip4Addr_t srcAddr;
|
||||
ip4Addr_t dstAddr;
|
||||
}
|
||||
|
||||
header ipv4_option_t {
|
||||
bit<1> copyFlag;
|
||||
bit<2> optClass;
|
||||
bit<5> option;
|
||||
bit<8> optionLength;
|
||||
}
|
||||
|
||||
header mri_t {
|
||||
bit<16> count;
|
||||
}
|
||||
|
||||
header switch_t {
|
||||
switchID_t swid;
|
||||
qdepth_t qdepth;
|
||||
}
|
||||
|
||||
struct ingress_metadata_t {
|
||||
bit<16> count;
|
||||
}
|
||||
|
||||
struct parser_metadata_t {
|
||||
bit<16> remaining;
|
||||
}
|
||||
|
||||
struct metadata {
|
||||
ingress_metadata_t ingress_metadata;
|
||||
parser_metadata_t parser_metadata;
|
||||
}
|
||||
|
||||
struct headers {
|
||||
ethernet_t ethernet;
|
||||
ipv4_t ipv4;
|
||||
ipv4_option_t ipv4_option;
|
||||
mri_t mri;
|
||||
switch_t[MAX_HOPS] swtraces;
|
||||
}
|
||||
|
||||
error { IPHeaderTooShort }
|
||||
|
||||
/*************************************************************************
|
||||
*********************** P A R S E R ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
parser MyParser(packet_in packet,
|
||||
out headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
|
||||
state start {
|
||||
transition parse_ethernet;
|
||||
}
|
||||
|
||||
state parse_ethernet {
|
||||
packet.extract(hdr.ethernet);
|
||||
transition select(hdr.ethernet.etherType) {
|
||||
TYPE_IPV4: parse_ipv4;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4 {
|
||||
packet.extract(hdr.ipv4);
|
||||
verify(hdr.ipv4.ihl >= 5, error.IPHeaderTooShort);
|
||||
transition select(hdr.ipv4.ihl) {
|
||||
5 : accept;
|
||||
default : parse_ipv4_option;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4_option {
|
||||
packet.extract(hdr.ipv4_option);
|
||||
transition select(hdr.ipv4_option.option) {
|
||||
IPV4_OPTION_MRI: parse_mri;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_mri {
|
||||
packet.extract(hdr.mri);
|
||||
meta.parser_metadata.remaining = hdr.mri.count;
|
||||
transition select(meta.parser_metadata.remaining) {
|
||||
0 : accept;
|
||||
default: parse_swtrace;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_swtrace {
|
||||
packet.extract(hdr.swtraces.next);
|
||||
meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1;
|
||||
transition select(meta.parser_metadata.remaining) {
|
||||
0 : accept;
|
||||
default: parse_swtrace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
************ C H E C K S U M V E R I F I C A T I O N *************
|
||||
*************************************************************************/
|
||||
|
||||
control MyVerifyChecksum(in headers hdr, inout metadata meta) {
|
||||
apply { }
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
************** I N G R E S S P R O C E S S I N G *******************
|
||||
*************************************************************************/
|
||||
|
||||
control MyIngress(inout headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
action drop() {
|
||||
mark_to_drop();
|
||||
}
|
||||
|
||||
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
|
||||
standard_metadata.egress_spec = port;
|
||||
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
|
||||
hdr.ethernet.dstAddr = dstAddr;
|
||||
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
|
||||
}
|
||||
|
||||
table ipv4_lpm {
|
||||
key = {
|
||||
hdr.ipv4.dstAddr: lpm;
|
||||
}
|
||||
actions = {
|
||||
ipv4_forward;
|
||||
drop;
|
||||
NoAction;
|
||||
}
|
||||
size = 1024;
|
||||
default_action = NoAction();
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.ipv4.isValid()) {
|
||||
ipv4_lpm.apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** E G R E S S P R O C E S S I N G *******************
|
||||
*************************************************************************/
|
||||
|
||||
control MyEgress(inout headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
action add_swtrace(switchID_t swid) {
|
||||
hdr.mri.count = hdr.mri.count + 1;
|
||||
hdr.swtraces.push_front(1);
|
||||
hdr.swtraces[0].swid = swid;
|
||||
hdr.swtraces[0].qdepth = (qdepth_t)standard_metadata.deq_qdepth;
|
||||
|
||||
hdr.ipv4.ihl = hdr.ipv4.ihl + 2;
|
||||
hdr.ipv4_option.optionLength = hdr.ipv4_option.optionLength + 8;
|
||||
}
|
||||
|
||||
table swtrace {
|
||||
actions = {
|
||||
add_swtrace;
|
||||
NoAction;
|
||||
}
|
||||
default_action = NoAction();
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.mri.isValid()) {
|
||||
swtrace.apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************* C H E C K S U M C O M P U T A T I O N **************
|
||||
*************************************************************************/
|
||||
|
||||
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
|
||||
apply {
|
||||
update_checksum(
|
||||
hdr.ipv4.isValid(),
|
||||
{ hdr.ipv4.version,
|
||||
hdr.ipv4.ihl,
|
||||
hdr.ipv4.diffserv,
|
||||
hdr.ipv4.totalLen,
|
||||
hdr.ipv4.identification,
|
||||
hdr.ipv4.flags,
|
||||
hdr.ipv4.fragOffset,
|
||||
hdr.ipv4.ttl,
|
||||
hdr.ipv4.protocol,
|
||||
hdr.ipv4.srcAddr,
|
||||
hdr.ipv4.dstAddr },
|
||||
hdr.ipv4.hdrChecksum,
|
||||
HashAlgorithm.csum16);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** D E P A R S E R *******************************
|
||||
*************************************************************************/
|
||||
|
||||
control MyDeparser(packet_out packet, in headers hdr) {
|
||||
apply {
|
||||
packet.emit(hdr.ethernet);
|
||||
packet.emit(hdr.ipv4);
|
||||
packet.emit(hdr.ipv4_option);
|
||||
packet.emit(hdr.mri);
|
||||
packet.emit(hdr.swtraces);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
||||
Reference in New Issue
Block a user