├── .gitignore ├── README.md ├── fcm_p414 ├── README.md ├── fcmplus.p4 └── fcmsketch.p4 └── fcm_p416 ├── README.md ├── common ├── headers.p4 └── util.p4 ├── fcm.p4 └── ptf ├── __init__.py ├── fcm_utils.py ├── hash_calc.cpp ├── test_fcm.py └── test_fcm.pyc /.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | *.pyc 3 | *.out 4 | *.dat 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FCM-Sketch: Generic Network Measurements with Data Plane Support 2 | 3 | ## P4 Source Code 4 | This repository includes the P4 implementation of the CoNEXT 2020 paper [*FCM-Sketch: Generic Network Measurements with Data Plane Support*](https://www.comp.nus.edu.sg/~songch/papers/conext20_fcmsketch.pdf). 5 | 6 | Currently, the prototype P4 source code is written in both P4-14 and P4-16, and compiled using version 8.2.0 and 9.2.0 of BF_SDE, respectively. 7 | 8 | ## Measurement 9 | FCM-Sketch supports both various in-network (data-plane) and control-plane queries. 10 | - Data-plane queries: 11 | - `Flow size estimation` 12 | - `Heavy hitter detection` 13 | - `Cardinality` 14 | - Control-plane queries: 15 | - `Flow size distribution` 16 | - `Entropy` 17 | 18 | In addition, `Heavy change detection` is avaialble entirely in the data plane if we keep multiple sketches with resource trade-off. 19 | 20 | ## Compile & Test 21 | Please use the description in the corresponding directory. 22 | 23 | ## Future Update plan 24 | Data-Plane queries are supported in P4-16 implementation. In future, we will provide a PTF test for EM-algorithm. Actually, it is easy to extend; we can load register values, sync with simulator's data structures, and run the algorithm. 25 | 26 | ## Citing FCM-Sketch 27 | 28 | You can cite this repository or FCM-Sketch as 29 | 30 | @inproceedings{song2020fcm, 31 | title={FCM-sketch: generic network measurements with data plane support}, 32 | author={Song, Cha Hwan and Kannan, Pravein Govindan and Low, Bryan Kian Hsiang and Chan, Mun Choon}, 33 | booktitle={Proceedings of the 16th International Conference on emerging Networking EXperiments and Technologies}, 34 | pages={78--92}, 35 | year={2020} 36 | } 37 | -------------------------------------------------------------------------------- /fcm_p414/README.md: -------------------------------------------------------------------------------- 1 | # P4-14 Source Code 2 | 3 | In this repository, the prototype P4 source code is written in P4-14, and compiled using version 8.2.0 of BF_SDE. 4 | 5 | The p4-14 implementations are currently only for updating counts. They do not support in-network queries. But, you can easily get the in-network query (flow size, classification of heavy hitters, cardinality estimation) from the returned register values. 6 | -------------------------------------------------------------------------------- /fcm_p414/fcmplus.p4: -------------------------------------------------------------------------------- 1 | /* 2 | * FCM-TopK - baseline code for recording packets 3 | */ 4 | #include 5 | #include 6 | #include "tofino/stateful_alu_blackbox.p4" 7 | 8 | #define ETHERTYPE_IPV4 0x0800 9 | // Constants for Top-K 10 | #define BUCKETS 4096 // 8192 / 4096, for top-k 11 | #define BUCKET_POWER 12 // 13, 2^BUCKET_POWER = BUCKETS 12 | #define LAMBDA 5 // hyperparameter of Top-K 13 | // Constants for FCM-Sketch 14 | #define SKETCH_WIDTH_1 524288 // 8 bits, width at layer 1 15 | #define SKETCH_WIDTH_2 32768 // 16 bits, width at layer 2 16 | #define SKETCH_WIDTH_3 2048 // 32 bits, width at layer 3 17 | #define THETA_8BIT 127 // 8 bits - constant for stateful ALUs 18 | #define THETA_16BIT 32767 // 16 bits - constant for stateful ALUs 19 | 20 | 21 | header_type ethernet_t { 22 | fields { 23 | dstAddr : 48; 24 | srcAddr : 48; 25 | etherType : 16; 26 | } 27 | } 28 | 29 | header ethernet_t ethernet; 30 | 31 | header_type ipv4_t { 32 | fields { 33 | version : 4; 34 | ihl : 4; 35 | diffserv : 8; 36 | totalLen : 16; 37 | identification : 16; 38 | flags : 3; 39 | fragOffset : 13; 40 | ttl : 8; 41 | protocol : 8; 42 | hdrChecksum : 16; 43 | srcAddr : 32; 44 | dstAddr: 32; 45 | } 46 | } 47 | 48 | header ipv4_t ipv4; 49 | 50 | header_type metadata_t { 51 | fields { 52 | val_all : 32; 53 | val_to_sketch : 32; 54 | key_to_sketch : 32; 55 | go_stop_topk : 4; 56 | 57 | const_8bits : 8; // will be initialized when parsing 58 | const_16bits : 16; // will be initialized when parsing 59 | 60 | go_stop_l1_to_l2_d1 : 1; // 0 : stop, 1 : go 61 | go_stop_l2_to_l3_d1 : 1; // explained below 62 | 63 | go_stop_l1_to_l2_d2 : 1; // 0 : stop, 1 : go 64 | go_stop_l2_to_l3_d2 : 1; // explained below 65 | 66 | do_fcmplus : 1; 67 | } 68 | } 69 | 70 | metadata metadata_t mdata; 71 | 72 | field_list ipv4_field_list { 73 | ipv4.version; 74 | ipv4.ihl; 75 | ipv4.diffserv; 76 | ipv4.totalLen; 77 | ipv4.identification; 78 | ipv4.flags; 79 | ipv4.fragOffset; 80 | ipv4.ttl; 81 | ipv4.protocol; 82 | ipv4.srcAddr; 83 | ipv4.dstAddr; 84 | } 85 | 86 | field_list_calculation ipv4_checksum { 87 | input { 88 | ipv4_field_list; 89 | } 90 | algorithm : csum16; 91 | output_width : 16; 92 | } 93 | 94 | calculated_field ipv4.hdrChecksum { 95 | update ipv4_checksum; 96 | } 97 | 98 | header_type tcp_t { 99 | fields { 100 | srcPort : 16; 101 | dstPort : 16; 102 | seqNo : 32; 103 | ackNo : 32; 104 | dataOffset : 4; 105 | res : 3; 106 | ecn : 3; 107 | ctrl : 6; 108 | window : 16; 109 | checksum : 16; 110 | urgentPtr : 16; 111 | } 112 | } 113 | header tcp_t tcp; 114 | 115 | header_type udp_t { // 8 bytes 116 | fields { 117 | srcPort : 16; 118 | dstPort : 16; 119 | hdr_length : 16; 120 | checksum : 16; 121 | } 122 | } 123 | 124 | header udp_t udp; 125 | 126 | parser start { 127 | return parse_ethernet; 128 | } 129 | 130 | 131 | parser parse_ethernet { 132 | extract(ethernet); 133 | return select(latest.etherType) { 134 | ETHERTYPE_IPV4 : parse_ipv4; 135 | default: ingress; 136 | } 137 | } 138 | 139 | parser parse_ipv4 { 140 | extract(ipv4); 141 | set_metadata(mdata.val_all, 0); 142 | set_metadata(mdata.go_stop_topk, 0); 143 | 144 | set_metadata(mdata.go_stop_l1_to_l2_d1, 0); 145 | set_metadata(mdata.go_stop_l2_to_l3_d1, 0); 146 | 147 | set_metadata(mdata.go_stop_l1_to_l2_d2, 0); 148 | set_metadata(mdata.go_stop_l2_to_l3_d2, 0); 149 | 150 | set_metadata(mdata.const_8bits, 127); 151 | set_metadata(mdata.const_16bits, 32767); 152 | 153 | return select(ipv4.protocol) { 154 | 6 : parse_tcp; 155 | 17 : parse_udp; 156 | default : ingress; 157 | } 158 | } 159 | 160 | parser parse_tcp { 161 | extract(tcp); 162 | set_metadata(mdata.do_fcmplus, 1); 163 | return ingress; 164 | } 165 | 166 | parser parse_udp { 167 | extract(udp); 168 | set_metadata(mdata.do_fcmplus, 1); 169 | return ingress; 170 | } 171 | 172 | 173 | 174 | /*****************************************************/ 175 | /* Registers */ 176 | /*****************************************************/ 177 | 178 | /*-*-*-*-*- Top-K algorithm *-*-*-*-*-*/ 179 | register reg_val_all { 180 | width: 32; 181 | instance_count: BUCKETS; 182 | } 183 | register freq_id_first { 184 | width: 64; 185 | instance_count: BUCKETS; 186 | } 187 | register freq_id_second { 188 | width: 64; 189 | instance_count: BUCKETS; 190 | } 191 | register freq_id_third { 192 | width: 64; 193 | instance_count: BUCKETS; 194 | } 195 | 196 | /*-*-*-*-*-*- FCM-Sketch *-*-*-*-*-*-*-*/ 197 | register sketch_reg_l1_d1 { 198 | width : 8; 199 | instance_count : SKETCH_WIDTH_1; 200 | attributes : saturating; 201 | } 202 | register sketch_reg_l2_d1 { 203 | width: 16; 204 | instance_count : SKETCH_WIDTH_2; 205 | attributes : saturating; 206 | } 207 | register sketch_reg_l3_d1 { 208 | width: 32; 209 | instance_count : SKETCH_WIDTH_3; 210 | attributes : saturating; 211 | } 212 | register sketch_reg_l1_d2 { 213 | width : 8; 214 | instance_count : SKETCH_WIDTH_1; 215 | attributes : saturating; 216 | } 217 | register sketch_reg_l2_d2 { 218 | width: 16; 219 | instance_count : SKETCH_WIDTH_2; 220 | attributes : saturating; 221 | } 222 | register sketch_reg_l3_d2 { 223 | width: 32; 224 | instance_count : SKETCH_WIDTH_3; 225 | attributes : saturating; 226 | } 227 | 228 | 229 | /***************************************************************************/ 230 | /***************************** Hash actions ******************************/ 231 | /***************************************************************************/ 232 | 233 | /*-*-*-*-* heavy part *-*-*-*-*-*/ 234 | field_list hash_list_topk { 235 | ipv4.srcAddr; 236 | } 237 | 238 | field_list_calculation hash_topk { 239 | input { hash_list_topk; } 240 | algorithm : crc_32; 241 | output_width : BUCKET_POWER; 242 | } 243 | 244 | /*-*-*-*-* Sketches *-*-*-*-*-*/ 245 | field_list hash_list_sketch { 246 | mdata.key_to_sketch; 247 | } 248 | 249 | field_list_calculation sketch_hash_d1 { 250 | input { 251 | hash_list_sketch; 252 | } 253 | algorithm : crc_32c; 254 | output_width : 32; 255 | } 256 | 257 | field_list_calculation sketch_hash_d2 { 258 | input { 259 | hash_list_sketch; 260 | } 261 | algorithm : crc_32_mpeg; 262 | output_width : 32; 263 | } 264 | 265 | 266 | /******************************************************************/ 267 | // Actions for Top-K 268 | /******************************************************************/ 269 | 270 | // layer 1 271 | blackbox stateful_alu val_all_incre { 272 | reg : reg_val_all; 273 | update_lo_1_value : register_lo + 1; 274 | output_value : alu_lo; 275 | output_dst : mdata.val_all; 276 | } 277 | 278 | action val_all_incre_action() { 279 | val_all_incre.execute_stateful_alu_from_hash(hash_topk); 280 | } 281 | 282 | action val_all_shift_action() { 283 | shift_right(mdata.val_all, mdata.val_all, LAMBDA); 284 | } 285 | 286 | /* 287 | Rule of Predicate 288 | cond_hi | cond_lo | predicate | combined_predicate 289 | 0 | 0 | 0001 | 0 290 | 0 | 1 | 0010 | 1 291 | 1 | 0 | 0100 | 1 292 | 1 | 1 | 1000 | 1 293 | 294 | hi,lo for Top-K algorithm ::: 295 | (0,0) -> 1 -> (not same, not swap) -> go next stage 296 | (0,1) -> 2 -> (not same, swap) -> swap 297 | (1,0) -> 4 -> (same, not swap) -> just update and stop 298 | (1,1) -> 8 -> (same, swap) -> will not happen... 299 | */ 300 | 301 | blackbox stateful_alu freq_id_insert_first { 302 | reg: freq_id_first; 303 | 304 | condition_lo: mdata.val_all > register_lo; 305 | condition_hi: ipv4.srcAddr == register_hi; 306 | 307 | update_lo_1_predicate: condition_lo or condition_hi; 308 | update_lo_1_value: register_lo + 1; 309 | 310 | update_hi_1_predicate: condition_lo or condition_hi; 311 | update_hi_1_value: ipv4.srcAddr; 312 | 313 | output_predicate: condition_lo and not condition_hi; // if swap 314 | output_value: register_hi; // swap key 315 | output_dst: mdata.key_to_sketch; // key 316 | } 317 | 318 | blackbox stateful_alu freq_id_insert_second { 319 | reg: freq_id_second; 320 | 321 | condition_lo: mdata.val_all > register_lo; 322 | condition_hi: ipv4.srcAddr == register_hi; 323 | 324 | update_lo_1_predicate: condition_lo or condition_hi; 325 | update_lo_1_value: register_lo + 1; 326 | 327 | update_hi_1_predicate: condition_lo or condition_hi; 328 | update_hi_1_value: ipv4.srcAddr; 329 | 330 | output_predicate: condition_lo and not condition_hi; 331 | output_value: register_lo; // swap val 332 | output_dst: mdata.val_to_sketch; // value 333 | } 334 | 335 | blackbox stateful_alu freq_id_insert_third { 336 | reg: freq_id_third; 337 | 338 | condition_lo: mdata.val_all > register_lo; 339 | condition_hi: ipv4.srcAddr == register_hi; 340 | 341 | update_lo_1_predicate: condition_lo or condition_hi; 342 | update_lo_1_value: register_lo + 1; 343 | 344 | update_hi_1_predicate: condition_lo or condition_hi; 345 | update_hi_1_value: ipv4.srcAddr; 346 | 347 | output_value: predicate; // >=4 means con_hi is true 348 | output_dst: mdata.go_stop_topk; // 349 | } 350 | 351 | action freq_id_insert_first_action() { 352 | freq_id_insert_first.execute_stateful_alu_from_hash(hash_topk); 353 | } 354 | @pragma stage 2 355 | table freq_id_insert_first_table { 356 | actions { 357 | freq_id_insert_first_action; 358 | } 359 | default_action : freq_id_insert_first_action; 360 | } 361 | 362 | action freq_id_insert_second_action() { 363 | freq_id_insert_second.execute_stateful_alu_from_hash(hash_topk); 364 | } 365 | @pragma stage 2 366 | table freq_id_insert_second_table { 367 | actions { 368 | freq_id_insert_second_action; 369 | } 370 | default_action : freq_id_insert_second_action; 371 | } 372 | 373 | action freq_id_insert_third_action() { 374 | freq_id_insert_third.execute_stateful_alu_from_hash(hash_topk); 375 | } 376 | @pragma stage 2 377 | table freq_id_insert_third_table { 378 | actions { 379 | freq_id_insert_third_action; 380 | } 381 | default_action : freq_id_insert_third_action; 382 | } 383 | 384 | 385 | 386 | /**********************************************************************/ 387 | // Actions for FCM-SKetch 388 | /**********************************************************************/ 389 | // if not swapped, we need to manually copy the source IP and initialize value 1 390 | action sketch_transfer_action() { 391 | modify_field(mdata.key_to_sketch, ipv4.srcAddr); 392 | modify_field(mdata.val_to_sketch, 1); 393 | } 394 | 395 | 396 | 397 | /****************** actions to update counters ******************/ 398 | @pragma stateful_field_slice mdata.val_to_sketch 7 0 399 | blackbox stateful_alu update_counter_l1_d1 { 400 | reg: sketch_reg_l1_d1; 401 | condition_lo: register_lo - mdata.const_8bits >= THETA_8BIT; 402 | update_lo_1_value: register_lo + mdata.val_to_sketch; 403 | output_value: combined_predicate; // 1 bit => condition_lo 404 | output_dst: mdata.go_stop_l1_to_l2_d1; 405 | } 406 | 407 | @pragma stateful_field_slice mdata.val_to_sketch 15 0 408 | blackbox stateful_alu update_counter_l2_d1 { 409 | reg: sketch_reg_l2_d1; 410 | condition_lo: register_lo - mdata.const_16bits >= THETA_16BIT; 411 | update_lo_1_value: register_lo + mdata.val_to_sketch; 412 | output_value: combined_predicate; // 1 bit => condition_lo 413 | output_dst: mdata.go_stop_l2_to_l3_d1; 414 | } 415 | 416 | // @pragma stateful_field_slice mdata.val_to_sketch 31 0 417 | blackbox stateful_alu update_counter_l3_d1 { 418 | reg: sketch_reg_l3_d1; 419 | update_lo_1_value: register_lo + mdata.val_to_sketch; 420 | } 421 | 422 | @pragma stateful_field_slice mdata.val_to_sketch 7 0 423 | blackbox stateful_alu update_counter_l1_d2 { 424 | reg: sketch_reg_l1_d2; 425 | condition_lo: register_lo - mdata.const_8bits >= THETA_8BIT; 426 | update_lo_1_value: register_lo + mdata.val_to_sketch; 427 | output_value: combined_predicate; // 1 bit => condition_lo 428 | output_dst: mdata.go_stop_l1_to_l2_d2; 429 | } 430 | 431 | @pragma stateful_field_slice mdata.val_to_sketch 15 0 432 | blackbox stateful_alu update_counter_l2_d2 { 433 | reg: sketch_reg_l2_d2; 434 | condition_lo: register_lo - mdata.const_16bits >= THETA_16BIT; 435 | update_lo_1_value: register_lo + mdata.val_to_sketch; 436 | output_value: combined_predicate; // 1 bit => condition_lo 437 | output_dst: mdata.go_stop_l2_to_l3_d2; 438 | } 439 | 440 | // @pragma stateful_field_slice mdata.val_to_sketch 31 0 441 | blackbox stateful_alu update_counter_l3_d2 { 442 | reg: sketch_reg_l3_d2; 443 | update_lo_1_value: register_lo + mdata.val_to_sketch; 444 | } 445 | 446 | 447 | action do_update_counter_l1_d1() { 448 | update_counter_l1_d1.execute_stateful_alu_from_hash(sketch_hash_d1); 449 | } 450 | action do_update_counter_l2_d1() { 451 | update_counter_l2_d1.execute_stateful_alu_from_hash(sketch_hash_d1); 452 | } 453 | action do_update_counter_l3_d1() { 454 | update_counter_l3_d1.execute_stateful_alu_from_hash(sketch_hash_d1); 455 | } 456 | action do_update_counter_l1_d2() { 457 | update_counter_l1_d2.execute_stateful_alu_from_hash(sketch_hash_d2); 458 | } 459 | action do_update_counter_l2_d2() { 460 | update_counter_l2_d2.execute_stateful_alu_from_hash(sketch_hash_d2); 461 | } 462 | action do_update_counter_l3_d2() { 463 | update_counter_l3_d2.execute_stateful_alu_from_hash(sketch_hash_d2); 464 | } 465 | 466 | 467 | /***************************************************************************/ 468 | /***************************** Action Tables ******************************/ 469 | /***************************************************************************/ 470 | 471 | @pragma stage 0 472 | table val_all_incre_table { 473 | actions { 474 | val_all_incre_action; 475 | } 476 | default_action: val_all_incre_action; 477 | } 478 | 479 | @pragma stage 1 480 | table val_all_shift_table { 481 | actions { 482 | val_all_shift_action; 483 | } 484 | default_action : val_all_shift_action; 485 | } 486 | 487 | 488 | @pragma stage 3 489 | table sketch_transfer_table { 490 | actions { 491 | sketch_transfer_action; 492 | } 493 | default_action: sketch_transfer_action; 494 | } 495 | 496 | // update for sketch for depth 1 497 | @pragma stage 4 498 | table sketch_update_counter_l1_d1 { 499 | actions { 500 | do_update_counter_l1_d1; 501 | } 502 | default_action : do_update_counter_l1_d1; 503 | } 504 | @pragma stage 6 505 | table sketch_update_counter_l2_d1 { 506 | actions { 507 | do_update_counter_l2_d1; 508 | } 509 | default_action : do_update_counter_l2_d1; 510 | } 511 | @pragma stage 7 512 | table sketch_update_counter_l3_d1 { 513 | actions { 514 | do_update_counter_l3_d1; 515 | } 516 | default_action : do_update_counter_l3_d1; 517 | } 518 | 519 | // update for sketch for depth 2 520 | @pragma stage 5 521 | table sketch_update_counter_l1_d2 { 522 | actions { 523 | do_update_counter_l1_d2; 524 | } 525 | default_action : do_update_counter_l1_d2; 526 | } 527 | @pragma stage 6 528 | table sketch_update_counter_l2_d2 { 529 | actions { 530 | do_update_counter_l2_d2; 531 | } 532 | default_action : do_update_counter_l2_d2; 533 | } 534 | @pragma stage 7 535 | table sketch_update_counter_l3_d2 { 536 | actions { 537 | do_update_counter_l3_d2; 538 | } 539 | default_action : do_update_counter_l3_d2; 540 | } 541 | 542 | /***************************************************************************/ 543 | /********************************* Ingress *********************************/ 544 | /***************************************************************************/ 545 | 546 | 547 | control ingress { 548 | // if (ethernet.etherType == ETHERTYPE_IPV4) { // ipv4 549 | if (mdata.do_fcmplus == 1) { // TCP, UDP 550 | 551 | // Top-K process 552 | apply(table_fcmsketch_hash_l1_d1); // stage 0, preliminary for sketch's hash 553 | apply(table_fcmsketch_hash_l1_d2); // stage 0, preliminary for sketch's hash 554 | apply(val_all_incre_table); // stage 0 555 | 556 | apply(table_fcmsketch_hash_l2_d1); // stage 1, preliminary for sketch's hash 557 | apply(table_fcmsketch_hash_l2_d2); // stage 1, preliminary for sketch's hash 558 | apply(val_all_shift_table); // stage 1 559 | 560 | apply(freq_id_insert_first_table); // stage 2 561 | apply(freq_id_insert_second_table); // stage 2 562 | apply(freq_id_insert_third_table); // stage 2 563 | 564 | /***** if not swapped and insert into sketch, reinitialize key=srcIP, val=1 *****/ 565 | if (mdata.go_stop_topk == 1) { 566 | apply(sketch_transfer_table); // stage 3 567 | } 568 | /***************************************************************/ 569 | 570 | if (mdata.go_stop_topk < 4) { 571 | apply(sketch_update_counter_l1_d1); // stage 4 572 | apply(sketch_update_counter_l1_d2); // stage 5 573 | if (mdata.go_stop_l1_to_l2_d1 == 1) { 574 | apply(sketch_update_counter_l2_d1); // stage 6 575 | } 576 | if (mdata.go_stop_l1_to_l2_d2 == 1) { 577 | apply(sketch_update_counter_l2_d2); // stage 6 578 | } 579 | if (mdata.go_stop_l2_to_l3_d1 == 1) { 580 | apply(sketch_update_counter_l3_d1); // Stage 7 581 | } 582 | if (mdata.go_stop_l2_to_l3_d2 == 1) { 583 | apply(sketch_update_counter_l3_d2); // Stage 7 584 | } 585 | } 586 | } 587 | } 588 | 589 | /** Egress **/ 590 | control egress { 591 | } 592 | -------------------------------------------------------------------------------- /fcm_p414/fcmsketch.p4: -------------------------------------------------------------------------------- 1 | /* 2 | * FCM-Sketch - Baseline Feed-forward Count-Min Sketch 3 | */ 4 | #include 5 | #include 6 | #include "tofino/stateful_alu_blackbox.p4" 7 | 8 | 9 | #define ETHERTYPE_IPV4 0x0800 10 | #define K_ARY_2_POW 3 // k-ary, k = 2^3 11 | #define K_ARY_2_POW_DOUBLE 6 // k^2 = 2^6 12 | #define SKETCH_WIDTH_1 524288 // 8 bits, width at layer 1 13 | #define SKETCH_WIDTH_2 65536 // 16 bits, width at layer 2 14 | #define SKETCH_WIDTH_3 8192 // 32 bits, width at layer 3 15 | #define THETA_8BIT 127 // 8 bits - constant for stateful ALUs 16 | #define THETA_16BIT 32767 // 16 bits - constant for stateful ALUs 17 | 18 | header_type ethernet_t { 19 | fields { 20 | dstAddr : 48; 21 | srcAddr : 48; 22 | etherType : 16; 23 | } 24 | } 25 | 26 | header ethernet_t ethernet; 27 | 28 | header_type ipv4_t { 29 | fields { 30 | version : 4; 31 | ihl : 4; 32 | diffserv : 8; 33 | totalLen : 16; 34 | identification : 16; 35 | flags : 3; 36 | fragOffset : 13; 37 | ttl : 8; 38 | protocol : 8; 39 | hdrChecksum : 16; 40 | srcAddr : 32; 41 | dstAddr: 32; 42 | } 43 | } 44 | 45 | header ipv4_t ipv4; 46 | 47 | header_type metadata_t { 48 | fields { 49 | const_8bits : 8; // will be initialized when parsing 50 | const_16bits : 16; // will be initialized when parsing 51 | 52 | go_stop_l1_to_l2_d1 : 1; // 0 : stop, 1 : go 53 | go_stop_l2_to_l3_d1 : 1; // explained below 54 | 55 | go_stop_l1_to_l2_d2 : 1; // 0 : stop, 1 : go 56 | go_stop_l2_to_l3_d2 : 1; // explained below 57 | 58 | do_fcmsketch : 1; 59 | } 60 | } 61 | 62 | metadata metadata_t mdata; 63 | 64 | field_list ipv4_field_list { 65 | ipv4.version; 66 | ipv4.ihl; 67 | ipv4.diffserv; 68 | ipv4.totalLen; 69 | ipv4.identification; 70 | ipv4.flags; 71 | ipv4.fragOffset; 72 | ipv4.ttl; 73 | ipv4.protocol; 74 | ipv4.srcAddr; 75 | ipv4.dstAddr; 76 | } 77 | 78 | field_list_calculation ipv4_checksum { 79 | input { 80 | ipv4_field_list; 81 | } 82 | algorithm : csum16; 83 | output_width : 16; 84 | } 85 | 86 | calculated_field ipv4.hdrChecksum { 87 | update ipv4_checksum; 88 | } 89 | 90 | header_type tcp_t { 91 | fields { 92 | srcPort : 16; 93 | dstPort : 16; 94 | seqNo : 32; 95 | ackNo : 32; 96 | dataOffset : 4; 97 | res : 3; 98 | ecn : 3; 99 | ctrl : 6; 100 | window : 16; 101 | checksum : 16; 102 | urgentPtr : 16; 103 | } 104 | } 105 | 106 | header tcp_t tcp; 107 | 108 | header_type udp_t { // 8 bytes 109 | fields { 110 | srcPort : 16; 111 | dstPort : 16; 112 | hdr_length : 16; 113 | checksum : 16; 114 | } 115 | } 116 | 117 | header udp_t udp; 118 | 119 | parser start { 120 | return parse_ethernet; 121 | } 122 | 123 | parser parse_ethernet { 124 | extract(ethernet); 125 | return select(latest.etherType) { 126 | ETHERTYPE_IPV4 : parse_ipv4; 127 | default: ingress; 128 | } 129 | } 130 | 131 | parser parse_ipv4 { 132 | extract(ipv4); 133 | set_metadata(mdata.go_stop_l1_to_l2_d1, 0); 134 | set_metadata(mdata.go_stop_l2_to_l3_d1, 0); 135 | 136 | set_metadata(mdata.go_stop_l1_to_l2_d2, 0); 137 | set_metadata(mdata.go_stop_l2_to_l3_d2, 0); 138 | 139 | set_metadata(mdata.const_8bits, 127); 140 | set_metadata(mdata.const_16bits, 32767); 141 | return select(ipv4.protocol) { 142 | 6 : parse_tcp; 143 | 17 : parse_udp; 144 | default : ingress; 145 | } 146 | } 147 | 148 | parser parse_tcp { 149 | extract(tcp); 150 | set_metadata(mdata.do_fcmsketch, 1); 151 | return ingress; 152 | } 153 | 154 | parser parse_udp { 155 | extract(udp); 156 | set_metadata(mdata.do_fcmsketch, 1); 157 | return ingress; 158 | } 159 | /** hashing **/ 160 | 161 | field_list hash_fields { 162 | ipv4.srcAddr; 163 | } 164 | 165 | field_list_calculation sketch_hash_d1 { 166 | input { 167 | hash_fields; 168 | } 169 | algorithm : crc_32c; 170 | output_width : 32; 171 | } 172 | 173 | field_list_calculation sketch_hash_d2 { 174 | input { 175 | hash_fields; 176 | } 177 | algorithm : crc_32_mpeg; 178 | output_width : 32; 179 | } 180 | 181 | 182 | /***************************************************************************/ 183 | /********************** Registers/Actions for sketch ***********************/ 184 | /***************************************************************************/ 185 | //depth 1 layer 1 186 | register sketch_reg_l1_d1 { 187 | width : 8; 188 | instance_count : SKETCH_WIDTH_1; 189 | attributes : saturating; 190 | } 191 | 192 | /* stateful-alu for 8-bit counters 193 | 1) predicate is open(1) as default. 194 | 2) condition is false(0) as default. 195 | Note that ALU's 8-bits only supports [-128:127]..*/ 196 | blackbox stateful_alu update_counter_l1_d1 { 197 | reg: sketch_reg_l1_d1; 198 | condition_lo: register_lo - mdata.const_8bits >= THETA_8BIT; // go or stop if >= 2^8 - 2 199 | update_lo_1_value: register_lo + 1; 200 | output_value: combined_predicate; // 1 bit => condition_lo 201 | output_dst: mdata.go_stop_l1_to_l2_d1; 202 | } 203 | //depth 1 layer 2 204 | register sketch_reg_l2_d1 { 205 | width: 16; 206 | instance_count : SKETCH_WIDTH_2; 207 | attributes : saturating; 208 | } 209 | 210 | 211 | /* 212 | Rule of Predicate 213 | cond_hi | cond_lo | predicate | combined_predicate 214 | 0 | 0 | 0001 | 0 215 | 0 | 1 | 0010 | 1 216 | 1 | 0 | 0100 | 1 217 | 1 | 1 | 1000 | 1 218 | */ 219 | 220 | // stateful-alu for 16-bit counters 221 | blackbox stateful_alu update_counter_l2_d1 { 222 | reg: sketch_reg_l2_d1; 223 | condition_lo: register_lo - mdata.const_16bits >= THETA_16BIT; // go or stop if >= 2^16 - 2 224 | update_lo_1_value: register_lo + 1; 225 | output_value: combined_predicate; // 1 bit => condition_lo 226 | output_dst: mdata.go_stop_l2_to_l3_d1; 227 | } 228 | //depth 1 layer 3 229 | register sketch_reg_l3_d1 { 230 | width: 32; 231 | instance_count : SKETCH_WIDTH_3; 232 | attributes : saturating; 233 | } 234 | 235 | // stateful-alu for 32-bit counters 236 | blackbox stateful_alu update_counter_l3_d1 { 237 | reg: sketch_reg_l3_d1; 238 | update_lo_1_value: register_lo + 1; 239 | } 240 | 241 | 242 | //depth 2 layer 1 243 | register sketch_reg_l1_d2 { 244 | width : 8; 245 | instance_count : SKETCH_WIDTH_1; 246 | attributes : saturating; 247 | } 248 | 249 | blackbox stateful_alu update_counter_l1_d2 { 250 | reg: sketch_reg_l1_d2; 251 | condition_lo: register_lo - mdata.const_8bits >= THETA_8BIT; // go or stop 252 | update_lo_1_value: register_lo + 1; 253 | output_value: combined_predicate; // 1 bit => condition_lo 254 | output_dst: mdata.go_stop_l1_to_l2_d2; 255 | } 256 | //depth 2 layer 2 257 | register sketch_reg_l2_d2 { 258 | width: 16; 259 | instance_count : SKETCH_WIDTH_2; 260 | attributes : saturating; 261 | } 262 | 263 | // stateful-alu for 16-bit counters 264 | blackbox stateful_alu update_counter_l2_d2 { 265 | reg: sketch_reg_l2_d2; 266 | condition_lo: register_lo - mdata.const_16bits >= THETA_16BIT; // go or stop 267 | update_lo_1_value: register_lo + 1; 268 | output_value: combined_predicate; // 1 bit => condition_lo 269 | output_dst: mdata.go_stop_l2_to_l3_d2; 270 | } 271 | //depth 2 layer 3 272 | register sketch_reg_l3_d2 { 273 | width: 32; 274 | instance_count : SKETCH_WIDTH_3; 275 | attributes : saturating; 276 | } 277 | 278 | blackbox stateful_alu update_counter_l3_d2 { 279 | reg: sketch_reg_l3_d2; 280 | update_lo_1_value: register_lo + 1; 281 | } 282 | 283 | action do_update_counter_l1_d1() { 284 | update_counter_l1_d1.execute_stateful_alu_from_hash(sketch_hash_d1); 285 | } 286 | 287 | action do_update_counter_l2_d1() { 288 | update_counter_l2_d1.execute_stateful_alu_from_hash(sketch_hash_d1); 289 | } 290 | 291 | action do_update_counter_l3_d1() { 292 | update_counter_l3_d1.execute_stateful_alu_from_hash(sketch_hash_d1); 293 | } 294 | 295 | 296 | action do_update_counter_l1_d2() { 297 | update_counter_l1_d2.execute_stateful_alu_from_hash(sketch_hash_d2); 298 | } 299 | 300 | action do_update_counter_l2_d2() { 301 | update_counter_l2_d2.execute_stateful_alu_from_hash(sketch_hash_d2); 302 | } 303 | 304 | action do_update_counter_l3_d2() { 305 | update_counter_l3_d2.execute_stateful_alu_from_hash(sketch_hash_d2); 306 | } 307 | 308 | /***************************************************************************/ 309 | /***************************** Action Tables ******************************/ 310 | /***************************************************************************/ 311 | 312 | /***** update sketch *******/ 313 | 314 | // update for sketch for depth 1 315 | @pragma stage 0 316 | table sketch_update_counter_l1_d1 { 317 | actions { 318 | do_update_counter_l1_d1; 319 | } 320 | default_action : do_update_counter_l1_d1; 321 | } 322 | @pragma stage 2 323 | table sketch_update_counter_l2_d1 { 324 | actions { 325 | do_update_counter_l2_d1; 326 | } 327 | default_action : do_update_counter_l2_d1; 328 | } 329 | @pragma stage 3 330 | table sketch_update_counter_l3_d1 { 331 | actions { 332 | do_update_counter_l3_d1; 333 | } 334 | default_action : do_update_counter_l3_d1; 335 | } 336 | 337 | // update for sketch for depth 2 338 | @pragma stage 1 339 | table sketch_update_counter_l1_d2 { 340 | actions { 341 | do_update_counter_l1_d2; 342 | } 343 | default_action : do_update_counter_l1_d2; 344 | } 345 | @pragma stage 2 346 | table sketch_update_counter_l2_d2 { 347 | actions { 348 | do_update_counter_l2_d2; 349 | } 350 | default_action : do_update_counter_l2_d2; 351 | } 352 | @pragma stage 3 353 | table sketch_update_counter_l3_d2 { 354 | actions { 355 | do_update_counter_l3_d2; 356 | } 357 | default_action : do_update_counter_l3_d2; 358 | } 359 | 360 | /***************************************************************************/ 361 | /********************************* Ingress *********************************/ 362 | /***************************************************************************/ 363 | 364 | control ingress { 365 | if (mdata.do_fcmsketch == 1) { // ipv4 366 | 367 | // depth 1 process 368 | // update counters at layer 1, depth 1 369 | apply(sketch_update_counter_l1_d1); // Stage 0 370 | apply(sketch_update_counter_l1_d2); // Stage 1 371 | if (mdata.go_stop_l1_to_l2_d1 == 1) { 372 | // update counters at layer 2, depth 1 373 | apply(sketch_update_counter_l2_d1); // Stage 2 374 | } 375 | if (mdata.go_stop_l1_to_l2_d2 == 1) { 376 | // update counters at layer 2, depth 2 377 | apply(sketch_update_counter_l2_d2); // Stage 2 378 | } 379 | if (mdata.go_stop_l2_to_l3_d1 == 1) { 380 | // update counters at layer 3, depth 1 381 | apply(sketch_update_counter_l3_d1); // Stage 3 382 | } 383 | if (mdata.go_stop_l2_to_l3_d2 == 1) { 384 | // update counters at layer 3, depth 2 385 | apply(sketch_update_counter_l3_d2); // Stage 3 386 | } 387 | } 388 | 389 | } 390 | /** Egress **/ 391 | control egress { 392 | } 393 | -------------------------------------------------------------------------------- /fcm_p416/README.md: -------------------------------------------------------------------------------- 1 | # P4-16 Source Code 2 | 3 | In this repository, the prototype P4 source code is written in P4-16, and compiled using version 9.2.0 of BF_SDE. 4 | 5 | ## PTF 6 | 7 | We support PTF for your experiments on your server. Here are the steps: 8 | 9 | 1. In `bf-sde-9.2.0` directory, use the command `./p4_build.sh {Directory of this repository}/fcm.p4 -j4` to build `fcm.p4`. 10 | 2. In `bf-sde-9.2.0` directory, use the command `./run_tofino_model.sh --arch tofino -p fcm` to run Tofino model. 11 | 3. Concurrently, with another terminal, use the command `./run_switchd.sh --arch tofino -p fcm` 12 | 4. Concurrently, with another terminal, use the command `./run_p4_tests.sh --arch tofino -p fcm -t {Directory of this repository}/ptf` to add TCAM entries, generate and send packets, and get queries with error calculation. 13 | 14 | The PTF python script is implemented on `ptf/test_fcm.py`. You can refer the hash calculations and cardinality estimation in `ptf/fcm_utils.py`. To use with the FCM simulator codes, you can change the hash functions in the simulator with c++ codes `ptf/hash_calc.cpp`. 15 | 16 | 17 | -------------------------------------------------------------------------------- /fcm_p416/common/headers.p4: -------------------------------------------------------------------------------- 1 | #ifndef _HEADERS_ 2 | #define _HEADERS_ 3 | 4 | typedef bit<48> mac_addr_t; 5 | typedef bit<32> ipv4_addr_t; 6 | typedef bit<16> ether_type_t; 7 | const ether_type_t ETHERTYPE_IPV4 = 16w0x0800; 8 | 9 | typedef bit<8> ip_protocol_t; 10 | const ip_protocol_t IP_PROTOCOLS_ICMP = 1; 11 | const ip_protocol_t IP_PROTOCOLS_TCP = 6; 12 | const ip_protocol_t IP_PROTOCOLS_UDP = 17; 13 | 14 | header ethernet_h { 15 | mac_addr_t dst_addr; 16 | mac_addr_t src_addr; 17 | bit<16> ether_type; 18 | } 19 | 20 | header ipv4_h { 21 | bit<4> version; 22 | bit<4> ihl; 23 | bit<8> diffserv; 24 | bit<16> total_len; 25 | bit<16> identification; 26 | bit<3> flags; 27 | bit<13> frag_offset; 28 | bit<8> ttl; 29 | bit<8> protocol; 30 | bit<16> hdr_checksum; 31 | ipv4_addr_t src_addr; 32 | ipv4_addr_t dst_addr; 33 | } 34 | 35 | header tcp_h { 36 | bit<16> src_port; 37 | bit<16> dst_port; 38 | bit<32> seq_no; 39 | bit<32> ack_no; 40 | bit<4> data_offset; 41 | bit<4> res; 42 | bit<8> flags; 43 | bit<16> window; 44 | bit<16> checksum; 45 | bit<16> urgent_ptr; 46 | } 47 | 48 | header udp_h { 49 | bit<16> src_port; 50 | bit<16> dst_port; 51 | bit<16> hdr_length; 52 | bit<16> checksum; 53 | } 54 | 55 | struct header_t { 56 | ethernet_h ethernet; 57 | ipv4_h ipv4; 58 | tcp_h tcp; 59 | udp_h udp; 60 | } 61 | 62 | // for empty egress 63 | struct empty_header_t {} 64 | struct empty_metadata_t {} 65 | 66 | #endif /* _HEADERS_ */ 67 | -------------------------------------------------------------------------------- /fcm_p416/common/util.p4: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_ 2 | #define _UTIL_ 3 | 4 | parser TofinoIngressParser( 5 | packet_in pkt, 6 | out ingress_intrinsic_metadata_t ig_intr_md){ 7 | state start { 8 | pkt.extract(ig_intr_md); 9 | transition parse_port_metadata; 10 | } 11 | state parse_port_metadata { 12 | pkt.advance(PORT_METADATA_SIZE); // macro defined in tofino.p4 13 | transition accept; 14 | } 15 | } 16 | 17 | // Empty egress parser/control blocks 18 | parser EmptyEgressParser( 19 | packet_in pkt, 20 | out empty_header_t hdr, 21 | out empty_metadata_t eg_md, 22 | out egress_intrinsic_metadata_t eg_intr_md) { 23 | state start { 24 | transition accept; 25 | } 26 | } 27 | 28 | control EmptyEgressDeparser( 29 | packet_out pkt, 30 | inout empty_header_t hdr, 31 | in empty_metadata_t eg_md, 32 | in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { 33 | apply {} 34 | } 35 | 36 | control EmptyEgress( 37 | inout empty_header_t hdr, 38 | inout empty_metadata_t eg_md, 39 | in egress_intrinsic_metadata_t eg_intr_md, 40 | in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, 41 | inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, 42 | inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { 43 | apply {} 44 | } 45 | 46 | #endif /* _UTIL */ 47 | -------------------------------------------------------------------------------- /fcm_p416/fcm.p4: -------------------------------------------------------------------------------- 1 | /* -*- P4_16 -*- */ 2 | 3 | // ------------------------------------------------------------ 4 | // FCM-Sketch P4-16 Implementation 5 | // 6 | // - Flow identification 7 | // You can define our flow identification (currrnetly, we use srcIP) 8 | // in action 'fcm_action_calc_hash_d1' and 'fcm_action_calc_hash_d2'. 9 | // 10 | // - Data Plane Queries 11 | // - Flow Size estimation is supported by bit<32> flow_size. 12 | // Of course, you can in turn classify Heavy hitters and Heavy changes. 13 | // - Cardinality is supported by bit<32> cardinality. 14 | // We reduced the TCAM entries via sensitivity analysis of LC estimator. 15 | // It is a kind of adaptive spacing between TCAM entries with additional error. 16 | // Ideally, we can reduce 100X of TCAM entries with only 0.3 % additional error. 17 | // - Control Plane Queries : Read the bottom of "test_fcm.py". 18 | // - Flow Size distribution 19 | // - Entropy 20 | // 21 | // ------------------------------------------------------------ 22 | 23 | 24 | 25 | #include 26 | #if __TARGET_TOFINO__ == 2 27 | #include 28 | #else 29 | #include 30 | #endif 31 | 32 | #include "common/headers.p4" 33 | #include "common/util.p4" 34 | 35 | 36 | #define SKETCH_W1 0x80000 // 8 bits, width at layer 1, 2^19 = 524288 37 | #define SKETCH_W2 0x10000 // 16 bits, width at layer 2, 2^16 = 65536 38 | #define SKETCH_W3 0x2000 // 32 bits, width at layer 3, 2^13 = 8192 39 | 40 | #define ADD_LEVEL1 0x000000ff // 2^8 - 2 + 1 (property of SALU) 41 | #define ADD_LEVEL2 0x000100fd // (2^8 - 2) + (2^16 - 2) + 1 (property of SALU) 42 | 43 | 44 | // --------------------------------------------------------------------------- 45 | // Metadata fields 46 | // --------------------------------------------------------------------------- 47 | 48 | // metadata fields for fcm 49 | struct fcm_metadata_t { 50 | bit<32> hash_meta_d1; 51 | bit<32> hash_meta_d2; 52 | 53 | 54 | bit<32> result_d1; 55 | bit<32> result_d2; 56 | bit<32> increment_occupied; 57 | } 58 | 59 | // for ingress metadata 60 | struct metadata_t { 61 | fcm_metadata_t fcm_mdata; 62 | } 63 | 64 | // --------------------------------------------------------------------------- 65 | // Ingress parser 66 | // --------------------------------------------------------------------------- 67 | 68 | parser SwitchIngressParser( 69 | packet_in pkt, 70 | out header_t hdr, 71 | out metadata_t ig_md, 72 | out ingress_intrinsic_metadata_t ig_intr_md) { 73 | 74 | TofinoIngressParser() tofino_parser; 75 | 76 | state start { 77 | tofino_parser.apply(pkt, ig_intr_md); 78 | // initialize metadata 79 | ig_md.fcm_mdata.result_d1 = 0; 80 | ig_md.fcm_mdata.result_d2 = 0; 81 | ig_md.fcm_mdata.increment_occupied = 0; 82 | 83 | transition parse_ethernet; 84 | } 85 | 86 | state parse_ethernet { 87 | pkt.extract(hdr.ethernet); 88 | transition select (hdr.ethernet.ether_type) { 89 | ETHERTYPE_IPV4 : parse_ipv4; 90 | default : reject; 91 | } 92 | } 93 | 94 | state parse_ipv4 { 95 | pkt.extract(hdr.ipv4); 96 | transition select(hdr.ipv4.protocol) { 97 | IP_PROTOCOLS_TCP : parse_tcp; 98 | IP_PROTOCOLS_UDP : parse_udp; 99 | default : accept; 100 | } 101 | } 102 | 103 | state parse_tcp { 104 | pkt.extract(hdr.tcp); 105 | transition select(hdr.ipv4.total_len) { 106 | default : accept; 107 | } 108 | } 109 | 110 | state parse_udp { 111 | pkt.extract(hdr.udp); 112 | transition select(hdr.udp.dst_port) { 113 | default: accept; 114 | } 115 | } 116 | } 117 | 118 | // --------------------------------------------------------------------------- 119 | // Ingress Deparser 120 | // --------------------------------------------------------------------------- 121 | control SwitchIngressDeparser( 122 | packet_out pkt, 123 | inout header_t hdr, 124 | in metadata_t ig_md, 125 | in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { 126 | 127 | apply { 128 | pkt.emit(hdr); 129 | pkt.emit(hdr.ipv4); 130 | pkt.emit(hdr.tcp); 131 | pkt.emit(hdr.udp); 132 | } 133 | } 134 | 135 | // --------------------------------------------------------------------------- 136 | // FCM logic control block 137 | // --------------------------------------------------------------------------- 138 | control FCMSketch ( 139 | inout header_t hdr, 140 | out fcm_metadata_t fcm_mdata, 141 | out bit<19> num_occupied_reg, 142 | out bit<32> flow_size, 143 | out bit<32> cardinality) { 144 | 145 | // +++++++++++++++++++ 146 | // hashings & hash action 147 | // +++++++++++++++++++ 148 | 149 | CRCPolynomial>(32w0x04C11DB7, // polynomial 150 | true, // reversed 151 | false, // use msb? 152 | false, // extended? 153 | 32w0xFFFFFFFF, // initial shift register value 154 | 32w0xFFFFFFFF // result xor 155 | ) CRC32; 156 | Hash>(HashAlgorithm_t.CUSTOM, CRC32) hash_d1; 157 | 158 | // Hash>(HashAlgorithm_t.CRC32) hash_d1; 159 | 160 | 161 | CRCPolynomial>(32w0x04C11DB7, 162 | false, 163 | false, 164 | false, 165 | 32w0xFFFFFFFF, 166 | 32w0x00000000 167 | ) CRC32_MPEG; 168 | Hash>(HashAlgorithm_t.CUSTOM, CRC32_MPEG) hash_d2; 169 | 170 | 171 | // +++++++++++++++++++ 172 | // registers 173 | // +++++++++++++++++++ 174 | 175 | Register, bit<19>>(SKETCH_W1) sketch_reg_l1_d1; 176 | Register, bit<16>>(SKETCH_W2) sketch_reg_l2_d1; 177 | Register, bit<13>>(SKETCH_W3) sketch_reg_l3_d1; 178 | 179 | Register, bit<19>>(SKETCH_W1) sketch_reg_l1_d2; 180 | Register, bit<16>>(SKETCH_W2) sketch_reg_l2_d2; 181 | Register, bit<13>>(SKETCH_W3) sketch_reg_l3_d2; 182 | 183 | // total number of empty registers for all trees 184 | Register, _>(1) reg_num_empty; 185 | 186 | // +++++++++++++++++++ 187 | // register actions 188 | // +++++++++++++++++++ 189 | 190 | // level 1, depth 1 191 | RegisterAction, bit<19>, bit<32>>(sketch_reg_l1_d1) increment_l1_d1 = { 192 | void apply(inout bit<8> value, out bit<32> result) { 193 | value = value |+| 1; 194 | result = (bit<32>)value; // return level 1 value (255 -> count 254) 195 | } 196 | }; 197 | // level 2, depth 1, only when level 1 output is 255 198 | RegisterAction, bit<16>, bit<32>>(sketch_reg_l2_d1) increment_l2_d1 = { 199 | void apply(inout bit<16> value, out bit<32> result) { 200 | result = (bit<32>)value + ADD_LEVEL1; // return level 1 + 2 201 | value = value |+| 1; 202 | } 203 | }; 204 | // level 3, depth 1, only when level 2 output is 65789 205 | RegisterAction, bit<13>, bit<32>>(sketch_reg_l3_d1) increment_l3_d1 = { 206 | void apply(inout bit<32> value, out bit<32> result) { 207 | result = value + ADD_LEVEL2; // return level 1 + 2 + 3 208 | value = value |+| 1; 209 | 210 | } 211 | }; 212 | 213 | // level 1, depth 2 214 | RegisterAction, bit<19>, bit<32>>(sketch_reg_l1_d2) increment_l1_d2 = { 215 | void apply(inout bit<8> value, out bit<32> result) { 216 | value = value |+| 1; 217 | result = (bit<32>)value; // return level 1 value (255 -> count 254) 218 | } 219 | }; 220 | // level 2, depth 2, only when level 1 output is 255 221 | RegisterAction, bit<16>, bit<32>>(sketch_reg_l2_d2) increment_l2_d2 = { 222 | void apply(inout bit<16> value, out bit<32> result) { 223 | result = (bit<32>)value + ADD_LEVEL1; // return level 1 + 2 224 | value = value |+| 1; 225 | } 226 | }; 227 | // level 3, depth 2, only when level 2 output is 65789 228 | RegisterAction, bit<13>, bit<32>>(sketch_reg_l3_d2) increment_l3_d2 = { 229 | void apply(inout bit<32> value, out bit<32> result) { 230 | result = value + ADD_LEVEL2; // return level 1 + 2 + 3 231 | value = value |+| 1; // increment assuming no 32-bit overflow 232 | 233 | } 234 | }; 235 | 236 | // increment number of empty register value for cardinality 237 | RegisterAction, _, bit<32>>(reg_num_empty) increment_occupied_reg = { 238 | void apply(inout bit<32> value, out bit<32> result) { 239 | result = value + fcm_mdata.increment_occupied; 240 | value = value + fcm_mdata.increment_occupied; 241 | } 242 | }; 243 | 244 | 245 | // +++++++++++++++++++ 246 | // actions 247 | // +++++++++++++++++++ 248 | 249 | // action for level 1, depth 1, you can re-define the flow key identification 250 | action fcm_action_l1_d1() { 251 | fcm_mdata.result_d1 = increment_l1_d1.execute(hash_d1.get({ hdr.ipv4.src_addr })[18:0]); 252 | } 253 | // action for level 2, depth 1 254 | action fcm_action_l2_d1() { 255 | fcm_mdata.result_d1 = increment_l2_d1.execute(hash_d1.get({ hdr.ipv4.src_addr })[18:3]); 256 | } 257 | // action for level 3, depth 1 258 | action fcm_action_l3_d1() { 259 | fcm_mdata.result_d1 = increment_l3_d1.execute(hash_d1.get({ hdr.ipv4.src_addr })[18:6]); 260 | } 261 | 262 | // action for level 1, depth 2, you can re-define the flow key identification 263 | action fcm_action_l1_d2() { 264 | fcm_mdata.result_d2 = increment_l1_d2.execute(hash_d2.get({ hdr.ipv4.src_addr })[18:0]); 265 | } 266 | // action for level 2, depth 2 267 | action fcm_action_l2_d2() { 268 | fcm_mdata.result_d2 = increment_l2_d2.execute(hash_d2.get({ hdr.ipv4.src_addr })[18:0][18:3]); 269 | } 270 | // action for level 3, depth 2 271 | action fcm_action_l3_d2() { 272 | fcm_mdata.result_d2 = increment_l3_d2.execute(hash_d2.get({ hdr.ipv4.src_addr })[18:0][18:6]); 273 | } 274 | 275 | 276 | // increment reg of occupied leaf number 277 | action fcm_action_increment_cardreg() { 278 | num_occupied_reg = (increment_occupied_reg.execute(0))[19:1]; 279 | } 280 | 281 | action fcm_action_check_occupied(bit<32> increment_val) { 282 | fcm_mdata.increment_occupied = increment_val; 283 | } 284 | 285 | 286 | action fcm_action_set_cardinality(bit<32> card_match) { 287 | cardinality = card_match; 288 | } 289 | 290 | // +++++++++++++++++++ 291 | // tables 292 | // +++++++++++++++++++ 293 | 294 | // if level 1 is full, move to level 2. 295 | table tb_fcm_l1_to_l2_d1 { 296 | key = { 297 | fcm_mdata.result_d1 : exact; 298 | } 299 | actions = { 300 | fcm_action_l2_d1; 301 | @defaultonly NoAction; 302 | } 303 | const default_action = NoAction(); 304 | const entries = { 305 | 32w255: fcm_action_l2_d1(); 306 | } 307 | size = 2; 308 | } 309 | 310 | // if level 2 is full, move to level 3. 311 | table tb_fcm_l2_to_l3_d1 { 312 | key = { 313 | fcm_mdata.result_d1 : exact; 314 | } 315 | actions = { 316 | fcm_action_l3_d1; 317 | @defaultonly NoAction; 318 | } 319 | const default_action = NoAction(); 320 | const entries = { 321 | 32w65789: fcm_action_l3_d1(); 322 | } 323 | size = 2; 324 | } 325 | 326 | // if level 1 is full, move to level 2. 327 | table tb_fcm_l1_to_l2_d2 { 328 | key = { 329 | fcm_mdata.result_d2 : exact; 330 | } 331 | actions = { 332 | fcm_action_l2_d2; 333 | @defaultonly NoAction; 334 | } 335 | const default_action = NoAction(); 336 | const entries = { 337 | 32w255: fcm_action_l2_d2(); 338 | } 339 | size = 2; 340 | } 341 | 342 | // if level 2 is full, move to level 3. 343 | table tb_fcm_l2_to_l3_d2 { 344 | key = { 345 | fcm_mdata.result_d2 : exact; 346 | } 347 | actions = { 348 | fcm_action_l3_d2; 349 | @defaultonly NoAction; 350 | } 351 | const default_action = NoAction(); 352 | const entries = { 353 | 32w65789: fcm_action_l3_d2(); 354 | } 355 | size = 2; 356 | } 357 | 358 | // Update the number of occupied leaf nodes 359 | table tb_fcm_increment_occupied { 360 | key = { 361 | fcm_mdata.result_d1 : ternary; 362 | fcm_mdata.result_d2 : ternary; 363 | } 364 | actions = { 365 | fcm_action_check_occupied; 366 | } 367 | const default_action = fcm_action_check_occupied(0); 368 | const entries = { 369 | (32w1, 32w1) : fcm_action_check_occupied(2); 370 | (32w1, _) : fcm_action_check_occupied(1); 371 | (_, 32w1) : fcm_action_check_occupied(1); 372 | } 373 | size = 4; 374 | } 375 | 376 | 377 | // look up LC cardinality using number of empty counters at level 1 378 | // [30:12] : divide by 2 ("average" empty_reg number). 379 | // Each array size is 2 ** 19, so slice 19 bits 380 | table tb_fcm_cardinality { 381 | key = { 382 | num_occupied_reg : range; // 19 bits 383 | } 384 | actions = { 385 | fcm_action_set_cardinality; 386 | } 387 | const default_action = fcm_action_set_cardinality(0); 388 | size = 4096; 389 | } 390 | 391 | 392 | // +++++++++++++++++++ 393 | // apply 394 | // +++++++++++++++++++ 395 | apply { 396 | fcm_action_l1_d1(); // increment level 1, depth 1 397 | fcm_action_l1_d2(); // increment level 1, depth 2 398 | /* increment the number of occupied leaf nodes */ 399 | tb_fcm_increment_occupied.apply(); 400 | fcm_action_increment_cardreg(); 401 | tb_fcm_cardinality.apply(); // calculate cardinality estimate 402 | tb_fcm_l1_to_l2_d1.apply(); // conditional increment level 2, depth 1 403 | tb_fcm_l1_to_l2_d2.apply(); // conditional increment level 2, depth 2 404 | tb_fcm_l2_to_l3_d1.apply(); // conditional increment level 3, depth 1 405 | tb_fcm_l2_to_l3_d2.apply(); // conditional increment level 3, depth 2 406 | 407 | /* Take minimum for final count-query. */ 408 | flow_size = fcm_mdata.result_d1 > fcm_mdata.result_d2 ? fcm_mdata.result_d2 : fcm_mdata.result_d1; 409 | } 410 | } 411 | 412 | 413 | control SwitchIngress( 414 | inout header_t hdr, 415 | inout metadata_t ig_md, 416 | in ingress_intrinsic_metadata_t ig_intr_md, 417 | in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, 418 | inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, 419 | inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { 420 | 421 | 422 | /*** temp ***/ 423 | // increment when packet comes in 424 | Register, _>(1, 0) num_pkt; 425 | RegisterAction, _, bit<32>>(num_pkt) increment_pkt = { 426 | void apply(inout bit<32> value, out bit<32> result) { 427 | value = value |+| 1; 428 | result = value; 429 | } 430 | }; 431 | 432 | action count_pkt() { 433 | increment_pkt.execute(0); 434 | } 435 | /*** temp ***/ 436 | 437 | 438 | FCMSketch() fcmsketch; 439 | apply { 440 | bit<19> num_occupied_reg; // local variable for cardinality 441 | bit<32> flow_size; // local variable for final query 442 | bit<32> cardinality; // local variable for final query 443 | 444 | count_pkt(); // temp 445 | fcmsketch.apply(hdr, 446 | ig_md.fcm_mdata, 447 | num_occupied_reg, 448 | flow_size, 449 | cardinality); 450 | } 451 | } 452 | 453 | 454 | 455 | 456 | Pipeline(SwitchIngressParser(), 457 | SwitchIngress(), 458 | SwitchIngressDeparser(), 459 | EmptyEgressParser(), 460 | EmptyEgress(), 461 | EmptyEgressDeparser() 462 | ) pipe; 463 | 464 | Switch(pipe) main; 465 | 466 | 467 | -------------------------------------------------------------------------------- /fcm_p416/ptf/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fcm_p416/ptf/fcm_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import math 3 | import zlib 4 | import struct 5 | 6 | class bcolors: 7 | HEADER = '\033[95m' 8 | OKBLUE = '\033[94m' 9 | OKCYAN = '\033[96m' 10 | OKGREEN = '\033[92m' 11 | WARNING = '\033[93m' 12 | FAIL = '\033[91m' 13 | ENDC = '\033[0m' 14 | BOLD = '\033[1m' 15 | UNDERLINE = '\033[4m' 16 | 17 | 18 | # Cardinality with adaptive spacing for saving TCAM, using the sensitivity of LC estimator. 19 | # This technique makes an additional error at most 0.3 % 20 | def lc_cardinality(m_occupied, m): 21 | return m * np.log(m / float(m-m_occupied)) 22 | 23 | def lc_delta_m0(m_occupied, m, epsilon): 24 | return (m - m_occupied) * np.log(m / float(m - m_occupied)) * epsilon 25 | 26 | 27 | def fcm_crc32_mpeg2(msg="192.168.1.1"): 28 | crc = np.uint32(0xFFFFFFFF) 29 | msb = np.uint32(0) 30 | msg_arr = msg.split(".") # convert IP address to 8-bit values 31 | 32 | for i in range(len(msg_arr)): 33 | # xor next byte to upper bits of crc 34 | crc ^= np.uint32(np.uint32(msg_arr[i]) << 24) 35 | for j in range(8): 36 | msb = np.uint32(crc >> 31) 37 | crc = np.uint32(crc << 1) 38 | crc = np.uint32(crc ^ (np.uint32(0 - msb) & np.uint32(0x04C11DB7))); 39 | return crc 40 | 41 | def do_crc(s): 42 | n = zlib.crc32(s) 43 | return n + (1<<32) if n < 0 else n 44 | 45 | def fcm_crc32(msg="192.168.1.1"): 46 | msg_arr = msg.split(".") 47 | msg_val = b'' 48 | for i in range(len(msg_arr)): 49 | msg_val = msg_val + struct.pack("B", int(msg_arr[i])) 50 | # msg_val = b'\xc0\xa8\x01\x01' 51 | return do_crc(msg_val) 52 | 53 | 54 | # print("util directory - test : %f" % lc_cardinality(10000, 524288)) 55 | ## debugging 56 | # print(fcm_crc32() % 524288 ) # 408597 57 | # print(fcm_crc32_mpeg2() % 524288 ) # 465664 -------------------------------------------------------------------------------- /fcm_p416/ptf/hash_calc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // width=32 poly=0x04c11db7 init=0xffffffff refin=false refout=false xorout=0x00000000 check=0x0376e6e7 residue=0x00000000 name="CRC-32/MPEG-2" 13 | uint32_t crc32b(uint8_t *message, int l) { 14 | int i, j; 15 | uint32_t crc, msb; 16 | 17 | crc = 0xFFFFFFFF; 18 | for(i = 0; i < l; i++) { 19 | // xor next byte to upper bits of crc 20 | crc ^= (((uint32_t)message[i])<<24); 21 | for (j = 0; j < 8; j++) { // Do eight times. 22 | msb = crc>>31; 23 | crc <<= 1; 24 | crc ^= (0 - msb) & 0x04C11DB7; 25 | } 26 | } 27 | return crc; // don't complement crc on output 28 | } 29 | 30 | 31 | // width=32 poly=0x000000af init=0x00000000 refin=false refout=false xorout=0x00000000 check=0xbd0be338 residue=0x00000000 name="CRC-32/XFER" 32 | uint32_t crc32_xfer(uint8_t *message, int l) 33 | { 34 | int i, j; 35 | uint32_t crc, msb; 36 | 37 | crc = 0x00000000; 38 | for(i = 0; i < l; i++) { 39 | // xor next byte to upper bits of crc 40 | crc ^= (((uint32_t)message[i])<<24); 41 | for (j = 0; j < 8; j++) { // Do eight times. 42 | msb = crc>>31; 43 | crc <<= 1; 44 | crc ^= (0 - msb) & 0x000000af; 45 | } 46 | } 47 | return crc; // don't complement crc on output 48 | } 49 | 50 | 51 | // width=32 poly=0x814141ab init=0x00000000 refin=false refout=false xorout=0x00000000 check=0x3010bf7f residue=0x00000000 name="CRC-32/AIXM" 52 | uint32_t crc32q(uint8_t *message, int l) 53 | { 54 | int i, j; 55 | uint32_t crc, msb; 56 | 57 | crc = 0x0; 58 | for(i = 0; i < l; i++) { 59 | // xor next byte to upper bits of crc 60 | crc ^= (((uint32_t)message[i])<<24); 61 | for (j = 0; j < 8; j++) { // Do eight times. 62 | msb = crc>>31; 63 | crc <<= 1; 64 | crc ^= (0 - msb) & 0x814141AB; 65 | } 66 | } 67 | return crc; // don't complement crc on output 68 | } 69 | 70 | 71 | /************* CRC32C *********************/ 72 | 73 | #define POLY 0x82f63b78 74 | static uint32_t crc32c_table[8][256]; 75 | static pthread_once_t crc32c_once_sw = PTHREAD_ONCE_INIT; 76 | /* Construct table for software CRC-32C calculation. */ 77 | static void crc32c_init_sw(void) 78 | { 79 | uint32_t n, crc, k; 80 | 81 | for (n = 0; n < 256; n++) { 82 | crc = n; 83 | crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; 84 | crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; 85 | crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; 86 | crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; 87 | crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; 88 | crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; 89 | crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; 90 | crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; 91 | crc32c_table[0][n] = crc; 92 | } 93 | for (n = 0; n < 256; n++) { 94 | crc = crc32c_table[0][n]; 95 | for (k = 1; k < 8; k++) { 96 | crc = crc32c_table[0][crc & 0xff] ^ (crc >> 8); 97 | crc32c_table[k][n] = crc; 98 | } 99 | } 100 | } 101 | 102 | /* Table-driven software version as a fall-back. This is about 15 times slower 103 | than using the hardware instructions. This assumes little-endian integers, 104 | as is the case on Intel processors that the assembler code here is for. */ 105 | static uint32_t crc32c_sw(uint32_t crci, const uint8_t *buf, size_t len) 106 | { 107 | const unsigned char *next = buf; 108 | uint64_t crc; 109 | 110 | pthread_once(&crc32c_once_sw, crc32c_init_sw); 111 | crc = crci ^ 0xffffffff; 112 | // crc = crci ^ 0x80000000; 113 | while (len && ((uintptr_t)next & 7) != 0) { 114 | crc = crc32c_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); 115 | len--; 116 | } 117 | while (len >= 8) { 118 | crc ^= *(uint64_t *)next; 119 | crc = crc32c_table[7][crc & 0xff] ^ 120 | crc32c_table[6][(crc >> 8) & 0xff] ^ 121 | crc32c_table[5][(crc >> 16) & 0xff] ^ 122 | crc32c_table[4][(crc >> 24) & 0xff] ^ 123 | crc32c_table[3][(crc >> 32) & 0xff] ^ 124 | crc32c_table[2][(crc >> 40) & 0xff] ^ 125 | crc32c_table[1][(crc >> 48) & 0xff] ^ 126 | crc32c_table[0][crc >> 56]; 127 | next += 8; 128 | len -= 8; 129 | } 130 | while (len) { 131 | crc = crc32c_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); 132 | len--; 133 | } 134 | return (uint32_t)crc ^ 0xffffffff; 135 | // return (uint32_t)crc ^ 0x80000000; 136 | } 137 | 138 | /* 139 | To compiile this file, use the command 140 | g++-5 hash_calc.cpp -lz -lpthread -o hash_calc 141 | */ 142 | 143 | int main() 144 | { 145 | // hexidecimal, binary, decimal 146 | /* 147 | CRC32 value is : 408597 148 | crc32b : 465664 149 | CRC32c_sw value is: 161017 150 | */ 151 | // // 0xC0, 0xA8, 0x01, 0x01 152 | uint8_t bytes[4] = {192, 168, 1, 1}; 153 | uint32_t crc = crc32(0L, Z_NULL, 0); 154 | for (int i = 0; i < 4; ++i){ 155 | crc = crc32(crc, bytes + i, 1); 156 | } 157 | printf("CRC32 value is : %lu\n", crc); 158 | 159 | /*** CRC32/mpeg2 160 | 3215923968 -> 465664 (crc_32_mpeg) ***/ 161 | uint8_t buffer[] = {192, 168, 1, 1}; 162 | uint32_t result = crc32b(buffer, 4); 163 | printf("crc32b : %lu\n", result); 164 | 165 | /**** CRC32c test ******/ 166 | uint32_t crc32c = crc32c_sw(0L, Z_NULL, 0); 167 | for (int i = 0; i < 4; ++i) 168 | crc32c = crc32c_sw(crc32c, bytes + i, 1); 169 | printf("CRC32c_sw value is: %lu\n", crc32c); 170 | 171 | } 172 | 173 | 174 | -------------------------------------------------------------------------------- /fcm_p416/ptf/test_fcm.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from ptf import config 4 | from collections import namedtuple 5 | import ptf.testutils as testutils 6 | from bfruntime_client_base_tests import BfRuntimeTest 7 | import bfrt_grpc.bfruntime_pb2 as bfruntime_pb2 8 | import bfrt_grpc.client as gc 9 | import random 10 | import time 11 | 12 | from fcm_utils import * 13 | 14 | ## SKETCH CONSTANT VALUES ## 15 | 16 | DEBUGGING = False # debugging? (True -> yes) 17 | FROM_HW = True # From Tofino hardware model, must be True. 18 | SKETCH_W1 = 524288 # 8-bit, level 1 19 | SKETCH_W2 = 65536 # 16-bit, level 2 20 | SKETCH_W3 = 8192 # 32-bit, level 3 21 | ADD_LEVEL1 = 255 # 2^8 -2 + 1 (actual count is 254) 22 | ADD_LEVEL2 = 65789 # (2^8 - 2) + (2^16 - 2) + 1 (actual count is 65788) 23 | 24 | 25 | # Cardinality with adaptive spacing for saving TCAM, using the sensitivity of LC estimator. 26 | # This technique makes an additional error at most 0.3 % 27 | n_leaf = SKETCH_W1 # number of leaf nodes at a single tree 28 | epsilon = 0.003 # 0.3 % 29 | m_occupied = 0 # number of occupied registers 30 | n_entry = 0 # number of entries 31 | match_table = [None] * (n_leaf) # for PTF-testbed 32 | range_table = [(None, None)] * (n_leaf-1) # for TCAM range, a <= x < b 33 | 34 | while m_occupied <= n_leaf -1: 35 | if (m_occupied is 0): 36 | match_table[m_occupied] = 0 # no insertion, card = 0 37 | m_occupied += 1 38 | n_entry += 1 39 | continue 40 | # minimum spacing is 1 41 | delta = int(math.floor(max(lc_delta_m0(m_occupied, n_leaf, epsilon), 1))) 42 | # here we use under-estimation for simplicity, but we can further improve. 43 | est_card = int(lc_cardinality(m_occupied, n_leaf)) 44 | 45 | for i in range(delta): 46 | if (m_occupied + i <= n_leaf-1): 47 | match_table[m_occupied + i] = est_card 48 | range_table[n_entry] = (m_occupied, m_occupied + delta) 49 | m_occupied += delta 50 | n_entry += 1 51 | 52 | 53 | """ This test module is to evaluate fcm.p4 """ 54 | 55 | logger = logging.getLogger('Test') 56 | if not len(logger.handlers): 57 | logger.addHandler(logging.StreamHandler()) 58 | 59 | swports = [] 60 | for device, port, ifname in config["interfaces"]: 61 | swports.append(port) 62 | swports.sort() 63 | 64 | if swports == []: 65 | swports = list(range(9)) 66 | 67 | 68 | 69 | class FCMTest(BfRuntimeTest): 70 | """@brief This test inserts multiple random TCP packets, and evaluates the accuracy of flow size, cardinality, and possibly, flow size distribution and entropy. In fcm.p4, the flow ID is source IP. 71 | """ 72 | def setUp(self): 73 | client_id = 0 74 | BfRuntimeTest.setUp(self, client_id) 75 | # p4_name = "fcm" 76 | # BfRuntimeTest.setUp(self, client_id, p4_name) 77 | 78 | 79 | def runTest(self): 80 | ig_port = swports[1] 81 | num_pipes = int(testutils.test_param_get('num_pipes')) 82 | 83 | logger.info("[INFO-Switch] Adding TCAM Ranges for cardinality...") 84 | bfrt_info = self.interface.bfrt_info_get("fcm") 85 | card_range_tcam = bfrt_info.table_get("SwitchIngress.fcmsketch.tb_fcm_cardinality") 86 | target = gc.Target(device_id=0, pipe_id=0xffff) 87 | 88 | 89 | NUM_FLOWS = 1000 # number of sample flows 90 | MAX_FLOW_SIZE = 10 # max size of flows 91 | num_packets = 0 # PTF generates 40 packets per second 92 | seed = 30669 # random seed 93 | random.seed(seed) 94 | logger.info("Seed used %d" % seed) 95 | 96 | # info of sample packet 97 | dmac = "00:11:22:33:44:55" 98 | ip_src = None # random flow ID 99 | ip_dst = "192.168.1.1" 100 | ip_src_list = [] 101 | 102 | 103 | # # Make sure the table starts off empty 104 | resp_empty = card_range_tcam.entry_get(target, None, {"from_hw": FROM_HW}) 105 | for data, key in resp_empty: 106 | assert 0, "Shouldn't have hit here since table is supposed to be empty" 107 | 108 | # Insert TCAM Range Entries 109 | for i in range(1, n_entry-1): 110 | if DEBUGGING: 111 | logger.info("Rule %d : Card %d <- range (%d, %d)", i, int(lc_cardinality(float(range_table[i][0]), float(n_leaf))), range_table[i][0], range_table[i][1]) 112 | # add entry 113 | card_range_tcam.entry_add(target, 114 | [card_range_tcam.make_key( 115 | [gc.KeyTuple('$MATCH_PRIORITY', 1), 116 | gc.KeyTuple('num_occupied_reg', low=int(range_table[i][0]), high=int(range_table[i][1]))] 117 | )], 118 | [card_range_tcam.make_data([gc.DataTuple('card_match', int(lc_cardinality(float(range_table[i][0]), float(n_leaf))))], 119 | 'SwitchIngress.fcmsketch.fcm_action_set_cardinality')] 120 | ) 121 | logger.info("[INFO-Switch] TCAM Ranges %d entries are successfully added.", n_entry) 122 | 123 | 124 | 125 | # generate random packets and insert 126 | logger.info("[INFO-Switch] Sending %d flows on port %d", NUM_FLOWS, ig_port) 127 | for i in range(NUM_FLOWS): 128 | flow_size = random.randint(1, MAX_FLOW_SIZE) # 1 ~ 10 129 | num_packets += flow_size 130 | ip_src = "%d.%d.%d.%d" % (random.randint(0, 255), random.randint(0, 255), 131 | random.randint(0, 255), random.randint(0, 255)) 132 | ip_src_list.append((ip_src, flow_size)); 133 | # sending packets 134 | pkt = testutils.simple_tcp_packet(eth_dst=dmac, ip_src=ip_src, ip_dst=ip_dst) 135 | testutils.send_packet(self, ig_port, pkt, count=flow_size) 136 | if (i % 100 is 0): 137 | logger.info("[INFO-Switch] %d / %d flows are done...", i, NUM_FLOWS) 138 | 139 | 140 | logger.info("[INFO-Switch] Wait until packets are all received...") 141 | # check all packets are sent 142 | while True: 143 | register_pktcount = bfrt_info.table_get("SwitchIngress.num_pkt") 144 | register_pktcount.operations_execute(target, 'Sync') 145 | 146 | resp_pktcount = register_pktcount.entry_get(target, 147 | [register_pktcount.make_key([gc.KeyTuple('$REGISTER_INDEX', 0)])], 148 | {"from_hw": FROM_HW}) 149 | data, _ = next(resp_pktcount) 150 | data_dict = data.to_dict() 151 | 152 | logger.info("[INFO-Switch] Sent : %d, Received : %d\t\t wait 10s more...", num_packets, data_dict["SwitchIngress.num_pkt.f1"][0]) 153 | if (data_dict["SwitchIngress.num_pkt.f1"][0] == num_packets): 154 | break 155 | time.sleep(10) 156 | 157 | assert data_dict["SwitchIngress.num_pkt.f1"][0] == num_packets, "Error: Packets are not correctly inserted..." 158 | 159 | 160 | 161 | # call the register values and get flow size estimation 162 | logger.info("[INFO-FCM] Start query processing...") 163 | ARE = 0 164 | AAE = 0 165 | Card_RE = 0 166 | for i in range(NUM_FLOWS): 167 | # Flow size 168 | 169 | ## depth 1, level 1 170 | hash_d1 = fcm_crc32(ip_src_list[i][0]) 171 | register_l1_d1 = bfrt_info.table_get("SwitchIngress.fcmsketch.sketch_reg_l1_d1") 172 | resp_l1_d1 = register_l1_d1.entry_get(target, 173 | [register_l1_d1.make_key([gc.KeyTuple('$REGISTER_INDEX', hash_d1 % SKETCH_W1)])], 174 | {"from_hw": FROM_HW}) 175 | data_d1, _ = next(resp_l1_d1) 176 | data_d1_dict = data_d1.to_dict() 177 | val_d1 = data_d1_dict["SwitchIngress.fcmsketch.sketch_reg_l1_d1.f1"][0] 178 | # overflow to level 2? 179 | if (val_d1 is ADD_LEVEL1): 180 | register_l2_d1 = bfrt_info.table_get("SwitchIngress.fcmsketch.sketch_reg_l2_d1") 181 | resp_l2_d1 = register_l2_d1.entry_get(target, 182 | [register_l2_d1.make_key([gc.KeyTuple('$REGISTER_INDEX', hash_d1 % SKETCH_W2)])], 183 | {"from_hw": FROM_HW}) 184 | data_d1, _ = next(resp_l2_d1) 185 | data_d1_dict = data_d1.to_dict() 186 | val_d1 = data_d1_dict["SwitchIngress.fcmsketch.sketch_reg_l2_d1.f1"][0] + ADD_LEVEL1 - 1 187 | # overflow to level 3? 188 | if (val_d1 is ADD_LEVEL2): 189 | register_l3_d1 = bfrt_info.table_get("SwitchIngress.fcmsketch.sketch_reg_l3_d1") 190 | resp_l3_d1 = register_l3_d1.entry_get(target, 191 | [register_l3_d1.make_key([gc.KeyTuple('$REGISTER_INDEX', hash_d1 % SKETCH_W3)])], 192 | {"from_hw": FROM_HW}) 193 | data_d1, _ = next(resp_l3_d1) 194 | data_d1_dict = data_d1.to_dict() 195 | val_d1 = data_d1_dict["SwitchIngress.fcmsketch.sketch_reg_l3_d1.f1"][0] + ADD_LEVEL2 - 1 196 | 197 | ## depth 2, level 1 198 | hash_d2 = fcm_crc32_mpeg2(ip_src_list[i][0]) 199 | register_l1_d2 = bfrt_info.table_get("SwitchIngress.fcmsketch.sketch_reg_l1_d2") 200 | resp_l1_d2 = register_l1_d2.entry_get(target, 201 | [register_l1_d2.make_key([gc.KeyTuple('$REGISTER_INDEX', hash_d2 % SKETCH_W1)])], 202 | {"from_hw": FROM_HW}) 203 | data_d2, _ = next(resp_l1_d2) 204 | data_d2_dict = data_d2.to_dict() 205 | val_d2 = data_d2_dict["SwitchIngress.fcmsketch.sketch_reg_l1_d2.f1"][0] 206 | # overflow to level 2? 207 | if (val_d2 is ADD_LEVEL1): 208 | register_l2_d2 = bfrt_info.table_get("SwitchIngress.fcmsketch.sketch_reg_l2_d2") 209 | resp_l2_d2 = register_l2_d2.entry_get(target, 210 | [register_l2_d2.make_key([gc.KeyTuple('$REGISTER_INDEX', hash_d2 % SKETCH_W2)])], 211 | {"from_hw": FROM_HW}) 212 | data_d2, _ = next(resp_l2_d2) 213 | data_d2_dict = data_d2.to_dict() 214 | val_d2 = data_d2_dict["SwitchIngress.fcmsketch.sketch_reg_l2_d2.f1"][0] + ADD_LEVEL1 - 1 215 | # overflow to level 3? 216 | if (val_d2 is ADD_LEVEL2): 217 | register_l3_d2 = bfrt_info.table_get("SwitchIngress.fcmsketch.sketch_reg_l3_d2") 218 | resp_l3_d2 = register_l3_d2.entry_get(target, 219 | [register_l3_d2.make_key([gc.KeyTuple('$REGISTER_INDEX', hash_d2 % SKETCH_W3)])], 220 | {"from_hw": FROM_HW}) 221 | data_d2, _ = next(resp_l3_d2) 222 | data_d2_dict = data_d2.to_dict() 223 | val_d2 = data_d2_dict["SwitchIngress.fcmsketch.sketch_reg_l3_d2.f1"][0] + ADD_LEVEL2 - 1 224 | 225 | if DEBUGGING: 226 | logger.info("[INFO-FCM] Flow %d - True : %d, Est of FCM : %d", i, ip_src_list[i][1], min(val_d1, val_d2)) 227 | 228 | final_query = min(val_d1, val_d2) 229 | ARE += abs(final_query - ip_src_list[i][1]) / float(ip_src_list[i][1]) 230 | AAE += abs(final_query - ip_src_list[i][1]) / 1.0 231 | logger.info(bcolors.OKBLUE + "[INFO-FCM] Flow Size - ARE = %2.8f" + bcolors.ENDC, (ARE / NUM_FLOWS)) 232 | logger.info(bcolors.OKBLUE + "[INFO-FCM] Flow Size - AAE = %2.8f" + bcolors.ENDC, (AAE / NUM_FLOWS)) 233 | 234 | # # Cardinality 235 | 236 | ## check the cardinality parameter 237 | register_occupied_num = bfrt_info.table_get("SwitchIngress.fcmsketch.reg_num_empty") 238 | resp_occupied_num = register_occupied_num.entry_get(target, 239 | [register_occupied_num.make_key([gc.KeyTuple('$REGISTER_INDEX', 0)])], 240 | {"from_hw": FROM_HW}) 241 | data, _ = next(resp_occupied_num) 242 | data_dict = data.to_dict() 243 | avg_occupied_leaf = math.floor(data_dict["SwitchIngress.fcmsketch.reg_num_empty.f1"][0] / 2.0) 244 | est_cardinality = match_table[int(avg_occupied_leaf)] 245 | Card_RE = abs(est_cardinality - NUM_FLOWS) / NUM_FLOWS 246 | logger.info(bcolors.OKBLUE + "[INFO-FCM] Cardinality RE = %0.9e (True : %d, Est of FCM : %d)" + bcolors.ENDC, Card_RE, NUM_FLOWS, est_cardinality) 247 | 248 | 249 | # You can also estimate flow size distribution and entropy by loading the sketch values on simulator. 250 | # You MUST CHANGE the hash functions as the simulator currently uses BOBHASH. 251 | # You can use the cpp codes "./calc_hash.cpp" to sync hash values. 252 | # Currently, our simulator supports only 3-level FCM (8,16,32-bit). 253 | # Mainly it is because of heuristic complexity truncation of EM algorithm. 254 | # In future, we will generalize the implementation. 255 | 256 | 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /fcm_p416/ptf/test_fcm.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcm-project/fcm_p4/338dffbaa551d21a456367d5b5007e62eb439986/fcm_p416/ptf/test_fcm.pyc --------------------------------------------------------------------------------