Skip to content

Commit e7e6899

Browse files
gnikolantoninbas
authored andcommitted
Added advanced Heavy Hitter Detection example (p4lang#136)
* Added advanced Heavy Hitter Detection example * Changed directory location * Restored skeleton version * Added files for common run infra with the other tutorials * Updated readme * Autogenerate setup rules * Commends in simple_router.p4 * Fix typos * Removed commended out lines
1 parent 494706b commit e7e6899

23 files changed

+2595
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
simple_router.config
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Instructions
2+
3+
## Introduction
4+
5+
In this tutorial, you will implement a heavy hitter detection filter.
6+
7+
Network flows typically have a fairly wide distribution in terms of the
8+
data they transmit, with most of the flows sending little data and few
9+
flows sending a lot. The latter flows are called heavy hitters, and they
10+
often have a detrimental effect to network performance. This is
11+
because they cause congestion, leading to significantly increased completion
12+
times for small, short-lived flows. Detecting heavy hitters allows us to treat them
13+
differently, e.g. we can put their packets in low priority queues, allowing
14+
packets of other flows to face little or no congestion.
15+
16+
In this example, you will implement a heavy hitter detection filter within
17+
a router. You can find a skeleton of the program in simple_router.p4. In that
18+
file, you have to fill in the parts that are marked with TODO.
19+
20+
This example is based on [count-min sketch](http://theory.stanford.edu/~tim/s15/l/l2.pdf).
21+
In fact, we use two count-min sketches which are reset with an offset
22+
equal to their half-life. With every new packet coming in, we update
23+
the values of both sketches but we use only the ones of the least
24+
recently reset one to decide whether a packet belongs to a heavy hitter
25+
flow or not.
26+
27+
> **Spoiler alert:** There is a reference solution in the `solution`
28+
> sub-directory. Feel free to compare your implementation to the
29+
> reference.
30+
31+
32+
## Step 1: Run the (incomplete) starter code
33+
34+
The directory with this README also contains a skeleton P4 program,
35+
`simple_router.p4`, which implements a simple router. Your job will be to
36+
extend this skeleton program to properly implement a heavy hitter
37+
detection filter.
38+
39+
Before that, let's compile the incomplete `simple_router.p4` and bring
40+
up a switch in Mininet to test its behavior.
41+
42+
1. In your shell, run:
43+
```bash
44+
./run.sh
45+
```
46+
This will:
47+
* create a p4app application,
48+
* compile `simple_switch.p4`,
49+
* generate control plane code,
50+
* start a Mininet instance with one switch (`s1`) conected to
51+
two hosts (`h1` and `h2`).
52+
* install the control plane code to your switch,
53+
* The hosts are assigned IPs of `10.0.0.10` and `10.0.1.10`.
54+
55+
2. You should now see a Mininet command prompt. Run ping between
56+
`h1` and `h2` to make sure that everything runs correctly:
57+
```bash
58+
mininet> h1 ping h2
59+
```
60+
You should see all packets going through.
61+
62+
3. Type `exit` to leave each Mininet command line.
63+
64+
### A note about the control plane
65+
66+
A P4 program defines a packet-processing pipeline, but the rules
67+
within each table are inserted by the control plane. When a rule
68+
matches a packet, its action is invoked with parameters supplied by
69+
the control plane as part of the rule.
70+
71+
In this exercise, we have already implemented the control plane
72+
logic for you. As part of invoking `run.sh`, a set of rules is generated
73+
by `setup.py` and when bringing up the Mininet instance, these
74+
packet-processing rules are installed in the tables of
75+
the switch. These are defined in the `simple_router.config` file.
76+
77+
## Step 2: Implement the heavy hitter detection filter
78+
79+
The `simple_router.p4` file contains a skeleton P4 program with key pieces of
80+
logic replaced by `TODO` comments. Your implementation should follow
81+
the structure given in this file, just replace each `TODO` with logic
82+
implementing the missing piece.
83+
84+
More specifically, you need to implement the main actions used within
85+
the heavy hitter detection block. In this example, when our filter
86+
classifies a packet as belonging to a heavy hitter flow, it marks
87+
it as such and then the switch drops it before reaching the
88+
egress control.
89+
90+
## Step 3: Run your solution
91+
92+
Our heavy hitter filter requires periodic reset of the registers of the
93+
count-min sketches. Running:
94+
```bash
95+
bash filter_reset.sh
96+
```
97+
in a terminal window does that periodic reset for you.
98+
99+
The filter currently allows 1000 bytes/sec (you can change that value
100+
in `setup.py`).
101+
102+
In another terminal window, run:
103+
```bash
104+
./run.sh
105+
```
106+
107+
In the minigraph window, you can try:
108+
```
109+
h1 ping -s 80 -i 0.1 h2
110+
```
111+
With this command h1, sends a packet with a total IP length
112+
of 100 bytes every 100 ms. When you run this command, you
113+
shouldn't see any drops. If on the other hand you run:
114+
```
115+
h1 ping -s 80 -i 0.05 h2
116+
```
117+
h1 sends a packet every 50 ms, which puts the flow above
118+
the filter limit. In this case you will observe that about
119+
half of the packets send by h1 are being dropped at the switch.
120+
121+
### Next steps
122+
Check out the code in `setup.py` and `filter_reset.sh`. By changing
123+
the constants in those, you can experiment with different
124+
heavy hitter threshold levels, count-min sketch sizes and the accuracy
125+
of the throughput approximation.
126+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/sh
2+
CONTAINER_ID=`docker ps | tail -n 1 | cut -d ' ' -f 1`
3+
ACTIVE_FILTER='A'
4+
5+
while true; do
6+
CUR_TIME=`echo "get_time_elapsed" | docker exec -i $CONTAINER_ID simple_switch_CLI | grep Runtime | head -n 1 | cut -d ':' -f 2`
7+
CUR_TIME=${CUR_TIME}000
8+
echo $CUR_TIME
9+
echo "register_write last_reset_time 0 $CUR_TIME" | docker exec -i $CONTAINER_ID simple_switch_CLI
10+
if [ $ACTIVE_FILTER == 'A' ] ; then
11+
echo "register_write is_a_active 0 1"
12+
echo "register_reset hashtable_b0" | docker exec -i $CONTAINER_ID simple_switch_CLI
13+
echo "register_reset hashtable_b1" | docker exec -i $CONTAINER_ID simple_switch_CLI
14+
echo "register_reset hashtable_b2" | docker exec -i $CONTAINER_ID simple_switch_CLI
15+
echo "register_reset hashtable_b3" | docker exec -i $CONTAINER_ID simple_switch_CLI
16+
ACTIVE_FILTER='B'
17+
else
18+
echo "register_write is_a_active 0 0"
19+
echo "register_reset hashtable_a0" | docker exec -i $CONTAINER_ID simple_switch_CLI
20+
echo "register_reset hashtable_a1" | docker exec -i $CONTAINER_ID simple_switch_CLI
21+
echo "register_reset hashtable_a2" | docker exec -i $CONTAINER_ID simple_switch_CLI
22+
echo "register_reset hashtable_a3" | docker exec -i $CONTAINER_ID simple_switch_CLI
23+
ACTIVE_FILTER='A'
24+
fi
25+
sleep 4
26+
done
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#ifndef __HEADER_P4__
2+
#define __HEADER_P4__ 1
3+
4+
struct ingress_metadata_t {
5+
bit<32> nhop_ipv4;
6+
}
7+
8+
header ethernet_t {
9+
bit<48> dstAddr;
10+
bit<48> srcAddr;
11+
bit<16> etherType;
12+
}
13+
14+
header ipv4_t {
15+
bit<4> version;
16+
bit<4> ihl;
17+
bit<8> diffserv;
18+
bit<16> totalLen;
19+
bit<16> identification;
20+
bit<3> flags;
21+
bit<13> fragOffset;
22+
bit<8> ttl;
23+
bit<8> protocol;
24+
bit<16> hdrChecksum;
25+
bit<32> srcAddr;
26+
bit<32> dstAddr;
27+
}
28+
29+
header tcp_t {
30+
bit<16> srcPort;
31+
bit<16> dstPort;
32+
bit<32> seqNo;
33+
bit<32> ackNo;
34+
bit<4> dataOffset;
35+
bit<4> res;
36+
bit<8> flags;
37+
bit<16> window;
38+
bit<16> checksum;
39+
bit<16> urgentPtr;
40+
}
41+
42+
header udp_t {
43+
bit<16> srcPort;
44+
bit<16> dstPort;
45+
bit<16> hdrLength;
46+
bit<16> checksum;
47+
}
48+
49+
struct hhd_t {
50+
@name("filter_age")
51+
bit<48> filter_age;
52+
bit<32> value_a0;
53+
bit<32> value_a1;
54+
bit<32> value_a2;
55+
bit<32> value_a3;
56+
bit<32> value_b0;
57+
bit<32> value_b1;
58+
bit<32> value_b2;
59+
bit<32> value_b3;
60+
bit<32> threshold;
61+
bit<1> is_a_active;
62+
bit<1> is_heavy_hitter;
63+
}
64+
65+
struct metadata {
66+
@name("ingress_metadata")
67+
ingress_metadata_t ingress_metadata;
68+
@name("hhd")
69+
hhd_t hhd;
70+
}
71+
72+
struct headers {
73+
@name("ethernet")
74+
ethernet_t ethernet;
75+
@name("ipv4")
76+
ipv4_t ipv4;
77+
@name("tcp")
78+
tcp_t tcp;
79+
@name("udp")
80+
udp_t udp;
81+
}
82+
83+
#endif // __HEADER_P4__
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"program": "simple_router.p4",
3+
"language": "p4-16",
4+
"targets": {
5+
"mininet": {
6+
"num-hosts": 2,
7+
"switch-config": "simple_router.config"
8+
}
9+
}
10+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
parser ParserImpl(packet_in packet, out headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {
2+
state start {
3+
transition parse_ethernet;
4+
}
5+
6+
state parse_ethernet {
7+
packet.extract(hdr.ethernet);
8+
transition select(hdr.ethernet.etherType) {
9+
16w0x800: parse_ipv4;
10+
default: accept;
11+
}
12+
}
13+
14+
state parse_ipv4 {
15+
packet.extract(hdr.ipv4);
16+
transition select(hdr.ipv4.protocol) {
17+
8w0x6: parse_tcp;
18+
default: accept;
19+
}
20+
}
21+
22+
state parse_tcp {
23+
packet.extract(hdr.tcp);
24+
transition accept;
25+
}
26+
}
27+
28+
control DeparserImpl(packet_out packet, in headers hdr) {
29+
apply {
30+
packet.emit(hdr.ethernet);
31+
packet.emit(hdr.ipv4);
32+
packet.emit(hdr.tcp);
33+
}
34+
}
35+
36+
control verifyChecksum(inout headers hdr, inout metadata meta) {
37+
apply { }
38+
}
39+
40+
control computeChecksum(inout headers hdr, inout metadata meta) {
41+
apply {
42+
update_checksum(
43+
hdr.ipv4.isValid(),
44+
{ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,
45+
hdr.ipv4.totalLen, hdr.ipv4.identification,
46+
hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl,
47+
hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr },
48+
hdr.ipv4.hdrChecksum,
49+
HashAlgorithm.csum16);
50+
}
51+
}

Teaching/Stanford_CS344_2018/run.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
P4APPRUNNER=../utils/p4apprunner.py
2+
python setup.py
3+
mkdir -p build
4+
tar -czf build/p4app.tgz * --exclude='build'
5+
#cd build
6+
sudo python $P4APPRUNNER p4app.tgz --build-dir ./build

Teaching/Stanford_CS344_2018/setup.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import os
2+
from shutil import copyfile
3+
4+
unit_duration = 20 # log_2 of unit duration (so 2**unit_duration)
5+
total_time_bits = 48
6+
log_units = 3 # log_2 of number of units
7+
units = 2**log_units
8+
threshold = 8*1000.0 # in bytes
9+
10+
copyfile('simple_router.config.template', 'simple_router.config')
11+
12+
with open('simple_router.config', 'a') as fd:
13+
time_mask = (2**(unit_duration+log_units)-1) - (2**unit_duration -1)
14+
for unit in range(units):
15+
time_value = unit*2**unit_duration
16+
if unit < units/2:
17+
unit_threshold = int((unit+1) * threshold / units + threshold/2 )
18+
else:
19+
unit_threshold = int((unit+1) * threshold / units)
20+
fd.write('table_add threshold_table set_threshold %d&&&%d => %d 0\n' % (time_value, time_mask, unit_threshold))
21+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
set_crc16_parameters calc_2 0x1021 0xffff 0x0000 false false
2+
set_crc32_parameters calc_0 0x4c11db7 0xffffffff 0x00000000 false false
3+
table_set_default send_frame egress_drop
4+
table_set_default forward ingress_drop
5+
table_set_default ipv4_lpm ingress_drop
6+
table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00
7+
table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:01
8+
table_add forward set_dmac 10.0.0.10 => 00:04:00:00:00:00
9+
table_add forward set_dmac 10.0.1.10 => 00:04:00:00:00:01
10+
table_add ipv4_lpm set_nhop 10.0.0.10/32 => 10.0.0.10 1
11+
table_add ipv4_lpm set_nhop 10.0.1.10/32 => 10.0.1.10 2
12+
table_add drop_heavy_hitter heavy_hitter_drop 1 0

0 commit comments

Comments
 (0)