xref: /freebsd/sys/netinet/tcp_stacks/sack_filter.c (revision 092528b05d1fb0776fecb345ce99b1b68e8122b1)
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