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