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 #ifdef _KERNEL 39 #include <netinet/in_pcb.h> 40 #else 41 struct inpcb { 42 uint32_t stuff; 43 }; 44 #endif 45 #include <netinet/tcp.h> 46 #include <netinet/tcp_var.h> 47 #include <netinet/tcp_seq.h> 48 #ifndef _KERNEL 49 #include <stdio.h> 50 #include <unistd.h> 51 #include <string.h> 52 #include <strings.h> 53 #include <stdlib.h> 54 #include <limits.h> 55 #include <getopt.h> 56 #endif 57 #include "sack_filter.h" 58 59 /* 60 * Sack filter is used to filter out sacks 61 * that have already been processed. The idea 62 * is pretty simple really, consider two sacks 63 * 64 * SACK 1 65 * cum-ack A 66 * sack B - C 67 * SACK 2 68 * cum-ack A 69 * sack D - E 70 * sack B - C 71 * 72 * The previous sack information (B-C) is repeated 73 * in SACK 2. If the receiver gets SACK 1 and then 74 * SACK 2 then any work associated with B-C as already 75 * been completed. This only effects where we may have 76 * (as in bbr or rack) cases where we walk a linked list. 77 * 78 * Now the utility trys to keep everything in a single 79 * cache line. This means that its not perfect and 80 * it could be that so big of sack's come that a 81 * "remembered" processed sack falls off the list and 82 * so gets re-processed. Thats ok, it just means we 83 * did some extra work. We could of course take more 84 * cache line hits by expanding the size of this 85 * structure, but then that would cost more. 86 */ 87 88 #ifndef _KERNEL 89 int detailed_dump = 0; 90 uint64_t cnt_skipped_oldsack = 0; 91 uint64_t cnt_used_oldsack = 0; 92 int highest_used=0; 93 int over_written=0; 94 int empty_avail=0; 95 FILE *out = NULL; 96 FILE *in = NULL; 97 98 #endif 99 100 #define sack_blk_used(sf, i) ((1 << i) & sf->sf_bits) 101 #define sack_blk_set(sf, i) ((1 << i) | sf->sf_bits) 102 #define sack_blk_clr(sf, i) (~(1 << i) & sf->sf_bits) 103 104 #ifndef _KERNEL 105 106 static u_int tcp_fixed_maxseg(const struct tcpcb *tp) 107 { 108 /* Lets pretend their are timestamps on for user space */ 109 return (tp->t_maxseg - 12); 110 } 111 112 static 113 #endif 114 void 115 sack_filter_clear(struct sack_filter *sf, tcp_seq seq) 116 { 117 sf->sf_ack = seq; 118 sf->sf_bits = 0; 119 sf->sf_cur = 0; 120 sf->sf_used = 0; 121 } 122 /* 123 * Given a previous sack filter block, filter out 124 * any entries where the cum-ack moves over them 125 * fully or partially. 126 */ 127 static void 128 sack_filter_prune(struct sack_filter *sf, tcp_seq th_ack) 129 { 130 int32_t i; 131 /* start with the oldest */ 132 for (i = 0; i < SACK_FILTER_BLOCKS; i++) { 133 if (sack_blk_used(sf, i)) { 134 if (SEQ_GEQ(th_ack, sf->sf_blks[i].end)) { 135 /* This block is consumed */ 136 sf->sf_bits = sack_blk_clr(sf, i); 137 sf->sf_used--; 138 } else if (SEQ_GT(th_ack, sf->sf_blks[i].start)) { 139 /* Some of it is acked */ 140 sf->sf_blks[i].start = th_ack; 141 /* We could in theory break here, but 142 * there are some broken implementations 143 * that send multiple blocks. We want 144 * to catch them all with similar seq's. 145 */ 146 } 147 } 148 } 149 sf->sf_ack = th_ack; 150 } 151 152 /* 153 * Return true if you find that 154 * the sackblock b is on the score 155 * board. Update it along the way 156 * if part of it is on the board. 157 */ 158 static int32_t 159 is_sack_on_board(struct sack_filter *sf, struct sackblk *b, int32_t segmax, uint32_t snd_max) 160 { 161 int32_t i, cnt; 162 int span_cnt = 0; 163 uint32_t span_start, span_end; 164 165 if (SEQ_LT(b->start, sf->sf_ack)) { 166 /* Behind cum-ack update */ 167 b->start = sf->sf_ack; 168 } 169 if (SEQ_LT(b->end, sf->sf_ack)) { 170 /* End back behind too */ 171 b->end = sf->sf_ack; 172 } 173 if (b->start == b->end) { 174 return(1); 175 } 176 span_start = b->start; 177 span_end = b->end; 178 for (i = sf->sf_cur, cnt=0; cnt < SACK_FILTER_BLOCKS; cnt++) { 179 if (sack_blk_used(sf, i)) { 180 /* Jonathans Rule 1 */ 181 if (SEQ_LEQ(sf->sf_blks[i].start, b->start) && 182 SEQ_GEQ(sf->sf_blks[i].end, b->end)) { 183 /** 184 * Our board has this entirely in 185 * whole or in part: 186 * 187 * board |-------------| 188 * sack |-------------| 189 * <or> 190 * board |-------------| 191 * sack |----| 192 * 193 */ 194 return(1); 195 } 196 /* Jonathans Rule 2 */ 197 if(SEQ_LT(sf->sf_blks[i].end, b->start)) { 198 /** 199 * Not near each other: 200 * 201 * board |---| 202 * sack |---| 203 */ 204 if ((b->end != snd_max) && 205 (span_cnt < 2) && 206 ((b->end - b->start) < segmax)) { 207 /* 208 * Too small for us to mess with so we 209 * pretend its on the board. 210 */ 211 return (1); 212 } 213 goto nxt_blk; 214 } 215 /* Jonathans Rule 3 */ 216 if (SEQ_GT(sf->sf_blks[i].start, b->end)) { 217 /** 218 * Not near each other: 219 * 220 * board |---| 221 * sack |---| 222 */ 223 if ((b->end != snd_max) && 224 (sf->sf_blks[i].end != snd_max) && 225 (span_cnt < 2) && 226 ((b->end - b->start) < segmax)) { 227 /* 228 * Too small for us to mess with so we 229 * pretend its on the board. 230 */ 231 return (1); 232 } 233 goto nxt_blk; 234 } 235 if (SEQ_LEQ(sf->sf_blks[i].start, b->start)) { 236 /** 237 * The board block partial meets: 238 * 239 * board |--------| 240 * sack |----------| 241 * <or> 242 * board |--------| 243 * sack |--------------| 244 * 245 * up with this one (we have part of it). 246 * 247 * 1) Update the board block to the new end 248 * and 249 * 2) Update the start of this block to my end. 250 * 251 * We only do this if the new piece is large enough. 252 */ 253 if (((b->end != snd_max) || (sf->sf_blks[i].end == snd_max)) && 254 (span_cnt == 0) && 255 ((b->end - sf->sf_blks[i].end) < segmax)) { 256 /* 257 * Too small for us to mess with so we 258 * pretend its on the board. 259 */ 260 return (1); 261 } 262 b->start = sf->sf_blks[i].end; 263 sf->sf_blks[i].end = b->end; 264 if (span_cnt == 0) { 265 span_start = sf->sf_blks[i].start; 266 span_end = sf->sf_blks[i].end; 267 } else { 268 if (SEQ_LT(span_start, sf->sf_blks[i].start)) { 269 span_start = sf->sf_blks[i].start; 270 } 271 if (SEQ_GT(span_end, sf->sf_blks[i].end)) { 272 span_end = sf->sf_blks[i].end; 273 } 274 } 275 span_cnt++; 276 goto nxt_blk; 277 } 278 if (SEQ_GEQ(sf->sf_blks[i].end, b->end)) { 279 /** 280 * The board block partial meets: 281 * 282 * board |--------| 283 * sack |----------| 284 * <or> 285 * board |----| 286 * sack |----------| 287 * 288 * 1) Update the board block to the new start 289 * and 290 * 2) Update the start of this block to my end. 291 * 292 * We only do this if the new piece is large enough. 293 */ 294 if (((b->end != snd_max) || (sf->sf_blks[i].end == snd_max)) && 295 (span_cnt == 0) && 296 ((sf->sf_blks[i].start - b->start) < segmax)) { 297 /* 298 * Too small for us to mess with so we 299 * pretend its on the board. 300 */ 301 return (1); 302 } 303 b->end = sf->sf_blks[i].start; 304 sf->sf_blks[i].start = b->start; 305 if (span_cnt == 0) { 306 span_start = sf->sf_blks[i].start; 307 span_end = sf->sf_blks[i].end; 308 } else { 309 if (SEQ_LT(span_start, sf->sf_blks[i].start)) { 310 span_start = sf->sf_blks[i].start; 311 } 312 if (SEQ_GT(span_end, sf->sf_blks[i].end)) { 313 span_end = sf->sf_blks[i].end; 314 } 315 } 316 span_cnt++; 317 goto nxt_blk; 318 } 319 } 320 nxt_blk: 321 i++; 322 i %= SACK_FILTER_BLOCKS; 323 } 324 /* Did we totally consume it in pieces? */ 325 if (b->start != b->end) { 326 if ((b->end != snd_max) && 327 ((b->end - b->start) < segmax) && 328 ((span_end - span_start) < segmax)) { 329 /* 330 * Too small for us to mess with so we 331 * pretend its on the board. 332 */ 333 return (1); 334 } 335 return(0); 336 } else { 337 /* 338 * It was all consumed by the board. 339 */ 340 return(1); 341 } 342 } 343 344 /* 345 * Given idx its used but there is space available 346 * move the entry to the next free slot 347 */ 348 static void 349 sack_move_to_empty(struct sack_filter *sf, uint32_t idx) 350 { 351 int32_t i, cnt; 352 353 i = (idx + 1) % SACK_FILTER_BLOCKS; 354 for (cnt=0; cnt <(SACK_FILTER_BLOCKS-1); cnt++) { 355 if (sack_blk_used(sf, i) == 0) { 356 memcpy(&sf->sf_blks[i], &sf->sf_blks[idx], sizeof(struct sackblk)); 357 sf->sf_bits = sack_blk_clr(sf, idx); 358 sf->sf_bits = sack_blk_set(sf, i); 359 return; 360 } 361 i++; 362 i %= SACK_FILTER_BLOCKS; 363 } 364 } 365 366 static int32_t 367 sack_filter_run(struct sack_filter *sf, struct sackblk *in, int numblks, tcp_seq th_ack, int32_t segmax, uint32_t snd_max) 368 { 369 struct sackblk blkboard[TCP_MAX_SACK]; 370 int32_t num, i, room, at; 371 /* 372 * First lets trim the old and possibly 373 * throw any away we have. 374 */ 375 for(i=0, num=0; i<numblks; i++) { 376 if (is_sack_on_board(sf, &in[i], segmax, snd_max)) 377 continue; 378 memcpy(&blkboard[num], &in[i], sizeof(struct sackblk)); 379 num++; 380 } 381 if (num == 0) { 382 return(num); 383 } 384 385 /* 386 * Calculate the space we have in the filter table. 387 */ 388 room = SACK_FILTER_BLOCKS - sf->sf_used; 389 if (room < 1) 390 return (0); 391 /* 392 * Now lets walk through our filtered blkboard (the previous loop 393 * trimmed off anything on the board we already have so anything 394 * in blkboard is unique and not seen before) if there is room we copy 395 * it back out and place a new entry on our board. 396 */ 397 for(i=0, at=0; i<num; i++) { 398 if (room == 0) { 399 /* Can't copy out any more, no more room */ 400 break; 401 } 402 /* Copy it out to the outbound */ 403 memcpy(&in[at], &blkboard[i], sizeof(struct sackblk)); 404 at++; 405 room--; 406 /* now lets add it to our sack-board */ 407 sf->sf_cur++; 408 sf->sf_cur %= SACK_FILTER_BLOCKS; 409 if ((sack_blk_used(sf, sf->sf_cur)) && 410 (sf->sf_used < SACK_FILTER_BLOCKS)) { 411 sack_move_to_empty(sf, sf->sf_cur); 412 } 413 memcpy(&sf->sf_blks[sf->sf_cur], &blkboard[i], sizeof(struct sackblk)); 414 if (sack_blk_used(sf, sf->sf_cur) == 0) { 415 sf->sf_used++; 416 #ifndef _KERNEL 417 if (sf->sf_used > highest_used) 418 highest_used = sf->sf_used; 419 #endif 420 sf->sf_bits = sack_blk_set(sf, sf->sf_cur); 421 } 422 } 423 return(at); 424 } 425 426 /* 427 * Collapse entry src into entry into 428 * and free up the src entry afterwards. 429 */ 430 static void 431 sack_collapse(struct sack_filter *sf, int32_t src, int32_t into) 432 { 433 if (SEQ_LT(sf->sf_blks[src].start, sf->sf_blks[into].start)) { 434 /* src has a lower starting point */ 435 sf->sf_blks[into].start = sf->sf_blks[src].start; 436 } 437 if (SEQ_GT(sf->sf_blks[src].end, sf->sf_blks[into].end)) { 438 /* src has a higher ending point */ 439 sf->sf_blks[into].end = sf->sf_blks[src].end; 440 } 441 sf->sf_bits = sack_blk_clr(sf, src); 442 sf->sf_used--; 443 } 444 445 /* 446 * Given a sack block on the board (the skip index) see if 447 * any other used entries overlap or meet, if so return the index. 448 */ 449 static int32_t 450 sack_blocks_overlap_or_meet(struct sack_filter *sf, struct sackblk *sb, uint32_t skip) 451 { 452 int32_t i; 453 454 for(i=0; i<SACK_FILTER_BLOCKS; i++) { 455 if (sack_blk_used(sf, i) == 0) 456 continue; 457 if (i == skip) 458 continue; 459 if (SEQ_GEQ(sf->sf_blks[i].end, sb->start) && 460 SEQ_LEQ(sf->sf_blks[i].end, sb->end) && 461 SEQ_LEQ(sf->sf_blks[i].start, sb->start)) { 462 /** 463 * The two board blocks meet: 464 * 465 * board1 |--------| 466 * board2 |----------| 467 * <or> 468 * board1 |--------| 469 * board2 |--------------| 470 * <or> 471 * board1 |--------| 472 * board2 |--------| 473 */ 474 return(i); 475 } 476 if (SEQ_LEQ(sf->sf_blks[i].start, sb->end) && 477 SEQ_GEQ(sf->sf_blks[i].start, sb->start) && 478 SEQ_GEQ(sf->sf_blks[i].end, sb->end)) { 479 /** 480 * The board block partial meets: 481 * 482 * board |--------| 483 * sack |----------| 484 * <or> 485 * board |----| 486 * sack |----------| 487 * 1) Update the board block to the new start 488 * and 489 * 2) Update the start of this block to my end. 490 */ 491 return(i); 492 } 493 } 494 return (-1); 495 } 496 497 static void 498 sack_board_collapse(struct sack_filter *sf) 499 { 500 int32_t i, j, i_d, j_d; 501 502 for(i=0; i<SACK_FILTER_BLOCKS; i++) { 503 if (sack_blk_used(sf, i) == 0) 504 continue; 505 /* 506 * Look at all other blocks but this guy 507 * to see if they overlap. If so we collapse 508 * the two blocks together. 509 */ 510 j = sack_blocks_overlap_or_meet(sf, &sf->sf_blks[i], i); 511 if (j == -1) { 512 /* No overlap */ 513 continue; 514 } 515 /* 516 * Ok j and i overlap with each other, collapse the 517 * one out furthest away from the current position. 518 */ 519 if (sf->sf_cur > i) 520 i_d = sf->sf_cur - i; 521 else 522 i_d = i - sf->sf_cur; 523 if (sf->sf_cur > j) 524 j_d = sf->sf_cur - j; 525 else 526 j_d = j - sf->sf_cur; 527 if (j_d > i_d) { 528 sack_collapse(sf, j, i); 529 } else 530 sack_collapse(sf, i, j); 531 } 532 } 533 534 #ifndef _KERNEL 535 uint64_t saved=0; 536 uint64_t tot_sack_blks=0; 537 538 static void 539 sack_filter_dump(FILE *out, struct sack_filter *sf) 540 { 541 int i; 542 fprintf(out, " sf_ack:%u sf_bits:0x%x c:%d used:%d\n", 543 sf->sf_ack, sf->sf_bits, 544 sf->sf_cur, sf->sf_used); 545 546 for(i=0; i<SACK_FILTER_BLOCKS; i++) { 547 if (sack_blk_used(sf, i)) { 548 fprintf(out, "Entry:%d start:%u end:%u the block is %s\n", 549 i, 550 sf->sf_blks[i].start, 551 sf->sf_blks[i].end, 552 (sack_blk_used(sf, i) ? "USED" : "NOT-USED") 553 ); 554 } 555 } 556 } 557 #endif 558 559 #ifndef _KERNEL 560 static 561 #endif 562 int 563 sack_filter_blks(struct tcpcb *tp, struct sack_filter *sf, struct sackblk *in, int numblks, 564 tcp_seq th_ack) 565 { 566 int32_t i, ret; 567 int32_t segmax; 568 569 if (numblks > TCP_MAX_SACK) { 570 #ifdef _KERNEL 571 panic("sf:%p sb:%p Impossible number of sack blocks %d > 4\n", 572 sf, in, 573 numblks); 574 #endif 575 return(numblks); 576 } 577 if (sf->sf_used > 1) 578 sack_board_collapse(sf); 579 580 segmax = tcp_fixed_maxseg(tp); 581 if ((sf->sf_used == 0) && numblks) { 582 /* 583 * We are brand new add the blocks in 584 * reverse order. Note we can see more 585 * than one in new, since ack's could be lost. 586 */ 587 int cnt_added = 0; 588 589 sf->sf_ack = th_ack; 590 for(i=0, sf->sf_cur=0; i<numblks; i++) { 591 if ((in[i].end != tp->snd_max) && 592 ((in[i].end - in[i].start) < segmax)) { 593 /* 594 * We do not accept blocks less than a MSS minus all 595 * possible options space that is not at max_seg. 596 */ 597 continue; 598 } 599 memcpy(&sf->sf_blks[sf->sf_cur], &in[i], sizeof(struct sackblk)); 600 sf->sf_bits = sack_blk_set(sf, sf->sf_cur); 601 sf->sf_cur++; 602 sf->sf_cur %= SACK_FILTER_BLOCKS; 603 sf->sf_used++; 604 cnt_added++; 605 #ifndef _KERNEL 606 if (sf->sf_used > highest_used) 607 highest_used = sf->sf_used; 608 #endif 609 } 610 if (sf->sf_cur) 611 sf->sf_cur--; 612 613 return (cnt_added); 614 } 615 if (SEQ_GT(th_ack, sf->sf_ack)) { 616 sack_filter_prune(sf, th_ack); 617 } 618 if (numblks) { 619 ret = sack_filter_run(sf, in, numblks, th_ack, segmax, tp->snd_max); 620 if (sf->sf_used > 1) 621 sack_board_collapse(sf); 622 } else 623 ret = 0; 624 return (ret); 625 } 626 627 void 628 sack_filter_reject(struct sack_filter *sf, struct sackblk *in) 629 { 630 /* 631 * Given a specified block (that had made 632 * it past the sack filter). Reject that 633 * block triming it off any sack-filter block 634 * that has it. Usually because the block was 635 * too small and did not cover a whole send. 636 * 637 * This function will only "undo" sack-blocks 638 * that are fresh and touch the edges of 639 * blocks in our filter. 640 */ 641 int i; 642 643 for(i=0; i<SACK_FILTER_BLOCKS; i++) { 644 if (sack_blk_used(sf, i) == 0) 645 continue; 646 /* 647 * Now given the sack-filter block does it touch 648 * with one of the ends 649 */ 650 if (sf->sf_blks[i].end == in->end) { 651 /* The end moves back to start */ 652 if (SEQ_GT(in->start, sf->sf_blks[i].start)) 653 /* in-blk |----| */ 654 /* sf-blk |---------| */ 655 sf->sf_blks[i].end = in->start; 656 else { 657 /* It consumes this block */ 658 /* in-blk |---------| */ 659 /* sf-blk |------| */ 660 /* <or> */ 661 /* sf-blk |---------| */ 662 sf->sf_bits = sack_blk_clr(sf, i); 663 sf->sf_used--; 664 } 665 continue; 666 } 667 if (sf->sf_blks[i].start == in->start) { 668 if (SEQ_LT(in->end, sf->sf_blks[i].end)) { 669 /* in-blk |----| */ 670 /* sf-blk |---------| */ 671 sf->sf_blks[i].start = in->end; 672 } else { 673 /* It consumes this block */ 674 /* in-blk |----------| */ 675 /* sf-blk |-------| */ 676 /* <or> */ 677 /* sf-blk |----------| */ 678 sf->sf_bits = sack_blk_clr(sf, i); 679 sf->sf_used--; 680 } 681 continue; 682 } 683 } 684 } 685 686 #ifndef _KERNEL 687 688 int 689 main(int argc, char **argv) 690 { 691 char buffer[512]; 692 struct sackblk blks[TCP_MAX_SACK]; 693 FILE *err; 694 tcp_seq th_ack; 695 struct tcpcb tp; 696 struct sack_filter sf; 697 int32_t numblks,i; 698 int snd_una_set=0; 699 double a, b, c; 700 int invalid_sack_print = 0; 701 uint32_t chg_remembered=0; 702 uint32_t sack_chg=0; 703 char line_buf[10][256]; 704 int line_buf_at=0; 705 706 in = stdin; 707 out = stdout; 708 memset(&tp, 0, sizeof(tp)); 709 tp.t_maxseg = 1460; 710 711 while ((i = getopt(argc, argv, "dIi:o:?hS:")) != -1) { 712 switch (i) { 713 case 'S': 714 tp.t_maxseg = strtol(optarg, NULL, 0); 715 break; 716 case 'd': 717 detailed_dump = 1; 718 break; 719 case'I': 720 invalid_sack_print = 1; 721 break; 722 case 'i': 723 in = fopen(optarg, "r"); 724 if (in == NULL) { 725 fprintf(stderr, "Fatal error can't open %s for input\n", optarg); 726 exit(-1); 727 } 728 break; 729 case 'o': 730 out = fopen(optarg, "w"); 731 if (out == NULL) { 732 fprintf(stderr, "Fatal error can't open %s for output\n", optarg); 733 exit(-1); 734 } 735 break; 736 default: 737 case '?': 738 case 'h': 739 fprintf(stderr, "Use %s [ -i infile -o outfile -I -S maxseg -n -d ]\n", argv[0]); 740 return(0); 741 break; 742 }; 743 } 744 sack_filter_clear(&sf, 0); 745 memset(buffer, 0, sizeof(buffer)); 746 memset(blks, 0, sizeof(blks)); 747 numblks = 0; 748 fprintf(out, "************************************\n"); 749 while (fgets(buffer, sizeof(buffer), in) != NULL) { 750 sprintf(line_buf[line_buf_at], "%s", buffer); 751 line_buf_at++; 752 if (strncmp(buffer, "quit", 4) == 0) { 753 break; 754 } else if (strncmp(buffer, "dump", 4) == 0) { 755 sack_filter_dump(out, &sf); 756 } else if (strncmp(buffer, "max:", 4) == 0) { 757 tp.snd_max = strtoul(&buffer[4], NULL, 0); 758 } else if (strncmp(buffer, "commit", 6) == 0) { 759 int nn, ii; 760 if (numblks) { 761 uint32_t szof, tot_chg; 762 printf("Dumping line buffer (lines:%d)\n", line_buf_at); 763 for(ii=0; ii<line_buf_at; ii++) { 764 fprintf(out, "%s", line_buf[ii]); 765 } 766 fprintf(out, "------------------------------------ call sfb() nb:%d\n", numblks); 767 nn = sack_filter_blks(&tp, &sf, blks, numblks, th_ack); 768 saved += numblks - nn; 769 tot_sack_blks += numblks; 770 for(ii=0, tot_chg=0; ii<nn; ii++) { 771 szof = blks[ii].end - blks[ii].start; 772 tot_chg += szof; 773 fprintf(out, "sack:%u:%u [%u]\n", 774 blks[ii].start, 775 blks[ii].end, szof); 776 } 777 fprintf(out,"************************************\n"); 778 chg_remembered = tot_chg; 779 if (detailed_dump) { 780 sack_filter_dump(out, &sf); 781 fprintf(out,"************************************\n"); 782 } 783 } 784 memset(blks, 0, sizeof(blks)); 785 memset(line_buf, 0, sizeof(line_buf)); 786 line_buf_at=0; 787 numblks = 0; 788 } else if (strncmp(buffer, "chg:", 4) == 0) { 789 sack_chg = strtoul(&buffer[4], NULL, 0); 790 if ((sack_chg != chg_remembered) && 791 (sack_chg > chg_remembered)){ 792 fprintf(out,"***WARNING WILL RODGERS DANGER!! sack_chg:%u last:%u\n", 793 sack_chg, chg_remembered 794 ); 795 } 796 sack_chg = chg_remembered = 0; 797 } else if (strncmp(buffer, "rxt", 3) == 0) { 798 sack_filter_clear(&sf, tp.snd_una); 799 } else if (strncmp(buffer, "ack:", 4) == 0) { 800 th_ack = strtoul(&buffer[4], NULL, 0); 801 if (snd_una_set == 0) { 802 tp.snd_una = th_ack; 803 snd_una_set = 1; 804 } else if (SEQ_GT(th_ack, tp.snd_una)) { 805 tp.snd_una = th_ack; 806 } 807 sack_filter_blks(&tp, &sf, NULL, 0, th_ack); 808 } else if (strncmp(buffer, "exit", 4) == 0) { 809 sack_filter_clear(&sf, tp.snd_una); 810 sack_chg = chg_remembered = 0; 811 } else if (strncmp(buffer, "sack:", 5) == 0) { 812 char *end=NULL; 813 uint32_t start; 814 uint32_t endv; 815 816 start = strtoul(&buffer[5], &end, 0); 817 if (end) { 818 endv = strtoul(&end[1], NULL, 0); 819 } else { 820 fprintf(out, "--Sack invalid skip 0 start:%u : ??\n", start); 821 continue; 822 } 823 if (SEQ_GT(endv, tp.snd_max)) 824 tp.snd_max = endv; 825 if (SEQ_LT(endv, start)) { 826 fprintf(out, "--Sack invalid skip 1 endv:%u < start:%u\n", endv, start); 827 continue; 828 } 829 if (numblks == TCP_MAX_SACK) { 830 fprintf(out, "--Exceeded max %d\n", numblks); 831 exit(0); 832 } 833 blks[numblks].start = start; 834 blks[numblks].end = endv; 835 numblks++; 836 } else if (strncmp(buffer, "rej:n:n", 4) == 0) { 837 struct sackblk in; 838 char *end=NULL; 839 840 in.start = strtoul(&buffer[4], &end, 0); 841 if (end) { 842 in.end = strtoul(&end[1], NULL, 0); 843 sack_filter_reject(&sf, &in); 844 } else 845 fprintf(out, "Invalid input END:A:B\n"); 846 } else if (strncmp(buffer, "save", 4) == 0) { 847 FILE *io; 848 849 io = fopen("sack_setup.bin", "w+"); 850 if (io != NULL) { 851 if (fwrite(&sf, sizeof(sf), 1, io) != 1) { 852 printf("Failed to write out sf data\n"); 853 unlink("sack_setup.bin"); 854 goto outwrite; 855 } 856 if (fwrite(&tp, sizeof(tp), 1, io) != 1) { 857 printf("Failed to write out tp data\n"); 858 unlink("sack_setup.bin"); 859 } else 860 printf("Save completed\n"); 861 outwrite: 862 fclose(io); 863 } else { 864 printf("failed to open sack_setup.bin for writting .. sorry\n"); 865 } 866 } else if (strncmp(buffer, "restore", 7) == 0) { 867 FILE *io; 868 869 io = fopen("sack_setup.bin", "r"); 870 if (io != NULL) { 871 if (fread(&sf, sizeof(sf), 1, io) != 1) { 872 printf("Failed to read out sf data\n"); 873 goto outread; 874 } 875 if (fread(&tp, sizeof(tp), 1, io) != 1) { 876 printf("Failed to read out tp data\n"); 877 } else { 878 printf("Restore completed\n"); 879 sack_filter_dump(out, &sf); 880 } 881 outread: 882 fclose(io); 883 } else { 884 printf("can't open sack_setup.bin -- sorry no load\n"); 885 } 886 887 } else if (strncmp(buffer, "help", 4) == 0) { 888 help: 889 fprintf(out, "You can input:\n"); 890 fprintf(out, "sack:S:E -- to define a sack block\n"); 891 fprintf(out, "rxt -- to clear the filter without changing the remembered\n"); 892 fprintf(out, "save -- save current state to sack_setup.bin\n"); 893 fprintf(out, "restore -- restore state from sack_setup.bin\n"); 894 fprintf(out, "exit -- To clear the sack filter and start all fresh\n"); 895 fprintf(out, "ack:N -- To advance the cum-ack to N\n"); 896 fprintf(out, "max:N -- To set send-max to N\n"); 897 fprintf(out, "commit -- To apply the sack you built to the filter and dump the filter\n"); 898 fprintf(out, "dump -- To display the current contents of the sack filter\n"); 899 fprintf(out, "quit -- To exit this program\n"); 900 } else { 901 fprintf(out, "Command %s unknown\n", buffer); 902 goto help; 903 } 904 memset(buffer, 0, sizeof(buffer)); 905 } 906 if (in != stdin) { 907 fclose(in); 908 } 909 if (out != stdout) { 910 fclose(out); 911 } 912 a = saved * 100.0; 913 b = tot_sack_blks * 1.0; 914 if (b > 0.0) 915 c = a/b; 916 else 917 c = 0.0; 918 if (out != stdout) 919 err = stdout; 920 else 921 err = stderr; 922 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", 923 saved, tot_sack_blks, c, cnt_skipped_oldsack, cnt_used_oldsack, highest_used, over_written, empty_avail); 924 return(0); 925 } 926 #endif 927