├── .gitignore ├── README ├── cms_result ├── drop6.png ├── no drop.png └── test2_pcap.png ├── compile_bmv2.sh ├── concept ├── PoC.bmp └── poster-p4.png ├── env.sh ├── monitor ├── headers.p4 ├── intrinsic.p4 ├── main.p4 ├── parser.p4 └── tables.p4 ├── monitor_cmd.txt ├── network.py ├── p4_mininet.py ├── p4_mininet.pyc ├── receiver.py ├── send_cmd.sh ├── token ├── headers.p4 ├── main.p4 ├── parser.p4 └── tables.p4 ├── token_cmd.txt ├── verifier ├── headers.p4 ├── main.p4 ├── parser.p4 └── tables.p4 └── verifier_cmd.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Preinstall 2 | -------- 3 | p4 4 | 5 | 6 | Run 7 | -------- 8 | $ ./compile_bmv2.sh 9 | $ sudo ./network.py 10 | $ ./send_cmd.sh 11 | 12 | mininet> xterm h1 h2 13 | 14 | (h1)$ ./sender.py 15 | (h2)$ ./receiver.py 16 | 17 | 18 | Clear Env 19 | -------- 20 | $ sudo mn --clean 21 | 22 | 23 | Description 24 | -------- 25 | Monitor 26 | * Traffic monitor(count min sketch with 3 hashes) 27 | * IP 3 tuple as key 28 | * Constant threshold for heavy hitter detection 29 | * Timestamp window to exclude too old counts 30 | * Split flow to Different proxy according to client IP 31 | 32 | Proxy 33 | * 2^n proxy to share redirect(TODO) load (used 2 for PoC) 34 | * Redirect and send back client packets 35 | 36 | Token 37 | * Add token between IP header and TCP header 38 | * Currently, token is fixed. Real token, for example, should be generated by RNG with keys shared by "token switch" and "verifier switch" 39 | 40 | Verifier(P4 switch) 41 | * Check token 42 | * Drop packet if invalid 43 | 44 | Support bi-direction 45 | * client <-> Proxy <-> Server 46 | 47 | Packet handle 48 | * Currently, only handle IPV4 and ARP packets. Others are ignored 49 | 50 | 51 | Topology 52 | -------- 53 | See concept/ 54 | 55 | 56 | Testing 57 | -------- 58 | [Connectivity] 59 | * Test1: Client to Proxy 60 | c0 ping/nc h11(eth0) 61 | c1 ping/nc h12(eth0) 62 | c2 ping/nc h11(eth0) 63 | c0 and c2 would be directed to h11, so c0 and c2 can only ping/nc to h11(through eth0). It's the same for c1 to h12. 64 | 65 | * Proxy to Server 66 | h11(eth1) ping/nc h2 67 | h12(eth1) ping/nc h2 68 | 69 | * Attacker 70 | c3 ping/nc h2 71 | Packets from c3 would be dropped since they don't have token. 72 | 73 | [Monitor] 74 | c0 ping/nc h11(eth0) 75 | c1, c2 can also be used in this test 76 | If there are too many packets sended to monitor, heavy hitters would be dropped. Count's Arrival time previous than current timestamp window would be reset, so heavy hitter will be consider as non heavy if it stops sending packets for a while. 77 | 78 | -------------------------------------------------------------------------------- /cms_result/drop6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiwang123/HappyFlowFriends/35e75b05155a421dd85a91469ea88f1612b8b23e/cms_result/drop6.png -------------------------------------------------------------------------------- /cms_result/no drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiwang123/HappyFlowFriends/35e75b05155a421dd85a91469ea88f1612b8b23e/cms_result/no drop.png -------------------------------------------------------------------------------- /cms_result/test2_pcap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiwang123/HappyFlowFriends/35e75b05155a421dd85a91469ea88f1612b8b23e/cms_result/test2_pcap.png -------------------------------------------------------------------------------- /compile_bmv2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source env.sh 3 | COMPILER=$P4C_BM_PATH/p4c_bm/__main__.py 4 | 5 | if [ -f "monitor.json" ]; then 6 | rm monitor.json 7 | fi 8 | 9 | if [ -f "token.json" ]; then 10 | rm token.json 11 | fi 12 | 13 | if [ -f "verifier.json" ]; then 14 | rm router.json 15 | fi 16 | 17 | $COMPILER --json monitor.json monitor/main.p4 18 | $COMPILER --json token.json token/main.p4 19 | $COMPILER --json verifier.json verifier/main.p4 20 | -------------------------------------------------------------------------------- /concept/PoC.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiwang123/HappyFlowFriends/35e75b05155a421dd85a91469ea88f1612b8b23e/concept/PoC.bmp -------------------------------------------------------------------------------- /concept/poster-p4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiwang123/HappyFlowFriends/35e75b05155a421dd85a91469ea88f1612b8b23e/concept/poster-p4.png -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | # edit your env here! 2 | P4C_BM_PATH=~/p4/p4c-bm 3 | BMV2_PATH=~/p4/behavioral-model 4 | -------------------------------------------------------------------------------- /monitor/headers.p4: -------------------------------------------------------------------------------- 1 | header_type ethernet_t { 2 | fields { 3 | dstAddr : 48; 4 | srcAddr : 48; 5 | etherType : 16; 6 | } 7 | } 8 | 9 | header_type arp_ipv4_t { 10 | fields { 11 | ignore: 64; 12 | src_mac : 48; 13 | src_ip : 32; 14 | dst_mac : 48; 15 | dst_ip : 32; 16 | } 17 | } 18 | 19 | header_type ipv4_t { 20 | fields { 21 | version : 4; 22 | ihl : 4; 23 | diffserv : 8; 24 | totalLen : 16; 25 | identification : 16; 26 | flags : 3; 27 | fragOffset : 13; 28 | ttl : 8; 29 | protocol : 8; 30 | hdrChecksum : 16; 31 | srcAddr : 32; 32 | dstAddr: 32; 33 | } 34 | } 35 | 36 | header ethernet_t ethernet; 37 | header ipv4_t ipv4; 38 | header arp_ipv4_t arp_ipv4; 39 | 40 | header_type my_metadata_t { 41 | fields { 42 | hash_val0: 16; 43 | hash_val1: 16; 44 | hash_val2: 16; 45 | count_val0: 16; 46 | count_val1: 16; 47 | count_val2: 16; 48 | timestamp0: 48; 49 | timestamp1: 48; 50 | timestamp2: 48; 51 | } 52 | } 53 | 54 | header my_metadata_t my_metadata; 55 | 56 | register heavy_hitter_register0 { 57 | width : 16; 58 | instance_count : 65536; 59 | } 60 | 61 | register heavy_hitter_register1 { 62 | width : 16; 63 | instance_count : 65536; 64 | } 65 | 66 | register heavy_hitter_register2 { 67 | width : 16; 68 | instance_count : 65536; 69 | } 70 | 71 | register timestamp_register0 { 72 | width : 48; 73 | instance_count : 65536; 74 | } 75 | 76 | register timestamp_register1 { 77 | width : 48; 78 | instance_count : 65536; 79 | } 80 | 81 | register timestamp_register2 { 82 | width : 48; 83 | instance_count : 65536; 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /monitor/intrinsic.p4: -------------------------------------------------------------------------------- 1 | header_type intrinsic_metadata_t { 2 | fields { 3 | ingress_global_timestamp : 32; 4 | lf_field_list : 8; 5 | mcast_grp : 16; 6 | egress_rid : 16; 7 | resubmit_flag : 8; 8 | recirculate_flag : 8; 9 | } 10 | } 11 | 12 | metadata intrinsic_metadata_t intrinsic_metadata; 13 | -------------------------------------------------------------------------------- /monitor/main.p4: -------------------------------------------------------------------------------- 1 | #include "tables.p4" 2 | 3 | //microsecond, 3s 4 | #define timestamp_window_max 3000000 5 | 6 | control ingress { 7 | 8 | if (standard_metadata.ingress_port != 1) { // from proxy to client 9 | apply(table_forward_back); 10 | } else { // from client to proxy 11 | if (ethernet.etherType == ETHERTYPE_IPV4) { // ipv4 12 | // monitor 13 | 14 | // init 15 | apply(table_count_min_sketch_init); 16 | 17 | // timestamp 18 | apply(table_get_last_timestamp); 19 | if(my_metadata.timestamp0 + timestamp_window_max < intrinsic_metadata.ingress_global_timestamp){ 20 | apply(table_register0_reset); 21 | } 22 | if(my_metadata.timestamp1 + timestamp_window_max < intrinsic_metadata.ingress_global_timestamp){ 23 | apply(table_register1_reset); 24 | } 25 | if(my_metadata.timestamp2 + timestamp_window_max < intrinsic_metadata.ingress_global_timestamp){ 26 | apply(table_register2_reset); 27 | } 28 | apply(table_update_timestamp); 29 | 30 | // process 31 | apply(table_count_min_sketch_incr); 32 | if(my_metadata.count_val0 > heavy_hitter_max and 33 | my_metadata.count_val1 > heavy_hitter_max and 34 | my_metadata.count_val2 > heavy_hitter_max) { 35 | apply(table_count_min_sketch_decr); 36 | apply(table_drop); 37 | } else{ 38 | apply(table_forward_ahead_ipv4); 39 | } 40 | } else if (ethernet.etherType == ETHERTYPE_ARP_IPV4) { // arp 41 | apply(table_forward_ahead_arp_ipv4); 42 | } else { // for now, don't care 43 | apply(table_drop); 44 | } 45 | } 46 | } 47 | 48 | control egress { 49 | } 50 | -------------------------------------------------------------------------------- /monitor/parser.p4: -------------------------------------------------------------------------------- 1 | #include "headers.p4" 2 | #include "intrinsic.p4" 3 | 4 | parser start { 5 | return parse_ethernet; 6 | } 7 | 8 | 9 | #define ETHERTYPE_IPV4 0x0800 10 | #define ETHERTYPE_ARP_IPV4 0x0806 11 | 12 | parser parse_ethernet { 13 | extract(ethernet); 14 | return select(ethernet.etherType) { 15 | ETHERTYPE_IPV4 : parse_ipv4; 16 | ETHERTYPE_ARP_IPV4 : parse_arp_ipv4; 17 | default: ingress; 18 | } 19 | } 20 | 21 | parser parse_ipv4 { 22 | extract(ipv4); 23 | return ingress; 24 | } 25 | 26 | parser parse_arp_ipv4 { 27 | extract(arp_ipv4); 28 | return ingress; 29 | } 30 | 31 | field_list hash_fields { // ip 3 tuple 32 | ipv4.srcAddr; 33 | ipv4.dstAddr; 34 | ipv4.protocol; 35 | } 36 | 37 | field_list_calculation heavy_hitter_hash0 { // hash function 0 38 | input { 39 | hash_fields; 40 | } 41 | algorithm : csum16; 42 | output_width : 16; 43 | } 44 | 45 | field_list_calculation heavy_hitter_hash1 { // hash function 1 46 | input { 47 | hash_fields; 48 | } 49 | algorithm : crc16; 50 | output_width : 16; 51 | } 52 | 53 | field_list_calculation heavy_hitter_hash2 { // hash function 2 54 | input { 55 | hash_fields; 56 | } 57 | algorithm : bmv2_hash; 58 | output_width : 16; 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /monitor/tables.p4: -------------------------------------------------------------------------------- 1 | #include "parser.p4" 2 | 3 | action forward_back() { 4 | modify_field(standard_metadata.egress_spec, 1); 5 | } 6 | 7 | 8 | table table_forward_back { 9 | actions { 10 | forward_back; 11 | } 12 | 13 | } 14 | 15 | action forward_ahead_ipv4() { 16 | // host num is 2^n, modify egress port 17 | modify_field(standard_metadata.egress_spec, (ipv4.srcAddr&1) + 2); 18 | } 19 | 20 | 21 | table table_forward_ahead_ipv4 { 22 | actions { 23 | forward_ahead_ipv4; 24 | 25 | } 26 | 27 | } 28 | 29 | action forward_ahead_arp_ipv4() { 30 | // host num is 2^n, modify egress port 31 | modify_field(standard_metadata.egress_spec, (arp_ipv4.dst_ip&1) + 2); 32 | } 33 | 34 | 35 | table table_forward_ahead_arp_ipv4 { 36 | actions { 37 | forward_ahead_arp_ipv4; 38 | 39 | } 40 | 41 | } 42 | 43 | action action_drop() { 44 | drop(); 45 | } 46 | 47 | table table_drop { 48 | actions { 49 | action_drop; 50 | } 51 | } 52 | 53 | #define heavy_hitter_max 6 54 | 55 | // count min sketch init 56 | action action_get_hash_val() { 57 | modify_field_with_hash_based_offset(my_metadata.hash_val0, 0, heavy_hitter_hash0, 65536); 58 | modify_field_with_hash_based_offset(my_metadata.hash_val1, 0, heavy_hitter_hash1, 65536); 59 | modify_field_with_hash_based_offset(my_metadata.hash_val2, 0, heavy_hitter_hash2, 65536); 60 | } 61 | 62 | table table_count_min_sketch_init { 63 | actions { 64 | action_get_hash_val; 65 | } 66 | } 67 | 68 | // count min sketch increase count 69 | action action_count_min_sketch_incr() { 70 | register_read(my_metadata.count_val0, heavy_hitter_register0, my_metadata.hash_val0); 71 | register_read(my_metadata.count_val1, heavy_hitter_register1, my_metadata.hash_val1); 72 | register_read(my_metadata.count_val2, heavy_hitter_register2, my_metadata.hash_val2); 73 | 74 | add_to_field(my_metadata.count_val0, 1); 75 | add_to_field(my_metadata.count_val1, 1); 76 | add_to_field(my_metadata.count_val2, 1); 77 | 78 | register_write(heavy_hitter_register0, my_metadata.hash_val0, my_metadata.count_val0); 79 | register_write(heavy_hitter_register1, my_metadata.hash_val1, my_metadata.count_val1); 80 | register_write(heavy_hitter_register2, my_metadata.hash_val2, my_metadata.count_val2); 81 | } 82 | 83 | table table_count_min_sketch_incr{ 84 | actions { 85 | action_count_min_sketch_incr; 86 | } 87 | } 88 | 89 | 90 | // count min sketch decrease count 91 | action action_count_min_sketch_decr() { 92 | register_read(my_metadata.count_val0, heavy_hitter_register0, my_metadata.hash_val0); 93 | register_read(my_metadata.count_val1, heavy_hitter_register1, my_metadata.hash_val1); 94 | register_read(my_metadata.count_val2, heavy_hitter_register2, my_metadata.hash_val2); 95 | 96 | modify_field(my_metadata.count_val0, heavy_hitter_max - 1); 97 | modify_field(my_metadata.count_val1, heavy_hitter_max - 1); 98 | modify_field(my_metadata.count_val2, heavy_hitter_max - 1); 99 | 100 | register_write(heavy_hitter_register0, my_metadata.hash_val0, my_metadata.count_val0); 101 | register_write(heavy_hitter_register1, my_metadata.hash_val1, my_metadata.count_val1); 102 | register_write(heavy_hitter_register2, my_metadata.hash_val2, my_metadata.count_val2); 103 | } 104 | 105 | table table_count_min_sketch_decr{ 106 | actions { 107 | action_count_min_sketch_decr; 108 | } 109 | } 110 | 111 | //timestamp 112 | action action_get_last_timestamp() { 113 | register_read(my_metadata.timestamp0, timestamp_register0, my_metadata.hash_val0); 114 | register_read(my_metadata.timestamp1, timestamp_register1, my_metadata.hash_val1); 115 | register_read(my_metadata.timestamp2, timestamp_register2, my_metadata.hash_val2); 116 | } 117 | 118 | table table_get_last_timestamp { 119 | actions { 120 | action_get_last_timestamp; 121 | } 122 | } 123 | 124 | action action_update_timestamp() { 125 | register_write(timestamp_register0, my_metadata.hash_val0, intrinsic_metadata.ingress_global_timestamp); 126 | register_write(timestamp_register1, my_metadata.hash_val1, intrinsic_metadata.ingress_global_timestamp); 127 | register_write(timestamp_register2, my_metadata.hash_val2, intrinsic_metadata.ingress_global_timestamp); 128 | } 129 | 130 | table table_update_timestamp { 131 | actions { 132 | action_update_timestamp; 133 | } 134 | } 135 | 136 | //heavy hitter register reset 137 | 138 | action action_register0_reset() { 139 | register_write(heavy_hitter_register0, my_metadata.hash_val0, 0); 140 | } 141 | 142 | table table_register0_reset { 143 | actions { 144 | action_register0_reset; 145 | } 146 | } 147 | 148 | action action_register1_reset() { 149 | register_write(heavy_hitter_register1, my_metadata.hash_val1, 0); 150 | } 151 | 152 | table table_register1_reset { 153 | actions { 154 | action_register1_reset; 155 | } 156 | } 157 | 158 | action action_register2_reset() { 159 | register_write(heavy_hitter_register2, my_metadata.hash_val2, 0); 160 | } 161 | 162 | table table_register2_reset { 163 | actions { 164 | action_register2_reset; 165 | } 166 | } 167 | 168 | -------------------------------------------------------------------------------- /monitor_cmd.txt: -------------------------------------------------------------------------------- 1 | table_set_default table_forward_back forward_back 2 | table_set_default table_forward_ahead_ipv4 forward_ahead_ipv4 3 | table_set_default table_forward_ahead_arp_ipv4 forward_ahead_arp_ipv4 4 | table_set_default table_drop action_drop 5 | table_set_default table_count_min_sketch_init action_get_hash_val 6 | table_set_default table_count_min_sketch_incr action_count_min_sketch_incr 7 | table_set_default table_count_min_sketch_decr action_count_min_sketch_decr 8 | table_set_default table_get_last_timestamp action_get_last_timestamp 9 | table_set_default table_update_timestamp action_update_timestamp 10 | table_set_default table_register0_reset action_register0_reset 11 | table_set_default table_register1_reset action_register1_reset 12 | table_set_default table_register2_reset action_register2_reset 13 | -------------------------------------------------------------------------------- /network.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from mininet.topo import Topo 3 | from mininet.net import Mininet, VERSION 4 | from mininet.node import Node 5 | from mininet.log import setLogLevel, info, debug 6 | from mininet.cli import CLI 7 | from distutils.version import StrictVersion 8 | from p4_mininet import P4Switch, P4Host 9 | from time import sleep 10 | import sys 11 | import os 12 | 13 | # config path here 14 | SW_PATH='/home/hiwang123/p4/behavioral-model/targets/simple_switch/simple_switch' 15 | MONITOR_JSON_PATH='monitor.json' 16 | TOKEN_JSON_PATH='token.json' 17 | VERIFIER_JSON_PATH='verifier.json' 18 | 19 | class LinuxRouter( Node ): 20 | "A Node with IP forwarding enabled." 21 | 22 | def config( self, **params ): 23 | super( LinuxRouter, self).config( **params ) 24 | # Enable forwarding on the router 25 | self.cmd( 'sysctl net.ipv4.ip_forward=1' ) 26 | 27 | def terminate( self ): 28 | self.cmd( 'sysctl net.ipv4.ip_forward=0' ) 29 | super( LinuxRouter, self ).terminate() 30 | 31 | class NetworkTopo( Topo ): 32 | 33 | def build(self, **_opts): 34 | 35 | defaultIP0 = '192.168.1.1/24' 36 | R0 = self.addNode( 'r0', cls=LinuxRouter, ip=defaultIP0 ) 37 | h0 = self.addHost('h0', ip='192.168.1.100/24', defaultRoute='via 192.168.1.1') # for clarity 38 | self.addLink(h0, R0, intfName2='r0-eth0', params2={ 'ip' : defaultIP0 }) 39 | 40 | defaultIP1 = '172.16.1.1/24' 41 | R1 = self.addNode( 'r1', cls=LinuxRouter, ip=defaultIP1 ) 42 | s4 = self.addSwitch('s4') 43 | c0 = self.addHost('c0', ip='172.16.1.100/24', defaultRoute='via 172.16.1.1') 44 | c1 = self.addHost('c1', ip='172.16.1.101/24', defaultRoute='via 172.16.1.1') 45 | c2 = self.addHost('c2', ip='172.16.1.102/24', defaultRoute='via 172.16.1.1') 46 | self.addLink(s4, c0) 47 | self.addLink(s4, c1) 48 | self.addLink(s4, c2) 49 | self.addLink(s4, R1, intfName2='r1-eth0', params2={ 'ip' : defaultIP1 }) 50 | 51 | h11 = self.addHost('h11') 52 | h12 = self.addHost('h12') 53 | h2 = self.addHost('h2', ip='10.2.0.100/24', defaultRoute='via 10.2.0.1') 54 | c3 = self.addHost('c3', ip='10.3.0.100/24', defaultRoute='via 10.3.0.1') 55 | 56 | s0 = self.addSwitch('s0', cls = P4Switch, sw_path=SW_PATH, json_path=MONITOR_JSON_PATH, thrift_port=9090) 57 | s1 = self.addSwitch('s1', cls = P4Switch, sw_path=SW_PATH, json_path=TOKEN_JSON_PATH, thrift_port=9091) 58 | s2 = self.addSwitch('s2', cls = P4Switch, sw_path=SW_PATH, json_path=VERIFIER_JSON_PATH, thrift_port=9092) 59 | s3 = self.addSwitch('s3') 60 | 61 | self.addLink(s1, h11, port1=2, intfName2='h11-eth1') 62 | self.addLink(s1, h12, port1=3, intfName2='h12-eth1') 63 | self.addLink(s2, h2, port1=1) 64 | self.addLink(s3, c3) 65 | self.addLink(s0, h11, port1=2, intfName2='h11-eth0') 66 | self.addLink(s0, h12, port1=3, intfName2='h12-eth0') 67 | 68 | self.addLink(s1, R0, port1=1, intfName2='r0-eth1', params2={ 'ip' : '10.1.0.1/24' }) 69 | self.addLink(s2, R0, port1=2, intfName2='r0-eth2', params2={ 'ip' : '10.2.0.1/24' }) 70 | self.addLink(s3, R0, intfName2='r0-eth3', params2={ 'ip' : '10.3.0.1/24' }) 71 | self.addLink(s0, R1, port1=1, intfName2='r1-eth1', params2={ 'ip' : '10.4.0.1/24' }) 72 | 73 | 74 | 75 | 76 | def toe_options(h, iface): 77 | toe_options=['rx','tx','sg','tso','ufo','gso','gro','rxvlan','txvlan'] 78 | for option in toe_options: 79 | info( net[h].cmd( '/sbin/ethtool --offload '+ iface +' '+option+' off' ) ) 80 | 81 | def host_setting(h, iface1, iface2, ip1, ip2, gw1, gw2,target): 82 | info( net[h].cmd( 'ifconfig '+iface1+' '+ip1 ) ) 83 | info( net[h].cmd( 'ifconfig '+iface2+' '+ip2 ) ) 84 | info( net[h].cmd( 'route add default gw '+gw1) ) 85 | info( net[h].cmd( 'ip route add '+target+' via '+ gw2) ) 86 | toe_options(h, iface1) 87 | toe_options(h, iface2) 88 | 89 | 90 | if __name__ == '__main__': 91 | setLogLevel( 'info' ) 92 | topo = NetworkTopo() 93 | net = Mininet( topo=topo ) # controller for h3 switch and h0 switch 94 | net.start() 95 | info( '*** Settng host network option:\n' ) 96 | host_setting('h11', 'h11-eth0', 'h11-eth1', 97 | '10.4.0.100/24', '10.1.0.100/24', '10.4.0.1', '10.1.0.1', '10.2.0.0/24') 98 | host_setting('h12', 'h12-eth0', 'h12-eth1', 99 | '10.4.0.101/24', '10.1.0.101/24', '10.4.0.1', '10.1.0.1', '10.2.0.0/24') 100 | net['r1'].cmd('arp -s 10.4.0.2 FF:FF:FF:FF:FF:FF') 101 | # c0 ping 10.4.0.2 102 | toe_options('h2', 'h2-eth0') 103 | toe_options('c0', 'c0-eth0') 104 | toe_options('c1', 'c1-eth0') 105 | toe_options('c2', 'c2-eth0') 106 | CLI( net ) 107 | net.stop() 108 | 109 | -------------------------------------------------------------------------------- /p4_mininet.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013-present Barefoot Networks, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | from mininet.net import Mininet 17 | from mininet.node import Switch, Host 18 | from mininet.log import setLogLevel, info 19 | 20 | class P4Host(Host): 21 | def config(self, **params): 22 | r = super(Host, self).config(**params) 23 | 24 | # self.defaultIntf().rename("eth0") 25 | 26 | for off in ["rx", "tx", "sg"]: 27 | cmd = "/sbin/ethtool --offload eth0 %s off" % off 28 | self.cmd(cmd) 29 | 30 | # disable IPv6 31 | self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") 32 | self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") 33 | self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") 34 | 35 | return r 36 | 37 | def describe(self): 38 | print "**********" 39 | print self.name 40 | print "default interface: %s\t%s\t%s" %( 41 | self.defaultIntf().name, 42 | self.defaultIntf().IP(), 43 | self.defaultIntf().MAC() 44 | ) 45 | print "**********" 46 | 47 | class P4Switch(Switch): 48 | """P4 virtual switch""" 49 | device_id = 0 50 | 51 | def __init__( self, name, sw_path = None, json_path = None, 52 | thrift_port = None, 53 | pcap_dump = False, 54 | verbose = False, 55 | device_id = None, 56 | **kwargs ): 57 | Switch.__init__( self, name, **kwargs ) 58 | assert(sw_path) 59 | assert(json_path) 60 | self.sw_path = sw_path 61 | self.json_path = json_path 62 | self.verbose = verbose 63 | logfile = '/tmp/p4s.%s.log' % self.name 64 | self.output = open(logfile, 'w') 65 | self.thrift_port = thrift_port 66 | self.pcap_dump = pcap_dump 67 | if device_id is not None: 68 | self.device_id = device_id 69 | P4Switch.device_id = max(P4Switch.device_id, device_id) 70 | else: 71 | self.device_id = P4Switch.device_id 72 | P4Switch.device_id += 1 73 | self.nanomsg = "ipc:///tmp/bm-%d-log.ipc" % self.device_id 74 | 75 | @classmethod 76 | def setup( cls ): 77 | pass 78 | 79 | def start( self, controllers ): 80 | "Start up a new P4 switch" 81 | print "Starting P4 switch", self.name 82 | args = [self.sw_path] 83 | # args.extend( ['--name', self.name] ) 84 | # args.extend( ['--dpid', self.dpid] ) 85 | for port, intf in self.intfs.items(): 86 | if not intf.IP(): 87 | args.extend( ['-i', str(port) + "@" + intf.name] ) 88 | if self.pcap_dump: 89 | args.append("--pcap") 90 | # args.append("--useFiles") 91 | if self.thrift_port: 92 | args.extend( ['--thrift-port', str(self.thrift_port)] ) 93 | if self.nanomsg: 94 | args.extend( ['--nanolog', self.nanomsg] ) 95 | args.extend( ['--device-id', str(self.device_id)] ) 96 | P4Switch.device_id += 1 97 | args.append("--debugger") 98 | args.append(self.json_path) 99 | 100 | logfile = '/tmp/p4s.%s.log' % self.name 101 | 102 | print ' '.join(args) 103 | 104 | self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 &' ) 105 | # self.cmd( ' '.join(args) + ' > /dev/null 2>&1 &' ) 106 | 107 | print "switch has been started" 108 | 109 | def stop( self ): 110 | "Terminate IVS switch." 111 | self.output.flush() 112 | self.cmd( 'kill %' + self.sw_path ) 113 | self.cmd( 'wait' ) 114 | self.deleteIntfs() 115 | 116 | def attach( self, intf ): 117 | "Connect a data port" 118 | assert(0) 119 | 120 | def detach( self, intf ): 121 | "Disconnect a data port" 122 | assert(0) 123 | -------------------------------------------------------------------------------- /p4_mininet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiwang123/HappyFlowFriends/35e75b05155a421dd85a91469ea88f1612b8b23e/p4_mininet.pyc -------------------------------------------------------------------------------- /receiver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import struct 4 | from scapy.all import sniff 5 | from scapy.all import hexdump 6 | from scapy.all import IP 7 | 8 | def myprint(pkt): 9 | pkt.show() 10 | if IP in pkt: 11 | hexdump(pkt) 12 | pkt.show() 13 | print pkt[IP].src, pkt[IP].dst, pkt[IP].proto 14 | 15 | def main(): 16 | sniff(iface = sys.argv[1], prn = lambda x: myprint(x)) 17 | 18 | 19 | if __name__ == '__main__': 20 | main() 21 | -------------------------------------------------------------------------------- /send_cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source env.sh 3 | monitor_json_file="monitor.json" 4 | token_json_file="token.json" 5 | verifier_json_file="verifier.json" 6 | 7 | if [ -f "$monitor_json_file" ] 8 | then 9 | # insert default rule for p4 switches 10 | $BMV2_PATH/tools/runtime_CLI.py --json $monitor_json_file --thrift-port 9090 < monitor_cmd.txt 11 | else 12 | echo "$monitor_json_file not found." 13 | fi 14 | 15 | if [ -f "$token_json_file" ] 16 | then 17 | # insert default rule for p4 switches 18 | $BMV2_PATH/tools/runtime_CLI.py --json $token_json_file --thrift-port 9091 < token_cmd.txt 19 | else 20 | echo "$token_json_file not found." 21 | fi 22 | 23 | 24 | if [ -f "$verifier_json_file" ] 25 | then 26 | # insert default rule for p4 switches 27 | $BMV2_PATH/tools/runtime_CLI.py --json $verifier_json_file --thrift-port 9092 < verifier_cmd.txt 28 | else 29 | echo "$verifier_json_file not found." 30 | fi 31 | -------------------------------------------------------------------------------- /token/headers.p4: -------------------------------------------------------------------------------- 1 | header_type ethernet_t { 2 | fields { 3 | dstAddr : 48; 4 | srcAddr : 48; 5 | etherType : 16; 6 | } 7 | } 8 | 9 | header_type arp_ipv4_t { 10 | fields { 11 | ignore: 64; 12 | src_mac : 48; 13 | src_ip : 32; 14 | dst_mac : 48; 15 | dst_ip : 32; 16 | } 17 | } 18 | 19 | header_type ipv4_t { 20 | fields { 21 | version : 4; 22 | ihl : 4; 23 | diffserv : 8; 24 | totalLen : 16; 25 | identification : 16; 26 | flags : 3; 27 | fragOffset : 13; 28 | ttl : 8; 29 | protocol : 8; 30 | hdrChecksum : 16; 31 | srcAddr : 32; 32 | dstAddr: 32; 33 | } 34 | } 35 | 36 | header_type checker_t { 37 | fields { 38 | val : 32; 39 | } 40 | } 41 | 42 | header ethernet_t ethernet; 43 | header ipv4_t ipv4; 44 | header arp_ipv4_t arp_ipv4; 45 | header checker_t checker; 46 | 47 | -------------------------------------------------------------------------------- /token/main.p4: -------------------------------------------------------------------------------- 1 | #include "tables.p4" 2 | 3 | control ingress { 4 | if (standard_metadata.ingress_port != 1) { // from proxy to verifier 5 | apply(table_forward_ahead); 6 | } else { // from verifier to proxy 7 | if (ethernet.etherType == ETHERTYPE_IPV4) 8 | apply(table_forward_back_ipv4); 9 | else if (ethernet.etherType == ETHERTYPE_ARP_IPV4) 10 | apply(table_forward_back_arp_ipv4); 11 | else 12 | apply(table_drop); // for now, don't care 13 | } 14 | } 15 | 16 | control egress { 17 | // from proxy to verifier 18 | if(standard_metadata.egress_port == 1 and ethernet.etherType == ETHERTYPE_IPV4) { 19 | apply(table_add_header); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /token/parser.p4: -------------------------------------------------------------------------------- 1 | #include "headers.p4" 2 | 3 | parser start { 4 | return parse_ethernet; 5 | } 6 | 7 | 8 | #define ETHERTYPE_IPV4 0x0800 9 | #define ETHERTYPE_ARP_IPV4 0x0806 10 | #define FAKE_IPV4_VER 0x0 11 | 12 | parser parse_ethernet { 13 | extract(ethernet); 14 | return select(ethernet.etherType) { 15 | ETHERTYPE_IPV4 : parse_ipv4; 16 | ETHERTYPE_ARP_IPV4 : parse_arp_ipv4; 17 | default: ingress; 18 | } 19 | } 20 | 21 | parser parse_ipv4 { 22 | extract(ipv4); 23 | return select(ipv4.version) { 24 | FAKE_IPV4_VER : parse_checker; // hack(parse graph), otherwise will overwrite tcp part 25 | default: ingress; 26 | } 27 | } 28 | 29 | parser parse_arp_ipv4 { 30 | extract(arp_ipv4); 31 | return ingress; 32 | } 33 | 34 | parser parse_checker { 35 | extract(checker); 36 | return ingress; 37 | } 38 | 39 | field_list ipv4_checksum_list { 40 | ipv4.version; 41 | ipv4.ihl; 42 | ipv4.diffserv; 43 | ipv4.totalLen; 44 | ipv4.identification; 45 | ipv4.flags; 46 | ipv4.fragOffset; 47 | ipv4.ttl; 48 | ipv4.protocol; 49 | ipv4.srcAddr; 50 | ipv4.dstAddr; 51 | } 52 | 53 | field_list_calculation ipv4_new_hdrChecksum { 54 | input { 55 | ipv4_checksum_list; 56 | } 57 | algorithm : csum16; 58 | output_width : 16; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /token/tables.p4: -------------------------------------------------------------------------------- 1 | #include "parser.p4" 2 | 3 | action forward_ahead() { 4 | modify_field(standard_metadata.egress_spec, 1); 5 | } 6 | 7 | 8 | table table_forward_ahead { 9 | actions { 10 | forward_ahead; 11 | } 12 | 13 | } 14 | 15 | action forward_back_ipv4() { 16 | // host num is 2^n, modify egress port 17 | modify_field(standard_metadata.egress_spec, (ipv4.dstAddr&1) + 2); 18 | } 19 | 20 | 21 | table table_forward_back_ipv4 { 22 | actions { 23 | forward_back_ipv4; 24 | 25 | } 26 | 27 | } 28 | 29 | action forward_back_arp_ipv4() { 30 | // host num is 2^n, modify egress port 31 | modify_field(standard_metadata.egress_spec, (arp_ipv4.dst_ip&1) + 2); 32 | } 33 | 34 | 35 | table table_forward_back_arp_ipv4 { 36 | actions { 37 | forward_back_arp_ipv4; 38 | 39 | } 40 | 41 | } 42 | 43 | table table_drop { 44 | actions { 45 | action_drop; 46 | } 47 | } 48 | 49 | action action_drop() { 50 | drop(); 51 | } 52 | 53 | #define check_key 0xdeadbeef 54 | 55 | action action_add_header() { 56 | add_header(checker); 57 | modify_field(checker.val, check_key); 58 | modify_field(ipv4.totalLen, ipv4.totalLen + 4); 59 | modify_field_with_hash_based_offset(ipv4.hdrChecksum, 0, ipv4_new_hdrChecksum, 65536); 60 | } 61 | 62 | table table_add_header { 63 | actions { 64 | action_add_header; 65 | } 66 | 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /token_cmd.txt: -------------------------------------------------------------------------------- 1 | table_set_default table_forward_ahead forward_ahead 2 | table_set_default table_forward_back_ipv4 forward_back_ipv4 3 | table_set_default table_forward_back_arp_ipv4 forward_back_arp_ipv4 4 | table_set_default table_add_header action_add_header 5 | table_set_default table_drop action_drop 6 | -------------------------------------------------------------------------------- /verifier/headers.p4: -------------------------------------------------------------------------------- 1 | header_type ethernet_t { 2 | fields { 3 | dstAddr : 48; 4 | srcAddr : 48; 5 | etherType : 16; 6 | } 7 | } 8 | 9 | header_type ipv4_t { 10 | fields { 11 | version : 4; 12 | ihl : 4; 13 | diffserv : 8; 14 | totalLen : 16; 15 | identification : 16; 16 | flags : 3; 17 | fragOffset : 13; 18 | ttl : 8; 19 | protocol : 8; 20 | hdrChecksum : 16; 21 | srcAddr : 32; 22 | dstAddr: 32; 23 | } 24 | } 25 | 26 | header_type checker_t { 27 | fields { 28 | val : 32; 29 | } 30 | } 31 | 32 | header ethernet_t ethernet; 33 | header ipv4_t ipv4; 34 | header checker_t checker; 35 | -------------------------------------------------------------------------------- /verifier/main.p4: -------------------------------------------------------------------------------- 1 | #include "tables.p4" 2 | 3 | #define check_key 0xdeadbeef 4 | 5 | control ingress { 6 | if(standard_metadata.ingress_port == 2 and ethernet.etherType == ETHERTYPE_IPV4) { 7 | if(checker.val != check_key) { 8 | apply(table_drop); 9 | } else { 10 | apply(table_remove_header); 11 | apply(table_forward); 12 | } 13 | } else { 14 | apply(table_forward); 15 | } 16 | } 17 | 18 | control egress { 19 | } 20 | -------------------------------------------------------------------------------- /verifier/parser.p4: -------------------------------------------------------------------------------- 1 | #include "headers.p4" 2 | 3 | parser start { 4 | return parse_ethernet; 5 | } 6 | 7 | 8 | #define ETHERTYPE_IPV4 0x0800 9 | #define FAKE_IPV4_VER 0x0 10 | 11 | parser parse_ethernet { 12 | extract(ethernet); 13 | return select(ethernet.etherType) { 14 | ETHERTYPE_IPV4 : parse_ipv4; 15 | default: ingress; 16 | } 17 | } 18 | 19 | parser parse_ipv4 { 20 | extract(ipv4); 21 | return parse_checker; 22 | } 23 | 24 | parser parse_checker { 25 | extract(checker); 26 | return ingress; 27 | } 28 | 29 | field_list ipv4_checksum_list { 30 | ipv4.version; 31 | ipv4.ihl; 32 | ipv4.diffserv; 33 | ipv4.totalLen; 34 | ipv4.identification; 35 | ipv4.flags; 36 | ipv4.fragOffset; 37 | ipv4.ttl; 38 | ipv4.protocol; 39 | ipv4.srcAddr; 40 | ipv4.dstAddr; 41 | } 42 | 43 | field_list_calculation ipv4_new_hdrChecksum { 44 | input { 45 | ipv4_checksum_list; 46 | } 47 | algorithm : csum16; 48 | output_width : 16; 49 | } 50 | -------------------------------------------------------------------------------- /verifier/tables.p4: -------------------------------------------------------------------------------- 1 | #include "parser.p4" 2 | 3 | action action_forward(out_port) { 4 | modify_field(standard_metadata.egress_spec, out_port); 5 | } 6 | 7 | 8 | table table_forward { 9 | reads { 10 | standard_metadata.ingress_port: exact; 11 | } 12 | actions { 13 | action_forward; 14 | } 15 | 16 | } 17 | 18 | table table_drop { 19 | actions { 20 | action_drop; 21 | } 22 | } 23 | 24 | action action_drop() { 25 | drop(); 26 | } 27 | 28 | action action_remove_header(){ 29 | remove_header(checker); 30 | modify_field(ipv4.totalLen, ipv4.totalLen - 4); 31 | modify_field_with_hash_based_offset(ipv4.hdrChecksum, 0, ipv4_new_hdrChecksum, 65536); 32 | } 33 | 34 | table table_remove_header { 35 | actions { 36 | action_remove_header; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /verifier_cmd.txt: -------------------------------------------------------------------------------- 1 | table_add table_forward action_forward 2 => 1 2 | table_add table_forward action_forward 1 => 2 3 | table_set_default table_drop action_drop 4 | table_set_default table_remove_header action_remove_header 5 | --------------------------------------------------------------------------------