1 /*- 2 * Copyright (c) 2017-9 Netflix, Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 */ 26 #include <sys/cdefs.h> 27 #ifndef _KERNEL 28 #define _WANT_TCPCB 1 29 #endif 30 #include <sys/types.h> 31 #include <sys/queue.h> 32 #include <sys/socket.h> 33 #ifdef _KERNEL 34 #include <sys/mbuf.h> 35 #include <sys/sockopt.h> 36 #endif 37 #include <netinet/in.h> 38 #include <netinet/in_pcb.h> 39 #include <netinet/tcp.h> 40 #include <netinet/tcp_var.h> 41 #include <netinet/tcp_seq.h> 42 #ifndef _KERNEL 43 #include <stdio.h> 44 #include <unistd.h> 45 #include <string.h> 46 #include <strings.h> 47 #include <stdlib.h> 48 #include <limits.h> 49 #include <getopt.h> 50 #endif 51 #include "sack_filter.h" 52 53 /* 54 * Sack filter is used to filter out sacks 55 * that have already been processed. The idea 56 * is pretty simple really, consider two sacks 57 * 58 * SACK 1 59 * cum-ack A 60 * sack B - C 61 * SACK 2 62 * cum-ack A 63 * sack D - E 64 * sack B - C 65 * 66 * The previous sack information (B-C) is repeated 67 * in SACK 2. If the receiver gets SACK 1 and then 68 * SACK 2 then any work associated with B-C as already 69 * been completed. This only effects where we may have 70 * (as in bbr or rack) cases where we walk a linked list. 71 * 72 * Now the utility trys to keep everything in a single 73 * cache line. This means that its not perfect and 74 * it could be that so big of sack's come that a 75 * "remembered" processed sack falls off the list and 76 * so gets re-processed. Thats ok, it just means we 77 * did some extra work. We could of course take more 78 * cache line hits by expanding the size of this 79 * structure, but then that would cost more. 80 */ 81 82 #ifndef _KERNEL 83 int detailed_dump = 0; 84 uint64_t cnt_skipped_oldsack = 0; 85 uint64_t cnt_used_oldsack = 0; 86 int highest_used=0; 87 int over_written=0; 88 int empty_avail=0; 89 int no_collapse = 0; 90 FILE *out = NULL; 91 FILE *in = NULL; 92 #endif 93 94 #define sack_blk_used(sf, i) ((1 << i) & sf->sf_bits) 95 #define sack_blk_set(sf, i) ((1 << i) | sf->sf_bits) 96 #define sack_blk_clr(sf, i) (~(1 << i) & sf->sf_bits) 97 98 #ifndef _KERNEL 99 static 100 #endif 101 void 102 sack_filter_clear(struct sack_filter *sf, tcp_seq seq) 103 { 104 sf->sf_ack = seq; 105 sf->sf_bits = 0; 106 sf->sf_cur = 0; 107 sf->sf_used = 0; 108 } 109 /* 110 * Given a previous sack filter block, filter out 111 * any entries where the cum-ack moves over them 112 * fully or partially. 113 */ 114 static void 115 sack_filter_prune(struct sack_filter *sf, tcp_seq th_ack) 116 { 117 int32_t i; 118 /* start with the oldest */ 119 for (i = 0; i < SACK_FILTER_BLOCKS; i++) { 120 if (sack_blk_used(sf, i)) { 121 if (SEQ_GT(th_ack, sf->sf_blks[i].end)) { 122 /* This block is consumed */ 123 sf->sf_bits = sack_blk_clr(sf, i); 124 sf->sf_used--; 125 } else if (SEQ_GT(th_ack, sf->sf_blks[i].start)) { 126 /* Some of it is acked */ 127 sf->sf_blks[i].start = th_ack; 128 /* We could in theory break here, but 129 * there are some broken implementations 130 * that send multiple blocks. We want 131 * to catch them all with similar seq's. 132 */ 133 } 134 } 135 } 136 sf->sf_ack = th_ack; 137 } 138 139 /* 140 * Return true if you find that 141 * the sackblock b is on the score 142 * board. Update it along the way 143 * if part of it is on the board. 144 */ 145 static int32_t 146 is_sack_on_board(struct sack_filter *sf, struct sackblk *b) 147 { 148 int32_t i, cnt; 149 150 for (i = sf->sf_cur, cnt=0; cnt < SACK_FILTER_BLOCKS; cnt++) { 151 if (sack_blk_used(sf, i)) { 152 if (SEQ_LT(b->start, sf->sf_ack)) { 153 /* Behind cum-ack update */ 154 b->start = sf->sf_ack; 155 } 156 if (SEQ_LT(b->end, sf->sf_ack)) { 157 /* End back behind too */ 158 b->end = sf->sf_ack; 159 } 160 if (b->start == b->end) { 161 return(1); 162 } 163 /* Jonathans Rule 1 */ 164 if (SEQ_LEQ(sf->sf_blks[i].start, b->start) && 165 SEQ_GEQ(sf->sf_blks[i].end, b->end)) { 166 /** 167 * Our board has this entirely in 168 * whole or in part: 169 * 170 * board |-------------| 171 * sack |-------------| 172 * <or> 173 * board |-------------| 174 * sack |----| 175 * 176 */ 177 return(1); 178 } 179 /* Jonathans Rule 2 */ 180 if(SEQ_LT(sf->sf_blks[i].end, b->start)) { 181 /** 182 * Not near each other: 183 * 184 * board |---| 185 * sack |---| 186 */ 187 goto nxt_blk; 188 } 189 /* Jonathans Rule 3 */ 190 if (SEQ_GT(sf->sf_blks[i].start, b->end)) { 191 /** 192 * Not near each other: 193 * 194 * board |---| 195 * sack |---| 196 */ 197 goto nxt_blk; 198 } 199 if (SEQ_LEQ(sf->sf_blks[i].start, b->start)) { 200 /** 201 * The board block partial meets: 202 * 203 * board |--------| 204 * sack |----------| 205 * <or> 206 * board |--------| 207 * sack |--------------| 208 * 209 * up with this one (we have part of it). 210 * 1) Update the board block to the new end 211 * and 212 * 2) Update the start of this block to my end. 213 */ 214 b->start = sf->sf_blks[i].end; 215 sf->sf_blks[i].end = b->end; 216 goto nxt_blk; 217 } 218 if (SEQ_GEQ(sf->sf_blks[i].end, b->end)) { 219 /** 220 * The board block partial meets: 221 * 222 * board |--------| 223 * sack |----------| 224 * <or> 225 * board |----| 226 * sack |----------| 227 * 1) Update the board block to the new start 228 * and 229 * 2) Update the start of this block to my end. 230 */ 231 b->end = sf->sf_blks[i].start; 232 sf->sf_blks[i].start = b->start; 233 goto nxt_blk; 234 } 235 } 236 nxt_blk: 237 i++; 238 i %= SACK_FILTER_BLOCKS; 239 } 240 /* Did we totally consume it in pieces? */ 241 if (b->start != b->end) 242 return(0); 243 else 244 return(1); 245 } 246 247 static int32_t 248 sack_filter_old(struct sack_filter *sf, struct sackblk *in, int numblks) 249 { 250 int32_t num, i; 251 struct sackblk blkboard[TCP_MAX_SACK]; 252 /* 253 * An old sack has arrived. It may contain data 254 * we do not have. We might not have it since 255 * we could have had a lost ack <or> we might have the 256 * entire thing on our current board. We want to prune 257 * off anything we have. With this function though we 258 * won't add to the board. 259 */ 260 for( i = 0, num = 0; i<numblks; i++ ) { 261 if (is_sack_on_board(sf, &in[i])) { 262 #ifndef _KERNEL 263 cnt_skipped_oldsack++; 264 #endif 265 continue; 266 } 267 /* Did not find it (or found only 268 * a piece of it). Copy it to 269 * our outgoing board. 270 */ 271 memcpy(&blkboard[num], &in[i], sizeof(struct sackblk)); 272 #ifndef _KERNEL 273 cnt_used_oldsack++; 274 #endif 275 num++; 276 } 277 if (num) { 278 memcpy(in, blkboard, (num * sizeof(struct sackblk))); 279 } 280 return (num); 281 } 282 283 /* 284 * Given idx its used but there is space available 285 * move the entry to the next free slot 286 */ 287 static void 288 sack_move_to_empty(struct sack_filter *sf, uint32_t idx) 289 { 290 int32_t i, cnt; 291 292 i = (idx + 1) % SACK_FILTER_BLOCKS; 293 for (cnt=0; cnt <(SACK_FILTER_BLOCKS-1); cnt++) { 294 if (sack_blk_used(sf, i) == 0) { 295 memcpy(&sf->sf_blks[i], &sf->sf_blks[idx], sizeof(struct sackblk)); 296 sf->sf_bits = sack_blk_clr(sf, idx); 297 sf->sf_bits = sack_blk_set(sf, i); 298 return; 299 } 300 i++; 301 i %= SACK_FILTER_BLOCKS; 302 } 303 } 304 305 static int32_t 306 sack_filter_new(struct sack_filter *sf, struct sackblk *in, int numblks, tcp_seq th_ack) 307 { 308 struct sackblk blkboard[TCP_MAX_SACK]; 309 int32_t num, i; 310 /* 311 * First lets trim the old and possibly 312 * throw any away we have. 313 */ 314 for(i=0, num=0; i<numblks; i++) { 315 if (is_sack_on_board(sf, &in[i])) 316 continue; 317 memcpy(&blkboard[num], &in[i], sizeof(struct sackblk)); 318 num++; 319 } 320 if (num == 0) 321 return(num); 322 323 /* Now what we are left with is either 324 * completely merged on to the board 325 * from the above steps, or is new 326 * and need to be added to the board 327 * with the last one updated to current. 328 * 329 * First copy it out, we want to return that 330 * to our caller for processing. 331 */ 332 memcpy(in, blkboard, (num * sizeof(struct sackblk))); 333 numblks = num; 334 /* Now go through and add to our board as needed */ 335 for(i=(num-1); i>=0; i--) { 336 if (is_sack_on_board(sf, &blkboard[i])) { 337 continue; 338 } 339 /* Add this guy its not listed */ 340 sf->sf_cur++; 341 sf->sf_cur %= SACK_FILTER_BLOCKS; 342 if ((sack_blk_used(sf, sf->sf_cur)) && 343 (sf->sf_used < SACK_FILTER_BLOCKS)) { 344 sack_move_to_empty(sf, sf->sf_cur); 345 } 346 #ifndef _KERNEL 347 if (sack_blk_used(sf, sf->sf_cur)) { 348 over_written++; 349 if (sf->sf_used < SACK_FILTER_BLOCKS) 350 empty_avail++; 351 } 352 #endif 353 memcpy(&sf->sf_blks[sf->sf_cur], &in[i], sizeof(struct sackblk)); 354 if (sack_blk_used(sf, sf->sf_cur) == 0) { 355 sf->sf_used++; 356 #ifndef _KERNEL 357 if (sf->sf_used > highest_used) 358 highest_used = sf->sf_used; 359 #endif 360 sf->sf_bits = sack_blk_set(sf, sf->sf_cur); 361 } 362 } 363 return(numblks); 364 } 365 366 /* 367 * Given a sack block on the board (the skip index) see if 368 * any other used entries overlap or meet, if so return the index. 369 */ 370 static int32_t 371 sack_blocks_overlap_or_meet(struct sack_filter *sf, struct sackblk *sb, uint32_t skip) 372 { 373 int32_t i; 374 375 for(i=0; i<SACK_FILTER_BLOCKS; i++) { 376 if (sack_blk_used(sf, i) == 0) 377 continue; 378 if (i == skip) 379 continue; 380 if (SEQ_GEQ(sf->sf_blks[i].end, sb->start) && 381 SEQ_LEQ(sf->sf_blks[i].end, sb->end) && 382 SEQ_LEQ(sf->sf_blks[i].start, sb->start)) { 383 /** 384 * The two board blocks meet: 385 * 386 * board1 |--------| 387 * board2 |----------| 388 * <or> 389 * board1 |--------| 390 * board2 |--------------| 391 * <or> 392 * board1 |--------| 393 * board2 |--------| 394 */ 395 return(i); 396 } 397 if (SEQ_LEQ(sf->sf_blks[i].start, sb->end) && 398 SEQ_GEQ(sf->sf_blks[i].start, sb->start) && 399 SEQ_GEQ(sf->sf_blks[i].end, sb->end)) { 400 /** 401 * The board block partial meets: 402 * 403 * board |--------| 404 * sack |----------| 405 * <or> 406 * board |----| 407 * sack |----------| 408 * 1) Update the board block to the new start 409 * and 410 * 2) Update the start of this block to my end. 411 */ 412 return(i); 413 } 414 } 415 return (-1); 416 } 417 418 /* 419 * Collapse entry src into entry into 420 * and free up the src entry afterwards. 421 */ 422 static void 423 sack_collapse(struct sack_filter *sf, int32_t src, int32_t into) 424 { 425 if (SEQ_LT(sf->sf_blks[src].start, sf->sf_blks[into].start)) { 426 /* src has a lower starting point */ 427 sf->sf_blks[into].start = sf->sf_blks[src].start; 428 } 429 if (SEQ_GT(sf->sf_blks[src].end, sf->sf_blks[into].end)) { 430 /* src has a higher ending point */ 431 sf->sf_blks[into].end = sf->sf_blks[src].end; 432 } 433 sf->sf_bits = sack_blk_clr(sf, src); 434 sf->sf_used--; 435 } 436 437 static void 438 sack_board_collapse(struct sack_filter *sf) 439 { 440 int32_t i, j, i_d, j_d; 441 442 for(i=0; i<SACK_FILTER_BLOCKS; i++) { 443 if (sack_blk_used(sf, i) == 0) 444 continue; 445 /* 446 * Look at all other blocks but this guy 447 * to see if they overlap. If so we collapse 448 * the two blocks together. 449 */ 450 j = sack_blocks_overlap_or_meet(sf, &sf->sf_blks[i], i); 451 if (j == -1) { 452 /* No overlap */ 453 continue; 454 } 455 /* 456 * Ok j and i overlap with each other, collapse the 457 * one out furthest away from the current position. 458 */ 459 if (sf->sf_cur > i) 460 i_d = sf->sf_cur - i; 461 else 462 i_d = i - sf->sf_cur; 463 if (sf->sf_cur > j) 464 j_d = sf->sf_cur - j; 465 else 466 j_d = j - sf->sf_cur; 467 if (j_d > i_d) { 468 sack_collapse(sf, j, i); 469 } else 470 sack_collapse(sf, i, j); 471 } 472 } 473 474 #ifndef _KERNEL 475 uint64_t saved=0; 476 uint64_t tot_sack_blks=0; 477 478 static void 479 sack_filter_dump(FILE *out, struct sack_filter *sf) 480 { 481 int i; 482 fprintf(out, " sf_ack:%u sf_bits:0x%x c:%d used:%d\n", 483 sf->sf_ack, sf->sf_bits, 484 sf->sf_cur, sf->sf_used); 485 486 for(i=0; i<SACK_FILTER_BLOCKS; i++) { 487 if (sack_blk_used(sf, i)) { 488 fprintf(out, "Entry:%d start:%u end:%u\n", i, 489 sf->sf_blks[i].start, 490 sf->sf_blks[i].end); 491 } 492 } 493 } 494 #endif 495 496 #ifndef _KERNEL 497 static 498 #endif 499 int 500 sack_filter_blks(struct sack_filter *sf, struct sackblk *in, int numblks, 501 tcp_seq th_ack) 502 { 503 int32_t i, ret; 504 505 if (numblks > TCP_MAX_SACK) { 506 #ifdef _KERNEL 507 panic("sf:%p sb:%p Impossible number of sack blocks %d > 4\n", 508 sf, in, 509 numblks); 510 #endif 511 return(numblks); 512 } 513 #ifndef _KERNEL 514 if ((sf->sf_used > 1) && (no_collapse == 0)) 515 sack_board_collapse(sf); 516 517 #else 518 if (sf->sf_used > 1) 519 sack_board_collapse(sf); 520 #endif 521 if ((sf->sf_used == 0) && numblks) { 522 /* 523 * We are brand new add the blocks in 524 * reverse order. Note we can see more 525 * than one in new, since ack's could be lost. 526 */ 527 int cnt_added = 0; 528 529 sf->sf_ack = th_ack; 530 for(i=(numblks-1), sf->sf_cur=0; i >= 0; i--) { 531 memcpy(&sf->sf_blks[sf->sf_cur], &in[i], sizeof(struct sackblk)); 532 sf->sf_bits = sack_blk_set(sf, sf->sf_cur); 533 sf->sf_cur++; 534 sf->sf_cur %= SACK_FILTER_BLOCKS; 535 sf->sf_used++; 536 cnt_added++; 537 #ifndef _KERNEL 538 if (sf->sf_used > highest_used) 539 highest_used = sf->sf_used; 540 #endif 541 } 542 if (sf->sf_cur) 543 sf->sf_cur--; 544 545 return (cnt_added); 546 } 547 if (SEQ_GT(th_ack, sf->sf_ack)) { 548 sack_filter_prune(sf, th_ack); 549 } 550 if (numblks) { 551 if (SEQ_GEQ(th_ack, sf->sf_ack)) { 552 ret = sack_filter_new(sf, in, numblks, th_ack); 553 } else { 554 ret = sack_filter_old(sf, in, numblks); 555 } 556 } else 557 ret = 0; 558 return (ret); 559 } 560 561 void 562 sack_filter_reject(struct sack_filter *sf, struct sackblk *in) 563 { 564 /* 565 * Given a specified block (that had made 566 * it past the sack filter). Reject that 567 * block triming it off any sack-filter block 568 * that has it. Usually because the block was 569 * too small and did not cover a whole send. 570 * 571 * This function will only "undo" sack-blocks 572 * that are fresh and touch the edges of 573 * blocks in our filter. 574 */ 575 int i; 576 577 for(i=0; i<SACK_FILTER_BLOCKS; i++) { 578 if (sack_blk_used(sf, i) == 0) 579 continue; 580 /* 581 * Now given the sack-filter block does it touch 582 * with one of the ends 583 */ 584 if (sf->sf_blks[i].end == in->end) { 585 /* The end moves back to start */ 586 if (SEQ_GT(in->start, sf->sf_blks[i].start)) 587 /* in-blk |----| */ 588 /* sf-blk |---------| */ 589 sf->sf_blks[i].end = in->start; 590 else { 591 /* It consumes this block */ 592 /* in-blk |---------| */ 593 /* sf-blk |------| */ 594 /* <or> */ 595 /* sf-blk |---------| */ 596 sf->sf_bits = sack_blk_clr(sf, i); 597 sf->sf_used--; 598 } 599 continue; 600 } 601 if (sf->sf_blks[i].start == in->start) { 602 if (SEQ_LT(in->end, sf->sf_blks[i].end)) { 603 /* in-blk |----| */ 604 /* sf-blk |---------| */ 605 sf->sf_blks[i].start = in->end; 606 } else { 607 /* It consumes this block */ 608 /* in-blk |----------| */ 609 /* sf-blk |-------| */ 610 /* <or> */ 611 /* sf-blk |----------| */ 612 sf->sf_bits = sack_blk_clr(sf, i); 613 sf->sf_used--; 614 } 615 continue; 616 } 617 } 618 } 619 620 #ifndef _KERNEL 621 622 int 623 main(int argc, char **argv) 624 { 625 char buffer[512]; 626 struct sackblk blks[TCP_MAX_SACK]; 627 FILE *err; 628 tcp_seq th_ack, snd_una, snd_max = 0; 629 struct sack_filter sf; 630 int32_t numblks,i; 631 int snd_una_set=0; 632 double a, b, c; 633 int invalid_sack_print = 0; 634 uint32_t chg_remembered=0; 635 uint32_t sack_chg=0; 636 char line_buf[10][256]; 637 int line_buf_at=0; 638 639 in = stdin; 640 out = stdout; 641 while ((i = getopt(argc, argv, "ndIi:o:?h")) != -1) { 642 switch (i) { 643 case 'n': 644 no_collapse = 1; 645 break; 646 case 'd': 647 detailed_dump = 1; 648 break; 649 case'I': 650 invalid_sack_print = 1; 651 break; 652 case 'i': 653 in = fopen(optarg, "r"); 654 if (in == NULL) { 655 fprintf(stderr, "Fatal error can't open %s for input\n", optarg); 656 exit(-1); 657 } 658 break; 659 case 'o': 660 out = fopen(optarg, "w"); 661 if (out == NULL) { 662 fprintf(stderr, "Fatal error can't open %s for output\n", optarg); 663 exit(-1); 664 } 665 break; 666 default: 667 case '?': 668 case 'h': 669 fprintf(stderr, "Use %s [ -i infile -o outfile -I]\n", argv[0]); 670 return(0); 671 break; 672 }; 673 } 674 sack_filter_clear(&sf, 0); 675 memset(buffer, 0, sizeof(buffer)); 676 memset(blks, 0, sizeof(blks)); 677 numblks = 0; 678 fprintf(out, "************************************\n"); 679 while (fgets(buffer, sizeof(buffer), in) != NULL) { 680 sprintf(line_buf[line_buf_at], "%s", buffer); 681 line_buf_at++; 682 if (strncmp(buffer, "QUIT", 4) == 0) { 683 break; 684 } else if (strncmp(buffer, "DUMP", 4) == 0) { 685 sack_filter_dump(out, &sf); 686 } else if (strncmp(buffer, "MAX:", 4) == 0) { 687 snd_max = strtoul(&buffer[4], NULL, 0); 688 } else if (strncmp(buffer, "COMMIT", 6) == 0) { 689 int nn, ii; 690 if (numblks) { 691 uint32_t szof, tot_chg; 692 for(ii=0; ii<line_buf_at; ii++) { 693 fprintf(out, "%s", line_buf[ii]); 694 } 695 fprintf(out, "------------------------------------\n"); 696 nn = sack_filter_blks(&sf, blks, numblks, th_ack); 697 saved += numblks - nn; 698 tot_sack_blks += numblks; 699 fprintf(out, "ACK:%u\n", sf.sf_ack); 700 for(ii=0, tot_chg=0; ii<nn; ii++) { 701 szof = blks[ii].end - blks[ii].start; 702 tot_chg += szof; 703 fprintf(out, "SACK:%u:%u [%u]\n", 704 blks[ii].start, 705 blks[ii].end, szof); 706 } 707 fprintf(out,"************************************\n"); 708 chg_remembered = tot_chg; 709 if (detailed_dump) { 710 sack_filter_dump(out, &sf); 711 fprintf(out,"************************************\n"); 712 } 713 } 714 memset(blks, 0, sizeof(blks)); 715 memset(line_buf, 0, sizeof(line_buf)); 716 line_buf_at=0; 717 numblks = 0; 718 } else if (strncmp(buffer, "CHG:", 4) == 0) { 719 sack_chg = strtoul(&buffer[4], NULL, 0); 720 if ((sack_chg != chg_remembered) && 721 (sack_chg > chg_remembered)){ 722 fprintf(out,"***WARNING WILL RODGERS DANGER!! sack_chg:%u last:%u\n", 723 sack_chg, chg_remembered 724 ); 725 } 726 sack_chg = chg_remembered = 0; 727 } else if (strncmp(buffer, "RXT", 3) == 0) { 728 sack_filter_clear(&sf, snd_una); 729 } else if (strncmp(buffer, "ACK:", 4) == 0) { 730 th_ack = strtoul(&buffer[4], NULL, 0); 731 if (snd_una_set == 0) { 732 snd_una = th_ack; 733 snd_una_set = 1; 734 } else if (SEQ_GT(th_ack, snd_una)) { 735 snd_una = th_ack; 736 } 737 } else if (strncmp(buffer, "EXIT", 4) == 0) { 738 sack_filter_clear(&sf, snd_una); 739 sack_chg = chg_remembered = 0; 740 } else if (strncmp(buffer, "SACK:", 5) == 0) { 741 char *end=NULL; 742 uint32_t start; 743 uint32_t endv; 744 745 start = strtoul(&buffer[5], &end, 0); 746 if (end) { 747 endv = strtoul(&end[1], NULL, 0); 748 } else { 749 fprintf(out, "--Sack invalid skip 0 start:%u : ??\n", start); 750 continue; 751 } 752 if (SEQ_GT(endv, snd_max)) 753 snd_max = endv; 754 if (SEQ_LT(endv, start)) { 755 fprintf(out, "--Sack invalid skip 1 endv:%u < start:%u\n", endv, start); 756 continue; 757 } 758 if (numblks == TCP_MAX_SACK) { 759 fprintf(out, "--Exceeded max %d\n", numblks); 760 exit(0); 761 } 762 blks[numblks].start = start; 763 blks[numblks].end = endv; 764 numblks++; 765 } else if (strncmp(buffer, "REJ:n:n", 4) == 0) { 766 struct sackblk in; 767 char *end=NULL; 768 769 in.start = strtoul(&buffer[4], &end, 0); 770 if (end) { 771 in.end = strtoul(&end[1], NULL, 0); 772 sack_filter_reject(&sf, &in); 773 } else 774 fprintf(out, "Invalid input END:A:B\n"); 775 } else if (strncmp(buffer, "HELP", 4) == 0) { 776 fprintf(out, "You can input:\n"); 777 fprintf(out, "SACK:S:E -- to define a sack block\n"); 778 fprintf(out, "RXT -- to clear the filter without changing the remembered\n"); 779 fprintf(out, "EXIT -- To clear the sack filter and start all fresh\n"); 780 fprintf(out, "ACK:N -- To advance the cum-ack to N\n"); 781 fprintf(out, "MAX:N -- To set send-max to N\n"); 782 fprintf(out, "COMMIT -- To apply the sack you built to the filter and dump the filter\n"); 783 fprintf(out, "DUMP -- To display the current contents of the sack filter\n"); 784 fprintf(out, "QUIT -- To exit this program\n"); 785 } else { 786 fprintf(out, "Command %s unknown\n", buffer); 787 } 788 memset(buffer, 0, sizeof(buffer)); 789 } 790 if (in != stdin) { 791 fclose(in); 792 } 793 if (out != stdout) { 794 fclose(out); 795 } 796 a = saved * 100.0; 797 b = tot_sack_blks * 1.0; 798 if (b > 0.0) 799 c = a/b; 800 else 801 c = 0.0; 802 if (out != stdout) 803 err = stdout; 804 else 805 err = stderr; 806 fprintf(err, "Saved %lu sack blocks out of %lu (%2.3f%%) old_skip:%lu old_usd:%lu high_cnt:%d ow:%d ea:%d\n", 807 saved, tot_sack_blks, c, cnt_skipped_oldsack, cnt_used_oldsack, highest_used, over_written, empty_avail); 808 return(0); 809 } 810 #endif 811