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