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