Adding initial version of the heavy hitter detection
This commit is contained in:
parent
dd4c556be5
commit
846f059ddd
11
SIGCOMM_2016/heavy_hitter/README.md
Normal file
11
SIGCOMM_2016/heavy_hitter/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Implementing Heavy Hitter Dectection
|
||||
|
||||
## Introduction
|
||||
|
||||
In this tutorial, we want to detect the heavy hitters on the switch.
|
||||
Heavy hitters can simply be defined as the IP sources who send unusually large number of traffic.
|
||||
|
||||
|
||||
## Running the starter code
|
||||
|
||||
Simply run `./run_demo.sh`, which will fire up a mininet instance of 3 hosts and 1 switch.
|
3
SIGCOMM_2016/heavy_hitter/cleanup
Executable file
3
SIGCOMM_2016/heavy_hitter/cleanup
Executable file
@ -0,0 +1,3 @@
|
||||
sudo mn -c
|
||||
sudo killall lt-simple_switch
|
||||
sudo rm -f *.pcap
|
15
SIGCOMM_2016/heavy_hitter/commands.txt
Normal file
15
SIGCOMM_2016/heavy_hitter/commands.txt
Normal file
@ -0,0 +1,15 @@
|
||||
table_set_default send_frame _drop
|
||||
table_set_default forward _drop
|
||||
table_set_default ipv4_lpm _drop
|
||||
table_set_default set_heavy_hitter_count_table1 set_heavy_hitter_count1
|
||||
table_set_default set_heavy_hitter_count_table2 set_heavy_hitter_count2
|
||||
table_set_default set_heavy_hitter_table set_heavy_hitter
|
||||
table_add send_frame rewrite_mac 1 => 00:00:00:00:00:01
|
||||
table_add send_frame rewrite_mac 2 => 00:00:00:00:00:02
|
||||
table_add send_frame rewrite_mac 2 => 00:00:00:00:00:03
|
||||
table_add forward set_dmac 10.0.0.1 => 00:00:00:00:00:01
|
||||
table_add forward set_dmac 10.0.0.2 => 00:00:00:00:00:02
|
||||
table_add forward set_dmac 10.0.0.3 => 00:00:00:00:00:03
|
||||
table_add ipv4_lpm set_nhop 10.0.0.1/32 => 10.0.0.1 1
|
||||
table_add ipv4_lpm set_nhop 10.0.0.2/32 => 10.0.0.2 2
|
||||
table_add ipv4_lpm set_nhop 10.0.0.3/32 => 10.0.0.3 3
|
243
SIGCOMM_2016/heavy_hitter/p4src/heavy_hitter.p4
Normal file
243
SIGCOMM_2016/heavy_hitter/p4src/heavy_hitter.p4
Normal file
@ -0,0 +1,243 @@
|
||||
/* Copyright 2013-present Barefoot Networks, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define HEAVY_HITTER_THRESHOLD 1000
|
||||
|
||||
header_type ethernet_t {
|
||||
fields {
|
||||
dstAddr : 48;
|
||||
srcAddr : 48;
|
||||
etherType : 16;
|
||||
}
|
||||
}
|
||||
|
||||
header_type ipv4_t {
|
||||
fields {
|
||||
version : 4;
|
||||
ihl : 4;
|
||||
diffserv : 8;
|
||||
totalLen : 16;
|
||||
identification : 16;
|
||||
flags : 3;
|
||||
fragOffset : 13;
|
||||
ttl : 8;
|
||||
protocol : 8;
|
||||
hdrChecksum : 16;
|
||||
srcAddr : 32;
|
||||
dstAddr: 32;
|
||||
}
|
||||
}
|
||||
|
||||
parser start {
|
||||
return parse_ethernet;
|
||||
}
|
||||
|
||||
#define ETHERTYPE_IPV4 0x0800
|
||||
|
||||
header ethernet_t ethernet;
|
||||
|
||||
parser parse_ethernet {
|
||||
extract(ethernet);
|
||||
return select(latest.etherType) {
|
||||
ETHERTYPE_IPV4 : parse_ipv4;
|
||||
default: ingress;
|
||||
}
|
||||
}
|
||||
|
||||
header ipv4_t ipv4;
|
||||
|
||||
field_list ipv4_checksum_list {
|
||||
ipv4.version;
|
||||
ipv4.ihl;
|
||||
ipv4.diffserv;
|
||||
ipv4.totalLen;
|
||||
ipv4.identification;
|
||||
ipv4.flags;
|
||||
ipv4.fragOffset;
|
||||
ipv4.ttl;
|
||||
ipv4.protocol;
|
||||
ipv4.srcAddr;
|
||||
ipv4.dstAddr;
|
||||
}
|
||||
|
||||
field_list_calculation ipv4_checksum {
|
||||
input {
|
||||
ipv4_checksum_list;
|
||||
}
|
||||
algorithm : csum16;
|
||||
output_width : 16;
|
||||
}
|
||||
|
||||
calculated_field ipv4.hdrChecksum {
|
||||
verify ipv4_checksum;
|
||||
update ipv4_checksum;
|
||||
}
|
||||
|
||||
parser parse_ipv4 {
|
||||
extract(ipv4);
|
||||
return ingress;
|
||||
}
|
||||
|
||||
action _drop() {
|
||||
drop();
|
||||
}
|
||||
|
||||
header_type custom_metadata_t {
|
||||
fields {
|
||||
nhop_ipv4: 32;
|
||||
// Add metadata for hashes
|
||||
hash_val1: 16;
|
||||
hash_val2: 16;
|
||||
count_val1: 16;
|
||||
count_val2: 16;
|
||||
is_heavy_hitter: 8;
|
||||
}
|
||||
}
|
||||
|
||||
metadata custom_metadata_t custom_metadata;
|
||||
|
||||
action set_nhop(nhop_ipv4, port) {
|
||||
modify_field(custom_metadata.nhop_ipv4, nhop_ipv4);
|
||||
modify_field(standard_metadata.egress_spec, port);
|
||||
add_to_field(ipv4.ttl, -1);
|
||||
}
|
||||
|
||||
action set_dmac(dmac) {
|
||||
modify_field(ethernet.dstAddr, dmac);
|
||||
}
|
||||
|
||||
// Define the field list to compute the hash on
|
||||
field_list ipv4_hash_fields {
|
||||
ipv4.srcAddr;
|
||||
}
|
||||
|
||||
// Define two different hash functions to store the counts
|
||||
field_list_calculation heavy_hitter_hash1 {
|
||||
input {
|
||||
ipv4_hash_fields;
|
||||
}
|
||||
algorithm : csum16;
|
||||
output_width : 16;
|
||||
}
|
||||
|
||||
field_list_calculation heavy_hitter_hash2 {
|
||||
input {
|
||||
ipv4_hash_fields;
|
||||
}
|
||||
algorithm : crc16;
|
||||
output_width : 16;
|
||||
}
|
||||
|
||||
// Define the registers to store the counts
|
||||
register heavy_hitter_counter1{
|
||||
width : 16;
|
||||
instance_count : 16;
|
||||
}
|
||||
|
||||
register heavy_hitter_counter2{
|
||||
width : 16;
|
||||
instance_count : 16;
|
||||
}
|
||||
|
||||
// Actions to set heavy hitter filter
|
||||
action set_heavy_hitter_count1() {
|
||||
modify_field_with_hash_based_offset(custom_metadata.hash_val1, 0,
|
||||
heavy_hitter_hash1, 16);
|
||||
register_read(custom_metadata.count_val1, heavy_hitter_counter1, custom_metadata.hash_val1);
|
||||
add_to_field(custom_metadata.count_val1, 1);
|
||||
register_write(heavy_hitter_counter1, custom_metadata.hash_val1, custom_metadata.count_val1);
|
||||
}
|
||||
|
||||
action set_heavy_hitter_count2() {
|
||||
modify_field_with_hash_based_offset(custom_metadata.hash_val2, 0,
|
||||
heavy_hitter_hash2, 16);
|
||||
register_read(custom_metadata.count_val2, heavy_hitter_counter2, custom_metadata.hash_val2);
|
||||
add_to_field(custom_metadata.count_val2, 1);
|
||||
register_write(heavy_hitter_counter2, custom_metadata.hash_val2, custom_metadata.count_val2);
|
||||
}
|
||||
|
||||
// Action to set the heavy hitter metadata indicator
|
||||
action set_heavy_hitter() {
|
||||
modify_field(custom_metadata.is_heavy_hitter, 1);
|
||||
}
|
||||
|
||||
// Define the tables to run actions
|
||||
table set_heavy_hitter_count_table1 {
|
||||
actions { set_heavy_hitter_count1; }
|
||||
size: 1;
|
||||
}
|
||||
|
||||
table set_heavy_hitter_count_table2 {
|
||||
actions { set_heavy_hitter_count2; }
|
||||
size: 1;
|
||||
}
|
||||
|
||||
// Define table to set the heavy hitter metadata
|
||||
table set_heavy_hitter_table {
|
||||
actions { set_heavy_hitter; }
|
||||
size: 1;
|
||||
}
|
||||
|
||||
table ipv4_lpm {
|
||||
reads {
|
||||
ipv4.dstAddr : lpm;
|
||||
}
|
||||
actions {
|
||||
set_nhop;
|
||||
_drop;
|
||||
}
|
||||
size: 1024;
|
||||
}
|
||||
|
||||
table forward {
|
||||
reads {
|
||||
custom_metadata.nhop_ipv4 : exact;
|
||||
}
|
||||
actions {
|
||||
set_dmac;
|
||||
_drop;
|
||||
}
|
||||
size: 512;
|
||||
}
|
||||
|
||||
action rewrite_mac(smac) {
|
||||
modify_field(ethernet.srcAddr, smac);
|
||||
}
|
||||
|
||||
table send_frame {
|
||||
reads {
|
||||
standard_metadata.egress_port: exact;
|
||||
}
|
||||
actions {
|
||||
rewrite_mac;
|
||||
_drop;
|
||||
}
|
||||
size: 256;
|
||||
}
|
||||
|
||||
control ingress {
|
||||
apply(ipv4_lpm);
|
||||
// Add table control here
|
||||
apply(set_heavy_hitter_count_table1);
|
||||
apply(set_heavy_hitter_count_table2);
|
||||
if (custom_metadata.count_val1 > 1000 and custom_metadata.count_val2 > HEAVY_HITTER_THRESHOLD) {
|
||||
apply(set_heavy_hitter_table);
|
||||
}
|
||||
apply(forward);
|
||||
}
|
||||
|
||||
control egress {
|
||||
apply(send_frame);
|
||||
}
|
34
SIGCOMM_2016/heavy_hitter/receive.py
Executable file
34
SIGCOMM_2016/heavy_hitter/receive.py
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright 2013-present Barefoot Networks, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from scapy.all import sniff, sendp
|
||||
from scapy.all import Packet
|
||||
from scapy.all import ShortField, IntField, LongField, BitField
|
||||
|
||||
import sys
|
||||
import struct
|
||||
|
||||
def handle_pkt(pkt):
|
||||
pkt = str(pkt)
|
||||
print pkt
|
||||
sys.stdout.flush()
|
||||
|
||||
def main():
|
||||
sniff(iface = "eth0",
|
||||
prn = lambda x: handle_pkt(x))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
33
SIGCOMM_2016/heavy_hitter/run_demo.sh
Executable file
33
SIGCOMM_2016/heavy_hitter/run_demo.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2013-present Barefoot Networks, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
THIS_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
source $THIS_DIR/../../env.sh
|
||||
|
||||
P4C_BM_SCRIPT=$P4C_BM_PATH/p4c_bm/__main__.py
|
||||
|
||||
SWITCH_PATH=$BMV2_PATH/targets/simple_switch/simple_switch
|
||||
|
||||
CLI_PATH=$BMV2_PATH/tools/runtime_CLI.py
|
||||
|
||||
$P4C_BM_SCRIPT p4src/heavy_hitter.p4 --json heavy_hitter.json
|
||||
# This gives libtool the opportunity to "warm-up"
|
||||
sudo $SWITCH_PATH >/dev/null 2>&1
|
||||
sudo PYTHONPATH=$PYTHONPATH:$BMV2_PATH/mininet/ python topo.py \
|
||||
--behavioral-exe $SWITCH_PATH \
|
||||
--json heavy_hitter.json \
|
||||
--cli $CLI_PATH
|
47
SIGCOMM_2016/heavy_hitter/send.py
Executable file
47
SIGCOMM_2016/heavy_hitter/send.py
Executable file
@ -0,0 +1,47 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright 2013-present Barefoot Networks, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from scapy.all import Ether, IP, sendp
|
||||
|
||||
import networkx as nx
|
||||
|
||||
import sys
|
||||
|
||||
def read_topo():
|
||||
nb_hosts = 0
|
||||
nb_switches = 0
|
||||
links = []
|
||||
with open("topo.txt", "r") as f:
|
||||
line = f.readline()[:-1]
|
||||
w, nb_switches = line.split()
|
||||
assert(w == "switches")
|
||||
line = f.readline()[:-1]
|
||||
w, nb_hosts = line.split()
|
||||
assert(w == "hosts")
|
||||
for line in f:
|
||||
if not f: break
|
||||
a, b = line.split()
|
||||
links.append( (a, b) )
|
||||
return int(nb_hosts), int(nb_switches), links
|
||||
|
||||
def main():
|
||||
for i in range(1):
|
||||
p = Ether(dst="00:00:00:00:00:02")/IP(dst="10.0.0.2")
|
||||
print p.show()
|
||||
sendp(p, iface = "eth0")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
129
SIGCOMM_2016/heavy_hitter/topo.py
Normal file
129
SIGCOMM_2016/heavy_hitter/topo.py
Normal file
@ -0,0 +1,129 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright 2013-present Barefoot Networks, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.topo import Topo
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.cli import CLI
|
||||
|
||||
from p4_mininet import P4Switch, P4Host
|
||||
|
||||
import argparse
|
||||
from time import sleep
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
_THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
_THRIFT_BASE_PORT = 22222
|
||||
|
||||
parser = argparse.ArgumentParser(description='Mininet demo')
|
||||
parser.add_argument('--behavioral-exe', help='Path to behavioral executable',
|
||||
type=str, action="store", required=True)
|
||||
parser.add_argument('--json', help='Path to JSON config file',
|
||||
type=str, action="store", required=True)
|
||||
parser.add_argument('--cli', help='Path to BM CLI',
|
||||
type=str, action="store", required=True)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
class MyTopo(Topo):
|
||||
def __init__(self, sw_path, json_path, nb_hosts, nb_switches, links, **opts):
|
||||
# Initialize topology and default options
|
||||
Topo.__init__(self, **opts)
|
||||
|
||||
for i in xrange(nb_switches):
|
||||
self.addSwitch('s%d' % (i + 1),
|
||||
sw_path = sw_path,
|
||||
json_path = json_path,
|
||||
thrift_port = _THRIFT_BASE_PORT + i,
|
||||
pcap_dump = True,
|
||||
device_id = i)
|
||||
|
||||
for h in xrange(nb_hosts):
|
||||
self.addHost('h%d' % (h + 1), ip="10.0.0.%d" % (h + 1),
|
||||
mac="00:00:00:00:00:0%d" % (h+1))
|
||||
|
||||
for a, b in links:
|
||||
self.addLink(a, b)
|
||||
|
||||
def read_topo():
|
||||
nb_hosts = 0
|
||||
nb_switches = 0
|
||||
links = []
|
||||
with open("topo.txt", "r") as f:
|
||||
line = f.readline()[:-1]
|
||||
w, nb_switches = line.split()
|
||||
assert(w == "switches")
|
||||
line = f.readline()[:-1]
|
||||
w, nb_hosts = line.split()
|
||||
assert(w == "hosts")
|
||||
for line in f:
|
||||
if not f: break
|
||||
a, b = line.split()
|
||||
links.append( (a, b) )
|
||||
return int(nb_hosts), int(nb_switches), links
|
||||
|
||||
|
||||
def main():
|
||||
nb_hosts, nb_switches, links = read_topo()
|
||||
|
||||
topo = MyTopo(args.behavioral_exe,
|
||||
args.json,
|
||||
nb_hosts, nb_switches, links)
|
||||
|
||||
net = Mininet(topo = topo,
|
||||
host = P4Host,
|
||||
switch = P4Switch,
|
||||
controller = None )
|
||||
net.start()
|
||||
|
||||
for n in xrange(nb_hosts):
|
||||
h = net.get('h%d' % (n + 1))
|
||||
for off in ["rx", "tx", "sg"]:
|
||||
cmd = "/sbin/ethtool --offload eth0 %s off" % off
|
||||
print cmd
|
||||
h.cmd(cmd)
|
||||
print "disable ipv6"
|
||||
h.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
|
||||
h.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")
|
||||
h.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")
|
||||
h.cmd("sysctl -w net.ipv4.tcp_congestion_control=reno")
|
||||
h.cmd("iptables -I OUTPUT -p icmp --icmp-type destination-unreachable -j DROP")
|
||||
|
||||
sleep(1)
|
||||
|
||||
for i in xrange(nb_switches):
|
||||
cmd = [args.cli, "--json", args.json,
|
||||
"--thrift-port", str(_THRIFT_BASE_PORT + i)]
|
||||
with open("commands.txt", "r") as f:
|
||||
print " ".join(cmd)
|
||||
try:
|
||||
output = subprocess.check_output(cmd, stdin = f)
|
||||
print output
|
||||
except subprocess.CalledProcessError as e:
|
||||
print e
|
||||
print e.output
|
||||
|
||||
sleep(1)
|
||||
|
||||
print "Ready !"
|
||||
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'info' )
|
||||
main()
|
5
SIGCOMM_2016/heavy_hitter/topo.txt
Normal file
5
SIGCOMM_2016/heavy_hitter/topo.txt
Normal file
@ -0,0 +1,5 @@
|
||||
switches 1
|
||||
hosts 3
|
||||
h1 s1
|
||||
h2 s1
|
||||
h3 s1
|
Loading…
x
Reference in New Issue
Block a user