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
tcp_fixed_maxseg(const struct tcpcb * tp)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
sack_filter_clear(struct sack_filter * sf,tcp_seq seq)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
sack_filter_prune(struct sack_filter * sf,tcp_seq th_ack)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
is_sack_on_board(struct sack_filter * sf,struct sackblk * b,int32_t segmax,uint32_t snd_max)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
sack_move_to_empty(struct sack_filter * sf,uint32_t idx)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
sack_filter_run(struct sack_filter * sf,struct sackblk * in,int numblks,tcp_seq th_ack,int32_t segmax,uint32_t snd_max)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
sack_collapse(struct sack_filter * sf,int32_t src,int32_t into)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
sack_blocks_overlap_or_meet(struct sack_filter * sf,struct sackblk * sb,uint32_t skip)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
sack_board_collapse(struct sack_filter * sf)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
sack_filter_dump(FILE * out,struct sack_filter * sf)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
sack_filter_blks(struct tcpcb * tp,struct sack_filter * sf,struct sackblk * in,int numblks,tcp_seq th_ack)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
sack_filter_reject(struct sack_filter * sf,struct sackblk * in)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
main(int argc,char ** argv)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