├── README.md ├── commands.txt ├── p4src ├── includes │ ├── headers.p4 │ └── parser.p4 ├── loss_radar.p4 └── lossradar_switch.p4 ├── simple_switch_target └── simple_switch.cpp └── topo.txt /README.md: -------------------------------------------------------------------------------- 1 | LossRadar p4 code 2 | ======== 3 | 4 | ## need to first modify the bmv2 simple_switch.cpp 5 | 6 | A working modification to simple_switch.cpp is show in simple_switch_target/simple_switch.cpp. 7 | All modifications are within: 8 | 9 | #if LOSSRADAR_ENABLE 10 | #endif 11 | 12 | The modification includs adding customized hash functions (the simple switch does not expose enough hash functions), and exposing the timestamp when packet arrive at the ingress pipeline (this is exposed by intrinsic_metadata.ingress_global_timestamp, but when I use that, the switch just does not work, so I add my own metadata). 13 | 14 | ## How to run the example? 15 | 16 | 1. Feed the p4 code (p4src/lossradar_switch.p4) to the [p4c_bm](https://github.com/p4lang/p4c-bm). 17 | 18 | p4c-bmv2 p4src/lossradar_switch.p4 --json lossradar_switch.json 19 | 20 | 2. Build the [simple_switch](https://github.com/p4lang/behavioral-model/tree/master/targets/simple_switch) target in bmv2, with the modification provided in simple_switch_target/simple_switch.cpp. 21 | 22 | 3. Create a mininet, with the topology described in topo.txt 23 | 24 | 4. Set up the table rules, described in commands.txt 25 | 26 | ## How it works? 27 | 28 | Please refer to the CoNext' 16 paper. 29 | -------------------------------------------------------------------------------- /commands.txt: -------------------------------------------------------------------------------- 1 | set_switch s1 2 | 3 | table_set_default send_frame _drop 4 | table_set_default forward _drop 5 | table_set_default ipv4_lpm _drop 6 | table_set_default add_loss_radar_hdr_table _no_op 7 | table_set_default remove_loss_radar_hdr_table _no_op 8 | table_set_default loss_radar_calc_hash_table loss_radar_calc_hash 9 | table_set_default loss_radar_um_table _no_op 10 | table_set_default loss_radar_dm_table _no_op 11 | 12 | table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:01 13 | table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:02 14 | table_add send_frame rewrite_mac 3 => 00:aa:bb:00:00:03 15 | 16 | table_add forward set_dmac 10.0.1.10 => 00:04:00:00:00:01 17 | table_add forward set_dmac 10.0.2.10 => 00:04:00:00:00:02 18 | table_add forward set_dmac 10.0.3.10 => 00:04:00:00:00:03 19 | table_add forward set_dmac 10.0.4.10 => 00:04:00:00:00:04 20 | 21 | table_add ipv4_lpm set_nhop 10.0.1.10/24 => 10.0.1.10 1 22 | table_add ipv4_lpm set_nhop 10.0.2.10/24 => 10.0.2.10 2 23 | table_add ipv4_lpm set_nhop 10.0.3.10/24 => 10.0.3.10 3 24 | table_add ipv4_lpm set_nhop 10.0.4.10/24 => 10.0.4.10 3 25 | 26 | table_add add_loss_radar_hdr_table add_lossradar_hdr 6 => 27 | #table_add add_loss_radar_hdr_table add_lossradar_hdr 17 => 28 | table_add add_loss_radar_hdr_table add_lossradar_hdr 1 => 29 | 30 | table_add remove_loss_radar_hdr_table remove_lossradar_hdr 1 252 => 31 | table_add remove_loss_radar_hdr_table remove_lossradar_hdr 2 252 => 32 | 33 | table_add loss_radar_um_table loss_radar_um 1 252 0 => 34 | 35 | table_add loss_radar_dm_table loss_radar_dm 1 252 => 36 | 37 | set_switch s2 38 | 39 | table_set_default send_frame _drop 40 | table_set_default forward _drop 41 | table_set_default ipv4_lpm _drop 42 | table_set_default add_loss_radar_hdr_table _no_op 43 | table_set_default remove_loss_radar_hdr_table _no_op 44 | table_set_default loss_radar_calc_hash_table loss_radar_calc_hash 45 | table_set_default loss_radar_um_table _no_op 46 | table_set_default loss_radar_dm_table _no_op 47 | 48 | table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:01 49 | table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:02 50 | table_add send_frame rewrite_mac 3 => 00:aa:bb:00:00:03 51 | 52 | table_add forward set_dmac 10.0.1.10 => 00:04:00:00:00:01 53 | table_add forward set_dmac 10.0.2.10 => 00:04:00:00:00:02 54 | table_add forward set_dmac 10.0.3.10 => 00:04:00:00:00:03 55 | table_add forward set_dmac 10.0.4.10 => 00:04:00:00:00:04 56 | 57 | table_add ipv4_lpm set_nhop 10.0.1.10/24 => 10.0.1.10 3 58 | table_add ipv4_lpm set_nhop 10.0.2.10/24 => 10.0.2.10 3 59 | table_add ipv4_lpm set_nhop 10.0.3.10/24 => 10.0.3.10 1 60 | table_add ipv4_lpm set_nhop 10.0.4.10/24 => 10.0.4.10 2 61 | 62 | table_add add_loss_radar_hdr_table add_lossradar_hdr 6 => 63 | #table_add add_loss_radar_hdr_table add_lossradar_hdr 17 => 64 | table_add add_loss_radar_hdr_table add_lossradar_hdr 1 => 65 | 66 | table_add remove_loss_radar_hdr_table remove_lossradar_hdr 1 252 => 67 | table_add remove_loss_radar_hdr_table remove_lossradar_hdr 2 252 => 68 | 69 | table_add loss_radar_um_table loss_radar_um 1 252 0 => 70 | 71 | table_add loss_radar_dm_table loss_radar_dm 1 252 => 72 | -------------------------------------------------------------------------------- /p4src/includes/headers.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013-present Barefoot Networks, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | * Modified by Yuliang Li liyuliang001@gmail.com 19 | */ 20 | 21 | header_type ethernet_t { 22 | fields { 23 | dstAddr : 48; 24 | srcAddr : 48; 25 | etherType : 16; 26 | } 27 | } 28 | 29 | header_type ipv4_t { 30 | fields { 31 | version : 4; 32 | ihl : 4; 33 | diffserv : 8; 34 | totalLen : 16; 35 | identification : 16; 36 | flags : 3; 37 | fragOffset : 13; 38 | ttl : 8; 39 | protocol : 8; 40 | hdrChecksum : 16; 41 | srcAddr : 32; 42 | dstAddr: 32; 43 | } 44 | } 45 | 46 | header_type tcp_t { 47 | fields { 48 | srcPort : 16; 49 | dstPort : 16; 50 | } 51 | } 52 | 53 | header_type lossradar_hdr_t{ 54 | fields { 55 | batchID : 1; 56 | padding: 2; 57 | timestamp : 13; 58 | protocol : 8; // the original IPv4 protocol 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /p4src/includes/parser.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013-present Barefoot Networks, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | * Modified by Yuliang Li liyuliang001@gmail.com 19 | */ 20 | 21 | parser start { 22 | return parse_ethernet; 23 | } 24 | 25 | #define ETHERTYPE_IPV4 0x0800 26 | #define IPV4_TCP 0x0006 27 | #define IPV4_UDP 0x0011 28 | #define IPV4_LOSSRADAR 0xFC //0xFC is not assigned [https://www.ietf.org/assignments/protocol-numbers/protocol-numbers.xml] 29 | 30 | // parse ethernet header 31 | header ethernet_t ethernet; 32 | 33 | parser parse_ethernet { 34 | extract(ethernet); 35 | return select(latest.etherType) { 36 | ETHERTYPE_IPV4 : parse_ipv4; 37 | default: ingress; 38 | } 39 | } 40 | 41 | // parse ipv4 header 42 | header ipv4_t ipv4; 43 | 44 | field_list ipv4_checksum_list { 45 | ipv4.version; 46 | ipv4.ihl; 47 | ipv4.diffserv; 48 | ipv4.totalLen; 49 | ipv4.identification; 50 | ipv4.flags; 51 | ipv4.fragOffset; 52 | ipv4.ttl; 53 | ipv4.protocol; 54 | ipv4.srcAddr; 55 | ipv4.dstAddr; 56 | } 57 | 58 | field_list_calculation ipv4_checksum { 59 | input { 60 | ipv4_checksum_list; 61 | } 62 | algorithm : csum16; 63 | output_width : 16; 64 | } 65 | 66 | calculated_field ipv4.hdrChecksum { 67 | verify ipv4_checksum; 68 | update ipv4_checksum; 69 | } 70 | 71 | parser parse_ipv4 { 72 | extract(ipv4); 73 | return select(latest.protocol){ 74 | IPV4_TCP : parse_tcp; 75 | IPV4_LOSSRADAR : parse_lossradar; 76 | default: ingress; 77 | } 78 | } 79 | 80 | // parse lossradar header 81 | header lossradar_hdr_t lossradar_hdr; 82 | 83 | parser parse_lossradar { 84 | extract(lossradar_hdr); 85 | return select(latest.protocol){ 86 | IPV4_TCP : parse_tcp; 87 | default: ingress; 88 | } 89 | } 90 | 91 | // parse tcp header 92 | header tcp_t tcp; 93 | 94 | parser parse_tcp { 95 | extract(tcp); 96 | return ingress; 97 | } 98 | 99 | -------------------------------------------------------------------------------- /p4src/loss_radar.p4: -------------------------------------------------------------------------------- 1 | #define N_PORT 4 2 | #define N_PORT_IDX_WIDTH 2 3 | 4 | #define LOSSRADAR_SIZE 32 5 | #define LOSSRADAR_IDX_WIDTH 5 6 | 7 | #define LOSSRADAR_SUB_SIZE 8 8 | #define LOSSRADAR_SUB_IDX_WIDTH 3 9 | 10 | // round_up(LOSSRADAR_SIZE * N_PORT) to 2^x, becasue in register_read(x, arr, idx), the length of arr has to be at least the max number index could be. For example, if idx is 7 bits, then len(arr) must be at least 128. (this is not shown in any spec, but only with this setting can work in the bm) 11 | #define BATCH_SIZE 128 12 | // LOSSRADAR_IDX_WIDTH + N_PORT_IDX_WIDTH 13 | #define BATCH_WIDTH 7 14 | 15 | #define TOTAL_SIZE 256 16 | #define TOTAL_WIDTH 8 17 | 18 | #define LOSSRADAR_TIMESTAMP_WIDTH 13 19 | #define LOSSRADAR_TIMESTAMP_MAX 8192 20 | 21 | // exposed by the bm 22 | header_type lossradar_metadata_t{ 23 | fields { 24 | ingress_timestamp : 48; 25 | } 26 | } 27 | metadata lossradar_metadata_t lossradar_metadata; 28 | 29 | header_type lr_tmp_t{ 30 | fields { 31 | original_ttl: 8; 32 | this_batch: 1; 33 | ingress_timestamp: LOSSRADAR_TIMESTAMP_WIDTH; 34 | 35 | um_h1: TOTAL_WIDTH; 36 | um_h2: TOTAL_WIDTH; 37 | um_h3: TOTAL_WIDTH; 38 | 39 | dm_h1: TOTAL_WIDTH; 40 | dm_h2: TOTAL_WIDTH; 41 | dm_h3: TOTAL_WIDTH; 42 | 43 | tmp_srcip: 32; 44 | tmp_dstip: 32; 45 | tmp_srcport: 16; 46 | tmp_dstport: 16; 47 | tmp_protocol: 8; 48 | tmp_ipid: 16; 49 | tmp_timestamp: LOSSRADAR_TIMESTAMP_WIDTH; 50 | tmp_ttl: 8; 51 | tmp_pkt_cnt: 16; 52 | } 53 | } 54 | metadata lr_tmp_t lr_tmp; 55 | 56 | field_list um_packet{ 57 | ipv4.srcAddr; 58 | ipv4.dstAddr; 59 | tcp.srcPort; 60 | tcp.dstPort; 61 | lossradar_hdr.protocol; 62 | ipv4.identification; 63 | lr_tmp.ingress_timestamp; 64 | ipv4.ttl; 65 | } 66 | 67 | field_list dm_packet{ 68 | ipv4.srcAddr; 69 | ipv4.dstAddr; 70 | tcp.srcPort; 71 | tcp.dstPort; 72 | lossradar_hdr.protocol; 73 | ipv4.identification; 74 | lossradar_hdr.timestamp; 75 | lr_tmp.original_ttl; 76 | } 77 | 78 | field_list_calculation um_hash1{ 79 | input{ 80 | um_packet; 81 | } 82 | algorithm: my_hash1; 83 | output_width: LOSSRADAR_SUB_IDX_WIDTH; 84 | } 85 | 86 | field_list_calculation um_hash2{ 87 | input{ 88 | um_packet; 89 | } 90 | algorithm: my_hash2; 91 | output_width: LOSSRADAR_SUB_IDX_WIDTH; 92 | } 93 | 94 | field_list_calculation um_hash3{ 95 | input{ 96 | um_packet; 97 | } 98 | algorithm: my_hash3; 99 | output_width: LOSSRADAR_SUB_IDX_WIDTH; 100 | } 101 | 102 | field_list_calculation dm_hash1{ 103 | input{ 104 | dm_packet; 105 | } 106 | algorithm: my_hash1; 107 | output_width: LOSSRADAR_SUB_IDX_WIDTH; 108 | } 109 | 110 | field_list_calculation dm_hash2{ 111 | input{ 112 | dm_packet; 113 | } 114 | algorithm: my_hash2; 115 | output_width: LOSSRADAR_SUB_IDX_WIDTH; 116 | } 117 | 118 | field_list_calculation dm_hash3{ 119 | input{ 120 | dm_packet; 121 | } 122 | algorithm: my_hash3; 123 | output_width: LOSSRADAR_SUB_IDX_WIDTH; 124 | } 125 | 126 | // lossradar upstream meter 127 | register um_pkt_cnt{ 128 | width:16; 129 | instance_count: TOTAL_SIZE; 130 | } 131 | register um_xor_srcip{ 132 | width:32; 133 | instance_count: TOTAL_SIZE; 134 | } 135 | register um_xor_dstip{ 136 | width:32; 137 | instance_count: TOTAL_SIZE; 138 | } 139 | register um_xor_srcport{ 140 | width:16; 141 | instance_count: TOTAL_SIZE; 142 | } 143 | register um_xor_dstport{ 144 | width:16; 145 | instance_count: TOTAL_SIZE; 146 | } 147 | register um_xor_protocol{ 148 | width:8; 149 | instance_count: TOTAL_SIZE; 150 | } 151 | register um_xor_ipid{ 152 | width:16; 153 | instance_count: TOTAL_SIZE; 154 | } 155 | register um_xor_timestamp{ 156 | width:LOSSRADAR_TIMESTAMP_WIDTH; 157 | instance_count: TOTAL_SIZE; 158 | } 159 | register um_xor_ttl{ 160 | width:8; 161 | instance_count: TOTAL_SIZE; 162 | } 163 | 164 | // lossradar downstream meter 165 | register dm_pkt_cnt{ 166 | width:16; 167 | instance_count: TOTAL_SIZE; 168 | } 169 | register dm_xor_srcip{ 170 | width:32; 171 | instance_count: TOTAL_SIZE; 172 | } 173 | register dm_xor_dstip{ 174 | width:32; 175 | instance_count: TOTAL_SIZE; 176 | } 177 | register dm_xor_srcport{ 178 | width:16; 179 | instance_count: TOTAL_SIZE; 180 | } 181 | register dm_xor_dstport{ 182 | width:16; 183 | instance_count: TOTAL_SIZE; 184 | } 185 | register dm_xor_protocol{ 186 | width:8; 187 | instance_count: TOTAL_SIZE; 188 | } 189 | register dm_xor_ipid{ 190 | width:16; 191 | instance_count: TOTAL_SIZE; 192 | } 193 | register dm_xor_timestamp{ 194 | width:LOSSRADAR_TIMESTAMP_WIDTH; 195 | instance_count: TOTAL_SIZE; 196 | } 197 | register dm_xor_ttl{ 198 | width:8; 199 | instance_count: TOTAL_SIZE; 200 | } 201 | //debug 202 | register debug{ 203 | width: 32; 204 | instance_count: 100; 205 | } 206 | 207 | action add_lossradar_hdr(){ 208 | add_header(lossradar_hdr); 209 | modify_field(lossradar_hdr.protocol, ipv4.protocol); 210 | modify_field(ipv4.protocol, IPV4_LOSSRADAR); 211 | add_to_field(ipv4.totalLen, 3); 212 | } 213 | table add_loss_radar_hdr_table{ 214 | reads { 215 | ipv4.protocol: exact; 216 | } 217 | actions { 218 | add_lossradar_hdr; 219 | _no_op; 220 | } 221 | size: 10; 222 | } 223 | 224 | action remove_lossradar_hdr(){ 225 | modify_field(ipv4.protocol, lossradar_hdr.protocol); 226 | remove_header(lossradar_hdr); 227 | add_to_field(ipv4.totalLen, -3); 228 | } 229 | table remove_loss_radar_hdr_table{ 230 | reads { 231 | standard_metadata.egress_port: exact; 232 | ipv4.protocol: exact; 233 | } 234 | actions { 235 | remove_lossradar_hdr; 236 | _no_op; 237 | } 238 | size: 2; 239 | } 240 | 241 | action loss_radar_calc_hash(){ 242 | // update metadata 243 | modify_field(lr_tmp.original_ttl, ipv4.ttl + 1); 244 | modify_field(lr_tmp.this_batch, (lossradar_metadata.ingress_timestamp/LOSSRADAR_TIMESTAMP_MAX%2)); 245 | modify_field(lr_tmp.ingress_timestamp, lossradar_metadata.ingress_timestamp%LOSSRADAR_TIMESTAMP_MAX); 246 | 247 | // update um 248 | // 1. calculate the index to the IBF 249 | modify_field_with_hash_based_offset(lr_tmp.um_h1, 0 + LOSSRADAR_SIZE * standard_metadata.egress_spec + BATCH_SIZE * lr_tmp.this_batch, um_hash1, LOSSRADAR_SUB_SIZE); 250 | modify_field_with_hash_based_offset(lr_tmp.um_h2, LOSSRADAR_SUB_SIZE + LOSSRADAR_SIZE * standard_metadata.egress_spec + BATCH_SIZE * lr_tmp.this_batch, um_hash2, LOSSRADAR_SUB_SIZE); 251 | modify_field_with_hash_based_offset(lr_tmp.um_h3, LOSSRADAR_SUB_SIZE * 2 + LOSSRADAR_SIZE * standard_metadata.egress_spec + BATCH_SIZE * lr_tmp.this_batch, um_hash3, LOSSRADAR_SUB_SIZE); 252 | 253 | // update dm 254 | modify_field_with_hash_based_offset(lr_tmp.dm_h1, 0 + LOSSRADAR_SIZE * standard_metadata.ingress_port + BATCH_SIZE * lossradar_hdr.batchID, dm_hash1, LOSSRADAR_SUB_SIZE); 255 | modify_field_with_hash_based_offset(lr_tmp.dm_h2, LOSSRADAR_SUB_SIZE + LOSSRADAR_SIZE * standard_metadata.ingress_port + BATCH_SIZE * lossradar_hdr.batchID, dm_hash2, LOSSRADAR_SUB_SIZE); 256 | modify_field_with_hash_based_offset(lr_tmp.dm_h3, LOSSRADAR_SUB_SIZE * 2 + LOSSRADAR_SIZE * standard_metadata.ingress_port + BATCH_SIZE * lossradar_hdr.batchID, dm_hash3, LOSSRADAR_SUB_SIZE); 257 | } 258 | table loss_radar_calc_hash_table{ 259 | actions{ 260 | loss_radar_calc_hash; 261 | } 262 | } 263 | 264 | action loss_radar_um(){ 265 | // 2. change the register 266 | // count 267 | register_read(lr_tmp.tmp_pkt_cnt, um_pkt_cnt, lr_tmp.um_h1); 268 | add_to_field(lr_tmp.tmp_pkt_cnt, 1); 269 | register_write(um_pkt_cnt, lr_tmp.um_h1, lr_tmp.tmp_pkt_cnt); 270 | 271 | register_read(lr_tmp.tmp_pkt_cnt, um_pkt_cnt, lr_tmp.um_h2); 272 | add_to_field(lr_tmp.tmp_pkt_cnt, 1); 273 | register_write(um_pkt_cnt, lr_tmp.um_h2, lr_tmp.tmp_pkt_cnt); 274 | 275 | register_read(lr_tmp.tmp_pkt_cnt, um_pkt_cnt, lr_tmp.um_h3); 276 | add_to_field(lr_tmp.tmp_pkt_cnt, 1); 277 | register_write(um_pkt_cnt, lr_tmp.um_h3, lr_tmp.tmp_pkt_cnt); 278 | 279 | // srcip 280 | register_read(lr_tmp.tmp_srcip, um_xor_srcip, lr_tmp.um_h1); 281 | modify_field(lr_tmp.tmp_srcip, lr_tmp.tmp_srcip ^ ipv4.srcAddr); 282 | register_write(um_xor_srcip, lr_tmp.um_h1, lr_tmp.tmp_srcip); 283 | 284 | register_read(lr_tmp.tmp_srcip, um_xor_srcip, lr_tmp.um_h2); 285 | modify_field(lr_tmp.tmp_srcip, lr_tmp.tmp_srcip ^ ipv4.srcAddr); 286 | register_write(um_xor_srcip, lr_tmp.um_h2, lr_tmp.tmp_srcip); 287 | 288 | register_read(lr_tmp.tmp_srcip, um_xor_srcip, lr_tmp.um_h3); 289 | modify_field(lr_tmp.tmp_srcip, lr_tmp.tmp_srcip ^ ipv4.srcAddr); 290 | register_write(um_xor_srcip, lr_tmp.um_h3, lr_tmp.tmp_srcip); 291 | 292 | // ipid 293 | register_read(lr_tmp.tmp_ipid, um_xor_ipid, lr_tmp.um_h1); 294 | modify_field(lr_tmp.tmp_ipid, lr_tmp.tmp_ipid ^ ipv4.identification); 295 | register_write(um_xor_ipid, lr_tmp.um_h1, lr_tmp.tmp_ipid); 296 | 297 | register_read(lr_tmp.tmp_ipid, um_xor_ipid, lr_tmp.um_h2); 298 | modify_field(lr_tmp.tmp_ipid, lr_tmp.tmp_ipid ^ ipv4.identification); 299 | register_write(um_xor_ipid, lr_tmp.um_h2, lr_tmp.tmp_ipid); 300 | 301 | register_read(lr_tmp.tmp_ipid, um_xor_ipid, lr_tmp.um_h3); 302 | modify_field(lr_tmp.tmp_ipid, lr_tmp.tmp_ipid ^ ipv4.identification); 303 | register_write(um_xor_ipid, lr_tmp.um_h3, lr_tmp.tmp_ipid); 304 | 305 | // timestamp 306 | register_read(lr_tmp.tmp_timestamp, um_xor_timestamp, lr_tmp.um_h1); 307 | modify_field(lr_tmp.tmp_timestamp, lr_tmp.tmp_timestamp ^ lossradar_metadata.ingress_timestamp); 308 | register_write(um_xor_timestamp, lr_tmp.um_h1, lr_tmp.tmp_timestamp); 309 | 310 | register_read(lr_tmp.tmp_timestamp, um_xor_timestamp, lr_tmp.um_h2); 311 | modify_field(lr_tmp.tmp_timestamp, lr_tmp.tmp_timestamp ^ lossradar_metadata.ingress_timestamp); 312 | register_write(um_xor_timestamp, lr_tmp.um_h2, lr_tmp.tmp_timestamp); 313 | 314 | register_read(lr_tmp.tmp_timestamp, um_xor_timestamp, lr_tmp.um_h3); 315 | modify_field(lr_tmp.tmp_timestamp, lr_tmp.tmp_timestamp ^ lossradar_metadata.ingress_timestamp); 316 | register_write(um_xor_timestamp, lr_tmp.um_h3, lr_tmp.tmp_timestamp); 317 | 318 | // ttl 319 | register_read(lr_tmp.tmp_ttl, um_xor_ttl, lr_tmp.um_h1); 320 | modify_field(lr_tmp.tmp_ttl, lr_tmp.tmp_ttl ^ ipv4.ttl); 321 | register_write(um_xor_ttl, lr_tmp.um_h1, lr_tmp.tmp_ttl); 322 | 323 | register_read(lr_tmp.tmp_ttl, um_xor_ttl, lr_tmp.um_h2); 324 | modify_field(lr_tmp.tmp_ttl, lr_tmp.tmp_ttl ^ ipv4.ttl); 325 | register_write(um_xor_ttl, lr_tmp.um_h2, lr_tmp.tmp_ttl); 326 | 327 | register_read(lr_tmp.tmp_ttl, um_xor_ttl, lr_tmp.um_h3); 328 | modify_field(lr_tmp.tmp_ttl, lr_tmp.tmp_ttl ^ ipv4.ttl); 329 | register_write(um_xor_ttl, lr_tmp.um_h3, lr_tmp.tmp_ttl); 330 | 331 | } 332 | table loss_radar_um_table{ // only update if the packet is not dropped, so that we ca exclude the expected drops. 333 | reads { 334 | ipv4: valid; 335 | ipv4.protocol: exact; 336 | drop_metadata.drop : exact; 337 | } 338 | actions{ 339 | loss_radar_um; 340 | _no_op; 341 | } 342 | size: 10; 343 | } 344 | 345 | action loss_radar_dm(){ 346 | // update dm 347 | // count 348 | register_read(lr_tmp.tmp_pkt_cnt, dm_pkt_cnt, lr_tmp.dm_h1); 349 | add_to_field(lr_tmp.tmp_pkt_cnt, 1); 350 | register_write(dm_pkt_cnt, lr_tmp.dm_h1, lr_tmp.tmp_pkt_cnt); 351 | 352 | register_read(lr_tmp.tmp_pkt_cnt, dm_pkt_cnt, lr_tmp.dm_h2); 353 | add_to_field(lr_tmp.tmp_pkt_cnt, 1); 354 | register_write(dm_pkt_cnt, lr_tmp.dm_h2, lr_tmp.tmp_pkt_cnt); 355 | 356 | register_read(lr_tmp.tmp_pkt_cnt, dm_pkt_cnt, lr_tmp.dm_h3); 357 | add_to_field(lr_tmp.tmp_pkt_cnt, 1); 358 | register_write(dm_pkt_cnt, lr_tmp.dm_h3, lr_tmp.tmp_pkt_cnt); 359 | 360 | // srcip 361 | register_read(lr_tmp.tmp_srcip, dm_xor_srcip, lr_tmp.dm_h1); 362 | modify_field(lr_tmp.tmp_srcip, lr_tmp.tmp_srcip ^ ipv4.srcAddr); 363 | register_write(dm_xor_srcip, lr_tmp.dm_h1, lr_tmp.tmp_srcip); 364 | 365 | register_read(lr_tmp.tmp_srcip, dm_xor_srcip, lr_tmp.dm_h2); 366 | modify_field(lr_tmp.tmp_srcip, lr_tmp.tmp_srcip ^ ipv4.srcAddr); 367 | register_write(dm_xor_srcip, lr_tmp.dm_h2, lr_tmp.tmp_srcip); 368 | 369 | register_read(lr_tmp.tmp_srcip, dm_xor_srcip, lr_tmp.dm_h3); 370 | modify_field(lr_tmp.tmp_srcip, lr_tmp.tmp_srcip ^ ipv4.srcAddr); 371 | register_write(dm_xor_srcip, lr_tmp.dm_h3, lr_tmp.tmp_srcip); 372 | 373 | // ipid 374 | register_read(lr_tmp.tmp_ipid, dm_xor_ipid, lr_tmp.dm_h1); 375 | modify_field(lr_tmp.tmp_ipid, lr_tmp.tmp_ipid ^ ipv4.identification); 376 | register_write(dm_xor_ipid, lr_tmp.dm_h1, lr_tmp.tmp_ipid); 377 | 378 | register_read(lr_tmp.tmp_ipid, dm_xor_ipid, lr_tmp.dm_h2); 379 | modify_field(lr_tmp.tmp_ipid, lr_tmp.tmp_ipid ^ ipv4.identification); 380 | register_write(dm_xor_ipid, lr_tmp.dm_h2, lr_tmp.tmp_ipid); 381 | 382 | register_read(lr_tmp.tmp_ipid, dm_xor_ipid, lr_tmp.dm_h3); 383 | modify_field(lr_tmp.tmp_ipid, lr_tmp.tmp_ipid ^ ipv4.identification); 384 | register_write(dm_xor_ipid, lr_tmp.dm_h3, lr_tmp.tmp_ipid); 385 | 386 | // timestamp 387 | register_read(lr_tmp.tmp_timestamp, dm_xor_timestamp, lr_tmp.dm_h1); 388 | modify_field(lr_tmp.tmp_timestamp, lr_tmp.tmp_timestamp ^ lossradar_hdr.timestamp); 389 | register_write(dm_xor_timestamp, lr_tmp.dm_h1, lr_tmp.tmp_timestamp); 390 | 391 | register_read(lr_tmp.tmp_timestamp, dm_xor_timestamp, lr_tmp.dm_h2); 392 | modify_field(lr_tmp.tmp_timestamp, lr_tmp.tmp_timestamp ^ lossradar_hdr.timestamp); 393 | register_write(dm_xor_timestamp, lr_tmp.dm_h2, lr_tmp.tmp_timestamp); 394 | 395 | register_read(lr_tmp.tmp_timestamp, dm_xor_timestamp, lr_tmp.dm_h3); 396 | modify_field(lr_tmp.tmp_timestamp, lr_tmp.tmp_timestamp ^ lossradar_hdr.timestamp); 397 | register_write(dm_xor_timestamp, lr_tmp.dm_h3, lr_tmp.tmp_timestamp); 398 | 399 | // ttl 400 | register_read(lr_tmp.tmp_ttl, dm_xor_ttl, lr_tmp.dm_h1); 401 | modify_field(lr_tmp.tmp_ttl, lr_tmp.tmp_ttl ^ lr_tmp.original_ttl); 402 | register_write(dm_xor_ttl, lr_tmp.dm_h1, lr_tmp.tmp_ttl); 403 | 404 | register_read(lr_tmp.tmp_ttl, dm_xor_ttl, lr_tmp.dm_h2); 405 | modify_field(lr_tmp.tmp_ttl, lr_tmp.tmp_ttl ^ lr_tmp.original_ttl); 406 | register_write(dm_xor_ttl, lr_tmp.dm_h2, lr_tmp.tmp_ttl); 407 | 408 | register_read(lr_tmp.tmp_ttl, dm_xor_ttl, lr_tmp.dm_h3); 409 | modify_field(lr_tmp.tmp_ttl, lr_tmp.tmp_ttl ^ lr_tmp.original_ttl); 410 | register_write(dm_xor_ttl, lr_tmp.dm_h3, lr_tmp.tmp_ttl); 411 | 412 | // update batchID and timestamp 413 | modify_field(lossradar_hdr.batchID, (lossradar_metadata.ingress_timestamp/LOSSRADAR_TIMESTAMP_MAX%2)); 414 | modify_field(lossradar_hdr.timestamp, lossradar_metadata.ingress_timestamp); 415 | } 416 | table loss_radar_dm_table{ // no matter dropped or not, update the dm, so that we can exclude the expected drops (dropped by tables) 417 | reads { 418 | ipv4: valid; 419 | ipv4.protocol: exact; 420 | } 421 | actions{ 422 | loss_radar_dm; 423 | _no_op; 424 | } 425 | size: 10; 426 | } 427 | -------------------------------------------------------------------------------- /p4src/lossradar_switch.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013-present Barefoot Networks, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | * Modified by Yuliang Li liyuliang001@gmail.com; 19 | */ 20 | 21 | #include "includes/headers.p4" 22 | #include "includes/parser.p4" 23 | #include "loss_radar.p4" 24 | 25 | header_type drop_metadata_t { 26 | fields { 27 | drop : 1; 28 | } 29 | } 30 | metadata drop_metadata_t drop_metadata; 31 | 32 | action _drop() { 33 | modify_field(drop_metadata.drop, 1); 34 | drop(); 35 | } 36 | 37 | action _no_op(){ 38 | no_op(); 39 | } 40 | 41 | header_type routing_metadata_t { 42 | fields { 43 | nhop_ipv4 : 32; 44 | } 45 | } 46 | 47 | metadata routing_metadata_t routing_metadata; 48 | 49 | action set_nhop(nhop_ipv4, port) { 50 | modify_field(routing_metadata.nhop_ipv4, nhop_ipv4); 51 | modify_field(standard_metadata.egress_spec, port); 52 | add_to_field(ipv4.ttl, -1); 53 | } 54 | 55 | table ipv4_lpm { 56 | reads { 57 | ipv4.dstAddr : lpm; 58 | } 59 | actions { 60 | set_nhop; 61 | _drop; 62 | } 63 | size: 1024; 64 | } 65 | 66 | action set_dmac(dmac) { 67 | modify_field(ethernet.dstAddr, dmac); 68 | } 69 | 70 | table forward { 71 | reads { 72 | routing_metadata.nhop_ipv4 : exact; 73 | } 74 | actions { 75 | set_dmac; 76 | _drop; 77 | } 78 | size: 512; 79 | } 80 | 81 | action rewrite_mac(smac) { 82 | modify_field(ethernet.srcAddr, smac); 83 | } 84 | 85 | table send_frame { 86 | reads { 87 | standard_metadata.egress_port: exact; 88 | } 89 | actions { 90 | rewrite_mac; 91 | _drop; 92 | } 93 | size: 256; 94 | } 95 | 96 | 97 | control ingress { 98 | apply(ipv4_lpm); 99 | apply(forward); 100 | apply(add_loss_radar_hdr_table); 101 | apply(loss_radar_calc_hash_table); 102 | apply(loss_radar_um_table); 103 | apply(loss_radar_dm_table); 104 | } 105 | 106 | control egress { 107 | apply(send_frame); 108 | apply(remove_loss_radar_hdr_table); 109 | } 110 | -------------------------------------------------------------------------------- /simple_switch_target/simple_switch.cpp: -------------------------------------------------------------------------------- 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 | /* 17 | * Antonin Bas (antonin@barefootnetworks.com) 18 | * 19 | */ 20 | 21 | #define LOSSRADAR_ENABLE 1 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include "simple_switch.h" 34 | 35 | 36 | 37 | /* 38 | * The following murmur3_32 code is copied from wikipedia: https://en.wikipedia.org/wiki/MurmurHash 39 | * as well as https://github.com/USC-NSL/p4c-behavioral/blob/master/p4c_bm/templates/src/checksums_algos.h 40 | */ 41 | 42 | #if LOSSRADAR_ENABLE 43 | #define ROT32(x, y) ((x << y) | (x >> (32 - y))) // avoid effort 44 | static inline uint32_t murmur3_32(const char *key, uint32_t len, uint32_t seed) { 45 | static const uint32_t c1 = 0xcc9e2d51; 46 | static const uint32_t c2 = 0x1b873593; 47 | static const uint32_t r1 = 15; 48 | static const uint32_t r2 = 13; 49 | static const uint32_t m = 5; 50 | static const uint32_t n = 0xe6546b64; 51 | 52 | uint32_t hash = seed; 53 | 54 | const int nblocks = len / 4; 55 | const uint32_t *blocks = (const uint32_t *) key; 56 | int i; 57 | uint32_t k; 58 | for (i = 0; i < nblocks; i++) { 59 | k = blocks[i]; 60 | k *= c1; 61 | k = ROT32(k, r1); 62 | k *= c2; 63 | 64 | hash ^= k; 65 | hash = ROT32(hash, r2) * m + n; 66 | } 67 | 68 | const uint8_t *tail = (const uint8_t *) (key + nblocks * 4); 69 | uint32_t k1 = 0; 70 | 71 | switch (len & 3) { 72 | case 3: 73 | k1 ^= tail[2] << 16; 74 | case 2: 75 | k1 ^= tail[1] << 8; 76 | case 1: 77 | k1 ^= tail[0]; 78 | 79 | k1 *= c1; 80 | k1 = ROT32(k1, r1); 81 | k1 *= c2; 82 | hash ^= k1; 83 | } 84 | 85 | hash ^= len; 86 | hash ^= (hash >> 16); 87 | hash *= 0x85ebca6b; 88 | hash ^= (hash >> 13); 89 | hash *= 0xc2b2ae35; 90 | hash ^= (hash >> 16); 91 | 92 | return hash; 93 | } 94 | #endif 95 | 96 | 97 | namespace { 98 | 99 | #if LOSSRADAR_ENABLE 100 | struct my_hash1 { 101 | uint32_t operator()(const char *buf, size_t s) const { 102 | return murmur3_32(buf, s, 1); 103 | } 104 | }; 105 | 106 | struct my_hash2 { 107 | uint32_t operator()(const char *buf, size_t s) const { 108 | return murmur3_32(buf, s, 2); 109 | } 110 | }; 111 | 112 | struct my_hash3 { 113 | uint32_t operator()(const char *buf, size_t s) const { 114 | return murmur3_32(buf, s, 3); 115 | } 116 | }; 117 | 118 | struct my_hash4 { 119 | uint32_t operator()(const char *buf, size_t s) const { 120 | return murmur3_32(buf, s, 4); 121 | } 122 | }; 123 | 124 | struct my_hash5 { 125 | uint32_t operator()(const char *buf, size_t s) const { 126 | return murmur3_32(buf, s, 5); 127 | } 128 | }; 129 | 130 | struct my_hash6 { 131 | uint32_t operator()(const char *buf, size_t s) const { 132 | return murmur3_32(buf, s, 6); 133 | } 134 | }; 135 | 136 | struct my_hash7 { 137 | uint32_t operator()(const char *buf, size_t s) const { 138 | return murmur3_32(buf, s, 7); 139 | } 140 | }; 141 | 142 | struct my_hash8 { 143 | uint32_t operator()(const char *buf, size_t s) const { 144 | return murmur3_32(buf, s, 8); 145 | } 146 | }; 147 | 148 | struct my_hash9 { 149 | uint32_t operator()(const char *buf, size_t s) const { 150 | return murmur3_32(buf, s, 9); 151 | } 152 | }; 153 | 154 | struct my_hash10 { 155 | uint32_t operator()(const char *buf, size_t s) const { 156 | return murmur3_32(buf, s, 10); 157 | } 158 | }; 159 | 160 | struct my_hash11 { 161 | uint32_t operator()(const char *buf, size_t s) const { 162 | return murmur3_32(buf, s, 11); 163 | } 164 | }; 165 | 166 | struct my_hash12 { 167 | uint32_t operator()(const char *buf, size_t s) const { 168 | return murmur3_32(buf, s, 12); 169 | } 170 | }; 171 | 172 | struct my_hash13 { 173 | uint32_t operator()(const char *buf, size_t s) const { 174 | return murmur3_32(buf, s, 13); 175 | } 176 | }; 177 | 178 | struct my_hash14 { 179 | uint32_t operator()(const char *buf, size_t s) const { 180 | return murmur3_32(buf, s, 14); 181 | } 182 | }; 183 | 184 | struct my_hash15 { 185 | uint32_t operator()(const char *buf, size_t s) const { 186 | return murmur3_32(buf, s, 15); 187 | } 188 | }; 189 | 190 | struct my_hash16 { 191 | uint32_t operator()(const char *buf, size_t s) const { 192 | return murmur3_32(buf, s, 16); 193 | } 194 | }; 195 | 196 | struct my_hash17 { 197 | uint32_t operator()(const char *buf, size_t s) const { 198 | return murmur3_32(buf, s, 17); 199 | } 200 | }; 201 | 202 | struct my_hash18 { 203 | uint32_t operator()(const char *buf, size_t s) const { 204 | return murmur3_32(buf, s, 18); 205 | } 206 | }; 207 | 208 | struct my_hash19 { 209 | uint32_t operator()(const char *buf, size_t s) const { 210 | return murmur3_32(buf, s, 19); 211 | } 212 | }; 213 | 214 | struct my_hash20 { 215 | uint32_t operator()(const char *buf, size_t s) const { 216 | return murmur3_32(buf, s, 20); 217 | } 218 | }; 219 | 220 | struct my_hash21 { 221 | uint32_t operator()(const char *buf, size_t s) const { 222 | return murmur3_32(buf, s, 21); 223 | } 224 | }; 225 | 226 | struct my_hash22 { 227 | uint32_t operator()(const char *buf, size_t s) const { 228 | return murmur3_32(buf, s, 22); 229 | } 230 | }; 231 | #endif 232 | 233 | struct hash_ex { 234 | uint32_t operator()(const char *buf, size_t s) const { 235 | const int p = 16777619; 236 | int hash = 2166136261; 237 | 238 | for (size_t i = 0; i < s; i++) 239 | hash = (hash ^ buf[i]) * p; 240 | 241 | hash += hash << 13; 242 | hash ^= hash >> 7; 243 | hash += hash << 3; 244 | hash ^= hash >> 17; 245 | hash += hash << 5; 246 | return static_cast(hash); 247 | } 248 | }; 249 | 250 | struct bmv2_hash { 251 | uint64_t operator()(const char *buf, size_t s) const { 252 | return bm::hash::xxh64(buf, s); 253 | } 254 | }; 255 | 256 | } // namespace 257 | 258 | // if REGISTER_HASH calls placed in the anonymous namespace, some compiler can 259 | // give an unused variable warning 260 | REGISTER_HASH(hash_ex); 261 | REGISTER_HASH(bmv2_hash); 262 | 263 | #if LOSSRADAR_ENABLE 264 | REGISTER_HASH(my_hash1); 265 | REGISTER_HASH(my_hash2); 266 | REGISTER_HASH(my_hash3); 267 | REGISTER_HASH(my_hash4); 268 | REGISTER_HASH(my_hash5); 269 | REGISTER_HASH(my_hash6); 270 | REGISTER_HASH(my_hash7); 271 | REGISTER_HASH(my_hash8); 272 | REGISTER_HASH(my_hash9); 273 | REGISTER_HASH(my_hash10); 274 | REGISTER_HASH(my_hash11); 275 | REGISTER_HASH(my_hash12); 276 | REGISTER_HASH(my_hash13); 277 | REGISTER_HASH(my_hash14); 278 | REGISTER_HASH(my_hash15); 279 | REGISTER_HASH(my_hash16); 280 | REGISTER_HASH(my_hash17); 281 | REGISTER_HASH(my_hash18); 282 | REGISTER_HASH(my_hash19); 283 | REGISTER_HASH(my_hash20); 284 | REGISTER_HASH(my_hash21); 285 | REGISTER_HASH(my_hash22); 286 | #endif 287 | extern int import_primitives(); 288 | 289 | SimpleSwitch::SimpleSwitch(int max_port, bool enable_swap) 290 | : Switch(enable_swap), 291 | max_port(max_port), 292 | input_buffer(1024), 293 | #ifdef SSWITCH_PRIORITY_QUEUEING_ON 294 | egress_buffers(max_port, nb_egress_threads, 295 | 64, EgressThreadMapper(nb_egress_threads), 296 | SSWITCH_PRIORITY_QUEUEING_NB_QUEUES), 297 | #else 298 | egress_buffers(max_port, nb_egress_threads, 299 | 64, EgressThreadMapper(nb_egress_threads)), 300 | #endif 301 | output_buffer(128), 302 | pre(new McSimplePreLAG()), 303 | start(clock::now()) { 304 | add_component(pre); 305 | 306 | add_required_field("standard_metadata", "ingress_port"); 307 | add_required_field("standard_metadata", "packet_length"); 308 | add_required_field("standard_metadata", "instance_type"); 309 | add_required_field("standard_metadata", "egress_spec"); 310 | add_required_field("standard_metadata", "clone_spec"); 311 | add_required_field("standard_metadata", "egress_port"); 312 | 313 | force_arith_field("standard_metadata", "ingress_port"); 314 | force_arith_field("standard_metadata", "packet_length"); 315 | force_arith_field("standard_metadata", "instance_type"); 316 | force_arith_field("standard_metadata", "egress_spec"); 317 | force_arith_field("standard_metadata", "clone_spec"); 318 | 319 | force_arith_field("queueing_metadata", "enq_timestamp"); 320 | force_arith_field("queueing_metadata", "enq_qdepth"); 321 | force_arith_field("queueing_metadata", "deq_timedelta"); 322 | force_arith_field("queueing_metadata", "deq_qdepth"); 323 | 324 | force_arith_field("intrinsic_metadata", "ingress_global_timestamp"); 325 | force_arith_field("intrinsic_metadata", "lf_field_list"); 326 | force_arith_field("intrinsic_metadata", "mcast_grp"); 327 | force_arith_field("intrinsic_metadata", "resubmit_flag"); 328 | force_arith_field("intrinsic_metadata", "egress_rid"); 329 | force_arith_field("intrinsic_metadata", "recirculate_flag"); 330 | 331 | #if LOSSRADAR_ENABLE 332 | force_arith_field("lossradar_metadata", "ingress_timestamp"); 333 | #endif 334 | 335 | import_primitives(); 336 | } 337 | 338 | #define PACKET_LENGTH_REG_IDX 0 339 | 340 | int 341 | SimpleSwitch::receive(int port_num, const char *buffer, int len) { 342 | static int pkt_id = 0; 343 | 344 | // this is a good place to call this, because blocking this thread will not 345 | // block the processing of existing packet instances, which is a requirement 346 | if (do_swap() == 0) { 347 | check_queueing_metadata(); 348 | } 349 | 350 | // we limit the packet buffer to original size + 512 bytes, which means we 351 | // cannot add more than 512 bytes of header data to the packet, which should 352 | // be more than enough 353 | auto packet = new_packet_ptr(port_num, pkt_id++, len, 354 | bm::PacketBuffer(len + 512, buffer, len)); 355 | 356 | BMELOG(packet_in, *packet); 357 | 358 | PHV *phv = packet->get_phv(); 359 | // many current P4 programs assume this 360 | // it is also part of the original P4 spec 361 | phv->reset_metadata(); 362 | 363 | // setting standard metadata 364 | 365 | phv->get_field("standard_metadata.ingress_port").set(port_num); 366 | // using packet register 0 to store length, this register will be updated for 367 | // each add_header / remove_header primitive call 368 | packet->set_register(PACKET_LENGTH_REG_IDX, len); 369 | phv->get_field("standard_metadata.packet_length").set(len); 370 | Field &f_instance_type = phv->get_field("standard_metadata.instance_type"); 371 | f_instance_type.set(PKT_INSTANCE_TYPE_NORMAL); 372 | 373 | if (phv->has_field("intrinsic_metadata.ingress_global_timestamp")) { 374 | phv->get_field("intrinsic_metadata.ingress_global_timestamp") 375 | .set(get_ts().count()); 376 | } 377 | #if LOSSRADAR_ENABLE 378 | phv->get_field("lossradar_metadata.ingress_timestamp").set(get_ts().count()); 379 | #endif 380 | 381 | input_buffer.push_front(std::move(packet)); 382 | return 0; 383 | } 384 | 385 | void 386 | SimpleSwitch::start_and_return() { 387 | check_queueing_metadata(); 388 | 389 | std::thread t1(&SimpleSwitch::ingress_thread, this); 390 | t1.detach(); 391 | for (size_t i = 0; i < nb_egress_threads; i++) { 392 | std::thread t2(&SimpleSwitch::egress_thread, this, i); 393 | t2.detach(); 394 | } 395 | std::thread t3(&SimpleSwitch::transmit_thread, this); 396 | t3.detach(); 397 | } 398 | 399 | void 400 | SimpleSwitch::reset_target_state() { 401 | bm::Logger::get()->debug("Resetting simple_switch target-specific state"); 402 | get_component()->reset_state(); 403 | } 404 | 405 | int 406 | SimpleSwitch::set_egress_queue_depth(int port, const size_t depth_pkts) { 407 | egress_buffers.set_capacity(port, depth_pkts); 408 | return 0; 409 | } 410 | 411 | int 412 | SimpleSwitch::set_all_egress_queue_depths(const size_t depth_pkts) { 413 | for (int i = 0; i < max_port; i++) { 414 | set_egress_queue_depth(i, depth_pkts); 415 | } 416 | return 0; 417 | } 418 | 419 | int 420 | SimpleSwitch::set_egress_queue_rate(int port, const uint64_t rate_pps) { 421 | egress_buffers.set_rate(port, rate_pps); 422 | return 0; 423 | } 424 | 425 | int 426 | SimpleSwitch::set_all_egress_queue_rates(const uint64_t rate_pps) { 427 | for (int i = 0; i < max_port; i++) { 428 | set_egress_queue_rate(i, rate_pps); 429 | } 430 | return 0; 431 | } 432 | 433 | void 434 | SimpleSwitch::transmit_thread() { 435 | while (1) { 436 | std::unique_ptr packet; 437 | output_buffer.pop_back(&packet); 438 | BMELOG(packet_out, *packet); 439 | BMLOG_DEBUG_PKT(*packet, "Transmitting packet of size {} out of port {}", 440 | packet->get_data_size(), packet->get_egress_port()); 441 | transmit_fn(packet->get_egress_port(), 442 | packet->data(), packet->get_data_size()); 443 | } 444 | } 445 | 446 | ts_res 447 | SimpleSwitch::get_ts() const { 448 | return duration_cast(clock::now() - start); 449 | } 450 | 451 | void 452 | SimpleSwitch::enqueue(int egress_port, std::unique_ptr &&packet) { 453 | packet->set_egress_port(egress_port); 454 | 455 | PHV *phv = packet->get_phv(); 456 | 457 | if (with_queueing_metadata) { 458 | phv->get_field("queueing_metadata.enq_timestamp").set(get_ts().count()); 459 | phv->get_field("queueing_metadata.enq_qdepth") 460 | .set(egress_buffers.size(egress_port)); 461 | } 462 | 463 | #ifdef SSWITCH_PRIORITY_QUEUEING_ON 464 | size_t priority = 465 | phv->get_field(SSWITCH_PRIORITY_QUEUEING_SRC).get(); 466 | if (priority >= SSWITCH_PRIORITY_QUEUEING_NB_QUEUES) { 467 | bm::Logger::get()->error("Priority out of range, dropping packet"); 468 | return; 469 | } 470 | egress_buffers.push_front( 471 | egress_port, SSWITCH_PRIORITY_QUEUEING_NB_QUEUES - 1 - priority, 472 | std::move(packet)); 473 | #else 474 | egress_buffers.push_front(egress_port, std::move(packet)); 475 | #endif 476 | } 477 | 478 | // used for ingress cloning, resubmit 479 | std::unique_ptr 480 | SimpleSwitch::copy_ingress_pkt( 481 | const std::unique_ptr &packet, 482 | PktInstanceType copy_type, p4object_id_t field_list_id) { 483 | std::unique_ptr packet_copy = packet->clone_no_phv_ptr(); 484 | PHV *phv_copy = packet_copy->get_phv(); 485 | phv_copy->reset_metadata(); 486 | FieldList *field_list = this->get_field_list(field_list_id); 487 | const PHV *phv = packet->get_phv(); 488 | for (const auto &p : *field_list) { 489 | phv_copy->get_field(p.header, p.offset) 490 | .set(phv->get_field(p.header, p.offset)); 491 | } 492 | phv_copy->get_field("standard_metadata.instance_type").set(copy_type); 493 | return packet_copy; 494 | } 495 | 496 | void 497 | SimpleSwitch::check_queueing_metadata() { 498 | bool enq_timestamp_e = field_exists("queueing_metadata", "enq_timestamp"); 499 | bool enq_qdepth_e = field_exists("queueing_metadata", "enq_qdepth"); 500 | bool deq_timedelta_e = field_exists("queueing_metadata", "deq_timedelta"); 501 | bool deq_qdepth_e = field_exists("queueing_metadata", "deq_qdepth"); 502 | if (enq_timestamp_e || enq_qdepth_e || deq_timedelta_e || deq_qdepth_e) { 503 | if (enq_timestamp_e && enq_qdepth_e && deq_timedelta_e && deq_qdepth_e) 504 | with_queueing_metadata = true; 505 | else 506 | bm::Logger::get()->warn( 507 | "Your JSON input defines some but not all queueing metadata fields"); 508 | } 509 | } 510 | 511 | void 512 | SimpleSwitch::ingress_thread() { 513 | PHV *phv; 514 | 515 | while (1) { 516 | std::unique_ptr packet; 517 | input_buffer.pop_back(&packet); 518 | 519 | // TODO(antonin): only update these if swapping actually happened? 520 | Parser *parser = this->get_parser("parser"); 521 | Pipeline *ingress_mau = this->get_pipeline("ingress"); 522 | 523 | phv = packet->get_phv(); 524 | 525 | int ingress_port = packet->get_ingress_port(); 526 | (void) ingress_port; 527 | BMLOG_DEBUG_PKT(*packet, "Processing packet received on port {}", 528 | ingress_port); 529 | 530 | /* This looks like it comes out of the blue. However this is needed for 531 | ingress cloning. The parser updates the buffer state (pops the parsed 532 | headers) to make the deparser's job easier (the same buffer is 533 | re-used). But for ingress cloning, the original packet is needed. This 534 | kind of looks hacky though. Maybe a better solution would be to have the 535 | parser leave the buffer unchanged, and move the pop logic to the 536 | deparser. TODO? */ 537 | const Packet::buffer_state_t packet_in_state = packet->save_buffer_state(); 538 | parser->parse(packet.get()); 539 | 540 | ingress_mau->apply(packet.get()); 541 | 542 | packet->reset_exit(); 543 | 544 | Field &f_egress_spec = phv->get_field("standard_metadata.egress_spec"); 545 | int egress_spec = f_egress_spec.get_int(); 546 | 547 | Field &f_clone_spec = phv->get_field("standard_metadata.clone_spec"); 548 | unsigned int clone_spec = f_clone_spec.get_uint(); 549 | 550 | int learn_id = 0; 551 | unsigned int mgid = 0u; 552 | 553 | if (phv->has_field("intrinsic_metadata.lf_field_list")) { 554 | Field &f_learn_id = phv->get_field("intrinsic_metadata.lf_field_list"); 555 | learn_id = f_learn_id.get_int(); 556 | } 557 | 558 | // detect mcast support, if this is true we assume that other fields needed 559 | // for mcast are also defined 560 | if (phv->has_field("intrinsic_metadata.mcast_grp")) { 561 | Field &f_mgid = phv->get_field("intrinsic_metadata.mcast_grp"); 562 | mgid = f_mgid.get_uint(); 563 | } 564 | 565 | int egress_port; 566 | 567 | // INGRESS CLONING 568 | if (clone_spec) { 569 | BMLOG_DEBUG_PKT(*packet, "Cloning packet at ingress"); 570 | egress_port = get_mirroring_mapping(clone_spec & 0xFFFF); 571 | f_clone_spec.set(0); 572 | if (egress_port >= 0) { 573 | const Packet::buffer_state_t packet_out_state = 574 | packet->save_buffer_state(); 575 | packet->restore_buffer_state(packet_in_state); 576 | p4object_id_t field_list_id = clone_spec >> 16; 577 | auto packet_copy = copy_ingress_pkt( 578 | packet, PKT_INSTANCE_TYPE_INGRESS_CLONE, field_list_id); 579 | // we need to parse again 580 | // the alternative would be to pay the (huge) price of PHV copy for 581 | // every ingress packet 582 | parser->parse(packet_copy.get()); 583 | enqueue(egress_port, std::move(packet_copy)); 584 | packet->restore_buffer_state(packet_out_state); 585 | } 586 | } 587 | 588 | // LEARNING 589 | if (learn_id > 0) { 590 | get_learn_engine()->learn(learn_id, *packet.get()); 591 | } 592 | 593 | // RESUBMIT 594 | if (phv->has_field("intrinsic_metadata.resubmit_flag")) { 595 | Field &f_resubmit = phv->get_field("intrinsic_metadata.resubmit_flag"); 596 | if (f_resubmit.get_int()) { 597 | BMLOG_DEBUG_PKT(*packet, "Resubmitting packet"); 598 | // get the packet ready for being parsed again at the beginning of 599 | // ingress 600 | packet->restore_buffer_state(packet_in_state); 601 | p4object_id_t field_list_id = f_resubmit.get_int(); 602 | f_resubmit.set(0); 603 | // TODO(antonin): a copy is not needed here, but I don't yet have an 604 | // optimized way of doing this 605 | auto packet_copy = copy_ingress_pkt( 606 | packet, PKT_INSTANCE_TYPE_RESUBMIT, field_list_id); 607 | input_buffer.push_front(std::move(packet_copy)); 608 | continue; 609 | } 610 | } 611 | 612 | Field &f_instance_type = phv->get_field("standard_metadata.instance_type"); 613 | 614 | // MULTICAST 615 | int instance_type = f_instance_type.get_int(); 616 | if (mgid != 0) { 617 | BMLOG_DEBUG_PKT(*packet, "Multicast requested for packet"); 618 | Field &f_rid = phv->get_field("intrinsic_metadata.egress_rid"); 619 | const auto pre_out = pre->replicate({mgid}); 620 | auto packet_size = packet->get_register(PACKET_LENGTH_REG_IDX); 621 | for (const auto &out : pre_out) { 622 | egress_port = out.egress_port; 623 | // if (ingress_port == egress_port) continue; // pruning 624 | BMLOG_DEBUG_PKT(*packet, "Replicating packet on port {}", egress_port); 625 | f_rid.set(out.rid); 626 | f_instance_type.set(PKT_INSTANCE_TYPE_REPLICATION); 627 | std::unique_ptr packet_copy = packet->clone_with_phv_ptr(); 628 | packet_copy->set_register(PACKET_LENGTH_REG_IDX, packet_size); 629 | enqueue(egress_port, std::move(packet_copy)); 630 | } 631 | f_instance_type.set(instance_type); 632 | 633 | // when doing multicast, we discard the original packet 634 | continue; 635 | } 636 | 637 | egress_port = egress_spec; 638 | BMLOG_DEBUG_PKT(*packet, "Egress port is {}", egress_port); 639 | 640 | if (egress_port == 511) { // drop packet 641 | BMLOG_DEBUG_PKT(*packet, "Dropping packet at the end of ingress"); 642 | continue; 643 | } 644 | 645 | enqueue(egress_port, std::move(packet)); 646 | } 647 | } 648 | 649 | void 650 | SimpleSwitch::egress_thread(size_t worker_id) { 651 | PHV *phv; 652 | 653 | while (1) { 654 | std::unique_ptr packet; 655 | size_t port; 656 | egress_buffers.pop_back(worker_id, &port, &packet); 657 | 658 | Deparser *deparser = this->get_deparser("deparser"); 659 | Pipeline *egress_mau = this->get_pipeline("egress"); 660 | 661 | phv = packet->get_phv(); 662 | 663 | if (with_queueing_metadata) { 664 | auto enq_timestamp = 665 | phv->get_field("queueing_metadata.enq_timestamp").get(); 666 | phv->get_field("queueing_metadata.deq_timedelta").set( 667 | get_ts().count() - enq_timestamp); 668 | phv->get_field("queueing_metadata.deq_qdepth").set( 669 | egress_buffers.size(port)); 670 | } 671 | 672 | phv->get_field("standard_metadata.egress_port").set(port); 673 | 674 | Field &f_egress_spec = phv->get_field("standard_metadata.egress_spec"); 675 | f_egress_spec.set(0); 676 | 677 | phv->get_field("standard_metadata.packet_length").set( 678 | packet->get_register(PACKET_LENGTH_REG_IDX)); 679 | 680 | egress_mau->apply(packet.get()); 681 | 682 | Field &f_clone_spec = phv->get_field("standard_metadata.clone_spec"); 683 | unsigned int clone_spec = f_clone_spec.get_uint(); 684 | 685 | // EGRESS CLONING 686 | if (clone_spec) { 687 | BMLOG_DEBUG_PKT(*packet, "Cloning packet at egress"); 688 | int egress_port = get_mirroring_mapping(clone_spec & 0xFFFF); 689 | if (egress_port >= 0) { 690 | f_clone_spec.set(0); 691 | p4object_id_t field_list_id = clone_spec >> 16; 692 | std::unique_ptr packet_copy = 693 | packet->clone_with_phv_reset_metadata_ptr(); 694 | PHV *phv_copy = packet_copy->get_phv(); 695 | FieldList *field_list = this->get_field_list(field_list_id); 696 | for (const auto &p : *field_list) { 697 | phv_copy->get_field(p.header, p.offset) 698 | .set(phv->get_field(p.header, p.offset)); 699 | } 700 | phv_copy->get_field("standard_metadata.instance_type") 701 | .set(PKT_INSTANCE_TYPE_EGRESS_CLONE); 702 | enqueue(egress_port, std::move(packet_copy)); 703 | } 704 | } 705 | 706 | // TODO(antonin): should not be done like this in egress pipeline 707 | int egress_spec = f_egress_spec.get_int(); 708 | if (egress_spec == 511) { // drop packet 709 | BMLOG_DEBUG_PKT(*packet, "Dropping packet at the end of egress"); 710 | continue; 711 | } 712 | 713 | deparser->deparse(packet.get()); 714 | 715 | // RECIRCULATE 716 | if (phv->has_field("intrinsic_metadata.recirculate_flag")) { 717 | Field &f_recirc = phv->get_field("intrinsic_metadata.recirculate_flag"); 718 | if (f_recirc.get_int()) { 719 | BMLOG_DEBUG_PKT(*packet, "Recirculating packet"); 720 | p4object_id_t field_list_id = f_recirc.get_int(); 721 | f_recirc.set(0); 722 | FieldList *field_list = this->get_field_list(field_list_id); 723 | // TODO(antonin): just like for resubmit, there is no need for a copy 724 | // here, but it is more convenient for this first prototype 725 | std::unique_ptr packet_copy = packet->clone_no_phv_ptr(); 726 | PHV *phv_copy = packet_copy->get_phv(); 727 | phv_copy->reset_metadata(); 728 | for (const auto &p : *field_list) { 729 | phv_copy->get_field(p.header, p.offset) 730 | .set(phv->get_field(p.header, p.offset)); 731 | } 732 | phv_copy->get_field("standard_metadata.instance_type") 733 | .set(PKT_INSTANCE_TYPE_RECIRC); 734 | size_t packet_size = packet_copy->get_data_size(); 735 | packet_copy->set_register(PACKET_LENGTH_REG_IDX, packet_size); 736 | phv_copy->get_field("standard_metadata.packet_length").set(packet_size); 737 | input_buffer.push_front(std::move(packet_copy)); 738 | continue; 739 | } 740 | } 741 | 742 | output_buffer.push_front(std::move(packet)); 743 | } 744 | } 745 | -------------------------------------------------------------------------------- /topo.txt: -------------------------------------------------------------------------------- 1 | switches 2 2 | hosts 4 3 | h1 s1 4 | h2 s1 5 | h3 s2 6 | h4 s2 7 | s1 s2 8 | --------------------------------------------------------------------------------