xref: /dflybsd-src/sys/netinet/tcp_sack.c (revision 63f17add1cf6119ec8f692990df2892d86244f2f)
191489f6bSJeffrey Hsu /*
291489f6bSJeffrey Hsu  * Copyright (c) 2003, 2004 Jeffrey M. Hsu.  All rights reserved.
391489f6bSJeffrey Hsu  * Copyright (c) 2003, 2004 The DragonFly Project.  All rights reserved.
491489f6bSJeffrey Hsu  *
591489f6bSJeffrey Hsu  * This code is derived from software contributed to The DragonFly Project
691489f6bSJeffrey Hsu  * by Jeffrey M. Hsu.
791489f6bSJeffrey Hsu  *
891489f6bSJeffrey Hsu  * Redistribution and use in source and binary forms, with or without
991489f6bSJeffrey Hsu  * modification, are permitted provided that the following conditions
1091489f6bSJeffrey Hsu  * are met:
1191489f6bSJeffrey Hsu  * 1. Redistributions of source code must retain the above copyright
1291489f6bSJeffrey Hsu  *    notice, this list of conditions and the following disclaimer.
1391489f6bSJeffrey Hsu  * 2. Redistributions in binary form must reproduce the above copyright
1491489f6bSJeffrey Hsu  *    notice, this list of conditions and the following disclaimer in the
1591489f6bSJeffrey Hsu  *    documentation and/or other materials provided with the distribution.
1691489f6bSJeffrey Hsu  * 3. Neither the name of The DragonFly Project nor the names of its
1791489f6bSJeffrey Hsu  *    contributors may be used to endorse or promote products derived
1891489f6bSJeffrey Hsu  *    from this software without specific, prior written permission.
1991489f6bSJeffrey Hsu  *
2091489f6bSJeffrey Hsu  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2191489f6bSJeffrey Hsu  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2291489f6bSJeffrey Hsu  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2391489f6bSJeffrey Hsu  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2491489f6bSJeffrey Hsu  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2591489f6bSJeffrey Hsu  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2691489f6bSJeffrey Hsu  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2791489f6bSJeffrey Hsu  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2891489f6bSJeffrey Hsu  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2991489f6bSJeffrey Hsu  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3091489f6bSJeffrey Hsu  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3191489f6bSJeffrey Hsu  * SUCH DAMAGE.
3291489f6bSJeffrey Hsu  *
3312f68b86SNicolas Thery  * $DragonFly: src/sys/netinet/tcp_sack.c,v 1.8 2008/08/15 21:37:16 nth Exp $
3491489f6bSJeffrey Hsu  */
3591489f6bSJeffrey Hsu 
3691489f6bSJeffrey Hsu #include <sys/param.h>
3791489f6bSJeffrey Hsu #include <sys/systm.h>
3891489f6bSJeffrey Hsu #include <sys/kernel.h>
3991489f6bSJeffrey Hsu #include <sys/malloc.h>
4091489f6bSJeffrey Hsu #include <sys/queue.h>
4191489f6bSJeffrey Hsu #include <sys/thread.h>
4291489f6bSJeffrey Hsu #include <sys/types.h>
4391489f6bSJeffrey Hsu #include <sys/socket.h>
4491489f6bSJeffrey Hsu #include <sys/socketvar.h>
45*63f17addSMatthew Dillon #include <sys/resourcevar.h>
4691489f6bSJeffrey Hsu 
4791489f6bSJeffrey Hsu #include <net/if.h>
4891489f6bSJeffrey Hsu 
4991489f6bSJeffrey Hsu #include <netinet/in.h>
5091489f6bSJeffrey Hsu #include <netinet/in_systm.h>
5191489f6bSJeffrey Hsu #include <netinet/ip.h>
5291489f6bSJeffrey Hsu #include <netinet/in_var.h>
5391489f6bSJeffrey Hsu #include <netinet/in_pcb.h>
5491489f6bSJeffrey Hsu #include <netinet/ip_var.h>
5591489f6bSJeffrey Hsu #include <netinet/tcp.h>
5691489f6bSJeffrey Hsu #include <netinet/tcp_seq.h>
5791489f6bSJeffrey Hsu #include <netinet/tcp_var.h>
5891489f6bSJeffrey Hsu 
59bf55dc6dSSepherosa Ziehau /*
60bf55dc6dSSepherosa Ziehau  * Implemented:
61bf55dc6dSSepherosa Ziehau  *
62bf55dc6dSSepherosa Ziehau  * RFC 2018
63bf55dc6dSSepherosa Ziehau  * RFC 2883
64bf55dc6dSSepherosa Ziehau  * RFC 3517
65859bc3f7SSepherosa Ziehau  * RFC 6675
66bf55dc6dSSepherosa Ziehau  */
67bf55dc6dSSepherosa Ziehau 
6891489f6bSJeffrey Hsu struct sackblock {
6991489f6bSJeffrey Hsu 	tcp_seq			sblk_start;
7091489f6bSJeffrey Hsu 	tcp_seq			sblk_end;
7191489f6bSJeffrey Hsu 	TAILQ_ENTRY(sackblock)	sblk_list;
7291489f6bSJeffrey Hsu };
7391489f6bSJeffrey Hsu 
7491489f6bSJeffrey Hsu #define	MAXSAVEDBLOCKS	8			/* per connection limit */
7591489f6bSJeffrey Hsu 
7601d244e6SSepherosa Ziehau static int insert_block(struct scoreboard *scb,
776c1bbf57SSepherosa Ziehau 			const struct raw_sackblock *raw_sb, boolean_t *update);
7891489f6bSJeffrey Hsu 
7912f68b86SNicolas Thery static MALLOC_DEFINE(M_SACKBLOCK, "sblk", "sackblock struct");
8091489f6bSJeffrey Hsu 
8191489f6bSJeffrey Hsu /*
8291489f6bSJeffrey Hsu  * Per-tcpcb initialization.
8391489f6bSJeffrey Hsu  */
8491489f6bSJeffrey Hsu void
tcp_sack_tcpcb_init(struct tcpcb * tp)8591489f6bSJeffrey Hsu tcp_sack_tcpcb_init(struct tcpcb *tp)
8691489f6bSJeffrey Hsu {
8791489f6bSJeffrey Hsu 	struct scoreboard *scb = &tp->scb;
8891489f6bSJeffrey Hsu 
8991489f6bSJeffrey Hsu 	scb->nblocks = 0;
9091489f6bSJeffrey Hsu 	TAILQ_INIT(&scb->sackblocks);
9191489f6bSJeffrey Hsu 	scb->lastfound = NULL;
9291489f6bSJeffrey Hsu }
9391489f6bSJeffrey Hsu 
9491489f6bSJeffrey Hsu /*
9591489f6bSJeffrey Hsu  * Find the SACK block containing or immediately preceding "seq".
9691489f6bSJeffrey Hsu  * The boolean result indicates whether the sequence is actually
9791489f6bSJeffrey Hsu  * contained in the SACK block.
9891489f6bSJeffrey Hsu  */
9991489f6bSJeffrey Hsu static boolean_t
sack_block_lookup(struct scoreboard * scb,tcp_seq seq,struct sackblock ** sb)10091489f6bSJeffrey Hsu sack_block_lookup(struct scoreboard *scb, tcp_seq seq, struct sackblock **sb)
10191489f6bSJeffrey Hsu {
102*63f17addSMatthew Dillon 	static struct krate sackkrate = { .freq = 1 };
10391489f6bSJeffrey Hsu 	struct sackblock *hint = scb->lastfound;
10491489f6bSJeffrey Hsu 	struct sackblock *cur, *last, *prev;
10591489f6bSJeffrey Hsu 
10691489f6bSJeffrey Hsu 	if (TAILQ_EMPTY(&scb->sackblocks)) {
10791489f6bSJeffrey Hsu 		*sb = NULL;
10891489f6bSJeffrey Hsu 		return FALSE;
10991489f6bSJeffrey Hsu 	}
11091489f6bSJeffrey Hsu 
11191489f6bSJeffrey Hsu 	if (hint == NULL) {
11291489f6bSJeffrey Hsu 		/* No hint.  Search from start to end. */
11391489f6bSJeffrey Hsu 		cur = TAILQ_FIRST(&scb->sackblocks);
11491489f6bSJeffrey Hsu 		last = NULL;
11591489f6bSJeffrey Hsu 		prev = TAILQ_LAST(&scb->sackblocks, sackblock_list);
11691489f6bSJeffrey Hsu 	} else  {
11791489f6bSJeffrey Hsu 		if (SEQ_GEQ(seq, hint->sblk_start)) {
11891489f6bSJeffrey Hsu 			/* Search from hint to end of list. */
11991489f6bSJeffrey Hsu 			cur = hint;
12091489f6bSJeffrey Hsu 			last = NULL;
12191489f6bSJeffrey Hsu 			prev = TAILQ_LAST(&scb->sackblocks, sackblock_list);
12291489f6bSJeffrey Hsu 		} else {
12391489f6bSJeffrey Hsu 			/* Search from front of list to hint. */
12491489f6bSJeffrey Hsu 			cur = TAILQ_FIRST(&scb->sackblocks);
12591489f6bSJeffrey Hsu 			last = hint;
12691489f6bSJeffrey Hsu 			prev = TAILQ_PREV(hint, sackblock_list, sblk_list);
12791489f6bSJeffrey Hsu 		}
12891489f6bSJeffrey Hsu 	}
12991489f6bSJeffrey Hsu 
13091489f6bSJeffrey Hsu 	do {
131*63f17addSMatthew Dillon 		/*
132*63f17addSMatthew Dillon 		 * Ensure we can't crash if the list really blows up due to
133*63f17addSMatthew Dillon 		 * delta sign wraps when comparing seq against sblk_start vs
134*63f17addSMatthew Dillon 		 * sblk_end.
135*63f17addSMatthew Dillon 		 */
136*63f17addSMatthew Dillon 		if (cur == NULL) {
137*63f17addSMatthew Dillon 			krateprintf(&sackkrate,
138*63f17addSMatthew Dillon 				    "tcp_sack: fatal corrupt seq\n");
139*63f17addSMatthew Dillon 			*sb = NULL;
140*63f17addSMatthew Dillon 			return FALSE;
141*63f17addSMatthew Dillon 		}
142*63f17addSMatthew Dillon 
143*63f17addSMatthew Dillon 		/*
144*63f17addSMatthew Dillon 		 * Check completion
145*63f17addSMatthew Dillon 		 */
14691489f6bSJeffrey Hsu 		if (SEQ_GT(cur->sblk_end, seq)) {
14791489f6bSJeffrey Hsu 			if (SEQ_GEQ(seq, cur->sblk_start)) {
14891489f6bSJeffrey Hsu 				*sb = scb->lastfound = cur;
14991489f6bSJeffrey Hsu 				return TRUE;
15091489f6bSJeffrey Hsu 			} else {
15191489f6bSJeffrey Hsu 				*sb = scb->lastfound =
15291489f6bSJeffrey Hsu 				    TAILQ_PREV(cur, sackblock_list, sblk_list);
15391489f6bSJeffrey Hsu 				return FALSE;
15491489f6bSJeffrey Hsu 			}
15591489f6bSJeffrey Hsu 		}
156*63f17addSMatthew Dillon 
157*63f17addSMatthew Dillon 		/*
158*63f17addSMatthew Dillon 		 * seq is greater than sblk_end, nominally proceed to the
159*63f17addSMatthew Dillon 		 * next block.
160*63f17addSMatthew Dillon 		 *
161*63f17addSMatthew Dillon 		 * It is possible for an overflow to cause the comparison
162*63f17addSMatthew Dillon 		 * between seq an sblk_start vs sblk_end to make it appear
163*63f17addSMatthew Dillon 		 * that seq is less than sblk_start and also greater than
164*63f17addSMatthew Dillon 		 * sblk_end.  If we allow the case to fall through we can
165*63f17addSMatthew Dillon 		 * end up with cur == NULL on the next loop.
166*63f17addSMatthew Dillon 		 */
167*63f17addSMatthew Dillon 		if (SEQ_LT(seq, cur->sblk_start)) {
168*63f17addSMatthew Dillon 			krateprintf(&sackkrate,
169*63f17addSMatthew Dillon 				    "tcp_sack: corrupt seq "
170*63f17addSMatthew Dillon 				    "0x%08x vs 0x%08x-0x%08x\n",
171*63f17addSMatthew Dillon 				    seq, cur->sblk_start, cur->sblk_end);
172*63f17addSMatthew Dillon 			if (SEQ_GEQ(seq, cur->sblk_start)) {
173*63f17addSMatthew Dillon 				*sb = scb->lastfound = cur;
174*63f17addSMatthew Dillon 				return TRUE;
175*63f17addSMatthew Dillon 			} else {
176*63f17addSMatthew Dillon 				*sb = scb->lastfound =
177*63f17addSMatthew Dillon 				    TAILQ_PREV(cur, sackblock_list, sblk_list);
178*63f17addSMatthew Dillon 				return FALSE;
179*63f17addSMatthew Dillon 			}
180*63f17addSMatthew Dillon 		}
18191489f6bSJeffrey Hsu 		cur = TAILQ_NEXT(cur, sblk_list);
18291489f6bSJeffrey Hsu 	} while (cur != last);
18391489f6bSJeffrey Hsu 
18491489f6bSJeffrey Hsu 	*sb = scb->lastfound = prev;
18591489f6bSJeffrey Hsu 	return FALSE;
18691489f6bSJeffrey Hsu }
18791489f6bSJeffrey Hsu 
18891489f6bSJeffrey Hsu /*
18991489f6bSJeffrey Hsu  * Allocate a SACK block.
19091489f6bSJeffrey Hsu  */
19191489f6bSJeffrey Hsu static __inline struct sackblock *
alloc_sackblock(struct scoreboard * scb,const struct raw_sackblock * raw_sb)192006af0dbSSepherosa Ziehau alloc_sackblock(struct scoreboard *scb, const struct raw_sackblock *raw_sb)
19391489f6bSJeffrey Hsu {
19426d3f1fbSSepherosa Ziehau 	struct sackblock *sb;
19526d3f1fbSSepherosa Ziehau 
196006af0dbSSepherosa Ziehau 	if (scb->freecache != NULL) {
197006af0dbSSepherosa Ziehau 		sb = scb->freecache;
198006af0dbSSepherosa Ziehau 		scb->freecache = NULL;
199006af0dbSSepherosa Ziehau 		tcpstat.tcps_sacksbfast++;
200006af0dbSSepherosa Ziehau 	} else {
20126d3f1fbSSepherosa Ziehau 		sb = kmalloc(sizeof(struct sackblock), M_SACKBLOCK, M_NOWAIT);
202006af0dbSSepherosa Ziehau 		if (sb == NULL) {
203006af0dbSSepherosa Ziehau 			tcpstat.tcps_sacksbfailed++;
204006af0dbSSepherosa Ziehau 			return NULL;
205006af0dbSSepherosa Ziehau 		}
206006af0dbSSepherosa Ziehau 	}
20726d3f1fbSSepherosa Ziehau 	sb->sblk_start = raw_sb->rblk_start;
20826d3f1fbSSepherosa Ziehau 	sb->sblk_end = raw_sb->rblk_end;
20926d3f1fbSSepherosa Ziehau 	return sb;
21091489f6bSJeffrey Hsu }
21191489f6bSJeffrey Hsu 
21201d244e6SSepherosa Ziehau static __inline struct sackblock *
alloc_sackblock_limit(struct scoreboard * scb,const struct raw_sackblock * raw_sb)21301d244e6SSepherosa Ziehau alloc_sackblock_limit(struct scoreboard *scb,
21401d244e6SSepherosa Ziehau     const struct raw_sackblock *raw_sb)
21501d244e6SSepherosa Ziehau {
21601d244e6SSepherosa Ziehau 	if (scb->nblocks == MAXSAVEDBLOCKS) {
21701d244e6SSepherosa Ziehau 		/*
21801d244e6SSepherosa Ziehau 		 * Should try to kick out older blocks XXX JH
21901d244e6SSepherosa Ziehau 		 * May be able to coalesce with existing block.
22001d244e6SSepherosa Ziehau 		 * Or, go other way and free all blocks if we hit
22101d244e6SSepherosa Ziehau 		 * this limit.
22201d244e6SSepherosa Ziehau 		 */
22301d244e6SSepherosa Ziehau 		tcpstat.tcps_sacksboverflow++;
22401d244e6SSepherosa Ziehau 		return NULL;
22501d244e6SSepherosa Ziehau 	}
226006af0dbSSepherosa Ziehau 	return alloc_sackblock(scb, raw_sb);
22701d244e6SSepherosa Ziehau }
22801d244e6SSepherosa Ziehau 
22991489f6bSJeffrey Hsu /*
23091489f6bSJeffrey Hsu  * Free a SACK block.
23191489f6bSJeffrey Hsu  */
23291489f6bSJeffrey Hsu static __inline void
free_sackblock(struct scoreboard * scb,struct sackblock * s)233006af0dbSSepherosa Ziehau free_sackblock(struct scoreboard *scb, struct sackblock *s)
23491489f6bSJeffrey Hsu {
235006af0dbSSepherosa Ziehau 	if (scb->freecache == NULL) {
236006af0dbSSepherosa Ziehau 		/* YYY Maybe use the latest freed block? */
237006af0dbSSepherosa Ziehau 		scb->freecache = s;
238006af0dbSSepherosa Ziehau 		return;
239006af0dbSSepherosa Ziehau 	}
24012f68b86SNicolas Thery 	kfree(s, M_SACKBLOCK);
24191489f6bSJeffrey Hsu }
24291489f6bSJeffrey Hsu 
24391489f6bSJeffrey Hsu /*
24491489f6bSJeffrey Hsu  * Free up SACK blocks for data that's been acked.
24591489f6bSJeffrey Hsu  */
24691489f6bSJeffrey Hsu static void
tcp_sack_ack_blocks(struct tcpcb * tp,tcp_seq th_ack)247f616e306SSepherosa Ziehau tcp_sack_ack_blocks(struct tcpcb *tp, tcp_seq th_ack)
24891489f6bSJeffrey Hsu {
249f616e306SSepherosa Ziehau 	struct scoreboard *scb = &tp->scb;
25091489f6bSJeffrey Hsu 	struct sackblock *sb, *nb;
25191489f6bSJeffrey Hsu 
25291489f6bSJeffrey Hsu 	sb = TAILQ_FIRST(&scb->sackblocks);
25391489f6bSJeffrey Hsu 	while (sb && SEQ_LEQ(sb->sblk_end, th_ack)) {
25491489f6bSJeffrey Hsu 		nb = TAILQ_NEXT(sb, sblk_list);
2559e3d6c96SMatthew Dillon 		if (scb->lastfound == sb)
25691489f6bSJeffrey Hsu 			scb->lastfound = NULL;
25791489f6bSJeffrey Hsu 		TAILQ_REMOVE(&scb->sackblocks, sb, sblk_list);
258006af0dbSSepherosa Ziehau 		free_sackblock(scb, sb);
25991489f6bSJeffrey Hsu 		--scb->nblocks;
26091489f6bSJeffrey Hsu 		KASSERT(scb->nblocks >= 0,
26191489f6bSJeffrey Hsu 		    ("SACK block count underflow: %d < 0", scb->nblocks));
26291489f6bSJeffrey Hsu 		sb = nb;
26391489f6bSJeffrey Hsu 	}
2649ba0ed2fSSepherosa Ziehau 	if (sb && SEQ_GEQ(th_ack, sb->sblk_start)) {
2659ba0ed2fSSepherosa Ziehau 		/* Other side reneged? XXX */
2669ba0ed2fSSepherosa Ziehau 		tcpstat.tcps_sackrenege++;
267f616e306SSepherosa Ziehau 		tcp_sack_discard(tp);
2689ba0ed2fSSepherosa Ziehau 	}
26991489f6bSJeffrey Hsu }
27091489f6bSJeffrey Hsu 
27191489f6bSJeffrey Hsu /*
27291489f6bSJeffrey Hsu  * Delete and free SACK blocks saved in scoreboard.
27391489f6bSJeffrey Hsu  */
274f616e306SSepherosa Ziehau static void
tcp_sack_cleanup(struct scoreboard * scb)27591489f6bSJeffrey Hsu tcp_sack_cleanup(struct scoreboard *scb)
27691489f6bSJeffrey Hsu {
27791489f6bSJeffrey Hsu 	struct sackblock *sb, *nb;
27891489f6bSJeffrey Hsu 
27991489f6bSJeffrey Hsu 	TAILQ_FOREACH_MUTABLE(sb, &scb->sackblocks, sblk_list, nb) {
280006af0dbSSepherosa Ziehau 		free_sackblock(scb, sb);
28191489f6bSJeffrey Hsu 		--scb->nblocks;
28291489f6bSJeffrey Hsu 	}
28391489f6bSJeffrey Hsu 	KASSERT(scb->nblocks == 0,
28491489f6bSJeffrey Hsu 	    ("SACK block %d count not zero", scb->nblocks));
28591489f6bSJeffrey Hsu 	TAILQ_INIT(&scb->sackblocks);
28691489f6bSJeffrey Hsu 	scb->lastfound = NULL;
28791489f6bSJeffrey Hsu }
28891489f6bSJeffrey Hsu 
28991489f6bSJeffrey Hsu /*
290f616e306SSepherosa Ziehau  * Discard SACK scoreboard, HighRxt, RescueRxt and LostSeq.
291f616e306SSepherosa Ziehau  */
292f616e306SSepherosa Ziehau void
tcp_sack_discard(struct tcpcb * tp)293f616e306SSepherosa Ziehau tcp_sack_discard(struct tcpcb *tp)
294f616e306SSepherosa Ziehau {
295f616e306SSepherosa Ziehau 	tcp_sack_cleanup(&tp->scb);
296f616e306SSepherosa Ziehau 	tp->rexmt_high = tp->snd_una;
297f616e306SSepherosa Ziehau 	tp->sack_flags &= ~TSACK_F_SACKRESCUED;
298f616e306SSepherosa Ziehau 	tp->scb.lostseq = tp->snd_una;
299f616e306SSepherosa Ziehau }
300f616e306SSepherosa Ziehau 
301f616e306SSepherosa Ziehau /*
302006af0dbSSepherosa Ziehau  * Delete and free SACK blocks saved in scoreboard.
303006af0dbSSepherosa Ziehau  * Delete the one slot block cache.
304006af0dbSSepherosa Ziehau  */
305006af0dbSSepherosa Ziehau void
tcp_sack_destroy(struct scoreboard * scb)306006af0dbSSepherosa Ziehau tcp_sack_destroy(struct scoreboard *scb)
307006af0dbSSepherosa Ziehau {
308006af0dbSSepherosa Ziehau 	tcp_sack_cleanup(scb);
309006af0dbSSepherosa Ziehau 	if (scb->freecache != NULL) {
310006af0dbSSepherosa Ziehau 		kfree(scb->freecache, M_SACKBLOCK);
311006af0dbSSepherosa Ziehau 		scb->freecache = NULL;
312006af0dbSSepherosa Ziehau 	}
313006af0dbSSepherosa Ziehau }
314006af0dbSSepherosa Ziehau 
315006af0dbSSepherosa Ziehau /*
316d58ca578SSepherosa Ziehau  * Cleanup the reported SACK block information
317d58ca578SSepherosa Ziehau  */
318d58ca578SSepherosa Ziehau void
tcp_sack_report_cleanup(struct tcpcb * tp)319d58ca578SSepherosa Ziehau tcp_sack_report_cleanup(struct tcpcb *tp)
320d58ca578SSepherosa Ziehau {
321c7e6499aSSepherosa Ziehau 	tp->sack_flags &=
322c7e6499aSSepherosa Ziehau 	    ~(TSACK_F_DUPSEG | TSACK_F_ENCLOSESEG | TSACK_F_SACKLEFT);
323d58ca578SSepherosa Ziehau 	tp->reportblk.rblk_start = tp->reportblk.rblk_end;
324d58ca578SSepherosa Ziehau }
325d58ca578SSepherosa Ziehau 
326d58ca578SSepherosa Ziehau /*
3272fb3a851SSepherosa Ziehau  * Whether SACK report is needed or not
3282fb3a851SSepherosa Ziehau  */
3292fb3a851SSepherosa Ziehau boolean_t
tcp_sack_report_needed(const struct tcpcb * tp)3302fb3a851SSepherosa Ziehau tcp_sack_report_needed(const struct tcpcb *tp)
3312fb3a851SSepherosa Ziehau {
3322fb3a851SSepherosa Ziehau 	if ((tp->sack_flags &
3332fb3a851SSepherosa Ziehau 	     (TSACK_F_DUPSEG | TSACK_F_ENCLOSESEG | TSACK_F_SACKLEFT)) ||
3342fb3a851SSepherosa Ziehau 	    tp->reportblk.rblk_start != tp->reportblk.rblk_end)
3352fb3a851SSepherosa Ziehau 		return TRUE;
3362fb3a851SSepherosa Ziehau 	else
3372fb3a851SSepherosa Ziehau 		return FALSE;
3382fb3a851SSepherosa Ziehau }
3392fb3a851SSepherosa Ziehau 
3402fb3a851SSepherosa Ziehau /*
34191489f6bSJeffrey Hsu  * Returns	0 if not D-SACK block,
34291489f6bSJeffrey Hsu  *		1 if D-SACK,
34391489f6bSJeffrey Hsu  *		2 if duplicate of out-of-order D-SACK block.
34491489f6bSJeffrey Hsu  */
34591489f6bSJeffrey Hsu int
tcp_sack_ndsack_blocks(const struct raw_sackblock * blocks,const int numblocks,tcp_seq snd_una)346aaf34417SSepherosa Ziehau tcp_sack_ndsack_blocks(const struct raw_sackblock *blocks, const int numblocks,
34791489f6bSJeffrey Hsu     tcp_seq snd_una)
34891489f6bSJeffrey Hsu {
34991489f6bSJeffrey Hsu 	if (numblocks == 0)
35091489f6bSJeffrey Hsu 		return 0;
35191489f6bSJeffrey Hsu 
35291489f6bSJeffrey Hsu 	if (SEQ_LT(blocks[0].rblk_start, snd_una))
35391489f6bSJeffrey Hsu 		return 1;
35491489f6bSJeffrey Hsu 
35591489f6bSJeffrey Hsu 	/* block 0 inside block 1 */
35691489f6bSJeffrey Hsu 	if (numblocks > 1 &&
35791489f6bSJeffrey Hsu 	    SEQ_GEQ(blocks[0].rblk_start, blocks[1].rblk_start) &&
35891489f6bSJeffrey Hsu 	    SEQ_LEQ(blocks[0].rblk_end, blocks[1].rblk_end))
35991489f6bSJeffrey Hsu 		return 2;
36091489f6bSJeffrey Hsu 
36191489f6bSJeffrey Hsu 	return 0;
36291489f6bSJeffrey Hsu }
36391489f6bSJeffrey Hsu 
36491489f6bSJeffrey Hsu /*
36591489f6bSJeffrey Hsu  * Update scoreboard on new incoming ACK.
36691489f6bSJeffrey Hsu  */
36791489f6bSJeffrey Hsu static void
tcp_sack_add_blocks(struct tcpcb * tp,struct tcpopt * to)36891489f6bSJeffrey Hsu tcp_sack_add_blocks(struct tcpcb *tp, struct tcpopt *to)
36991489f6bSJeffrey Hsu {
37091489f6bSJeffrey Hsu 	const int numblocks = to->to_nsackblocks;
37191489f6bSJeffrey Hsu 	struct raw_sackblock *blocks = to->to_sackblocks;
37291489f6bSJeffrey Hsu 	struct scoreboard *scb = &tp->scb;
3736c1bbf57SSepherosa Ziehau 	int startblock, i;
37491489f6bSJeffrey Hsu 
37591489f6bSJeffrey Hsu 	if (tcp_sack_ndsack_blocks(blocks, numblocks, tp->snd_una) > 0)
37691489f6bSJeffrey Hsu 		startblock = 1;
37791489f6bSJeffrey Hsu 	else
37891489f6bSJeffrey Hsu 		startblock = 0;
37991489f6bSJeffrey Hsu 
3806c1bbf57SSepherosa Ziehau 	to->to_flags |= TOF_SACK_REDUNDANT;
38191489f6bSJeffrey Hsu 	for (i = startblock; i < numblocks; i++) {
38291489f6bSJeffrey Hsu 		struct raw_sackblock *newsackblock = &blocks[i];
3836c1bbf57SSepherosa Ziehau 		boolean_t update;
3846c1bbf57SSepherosa Ziehau 		int error;
38591489f6bSJeffrey Hsu 
386331721f5SSepherosa Ziehau 		/* Guard against ACK reordering */
387ccb518eaSSepherosa Ziehau 		if (SEQ_LEQ(newsackblock->rblk_start, tp->snd_una))
388331721f5SSepherosa Ziehau 			continue;
389331721f5SSepherosa Ziehau 
390331721f5SSepherosa Ziehau 		/* Don't accept bad SACK blocks */
39102cc2f35SSepherosa Ziehau 		if (SEQ_GT(newsackblock->rblk_end, tp->snd_max)) {
39202cc2f35SSepherosa Ziehau 			tcpstat.tcps_rcvbadsackopt++;
39391489f6bSJeffrey Hsu 			break;		/* skip all other blocks */
39402cc2f35SSepherosa Ziehau 		}
39502cc2f35SSepherosa Ziehau 		tcpstat.tcps_sacksbupdate++;
39691489f6bSJeffrey Hsu 
3976c1bbf57SSepherosa Ziehau 		error = insert_block(scb, newsackblock, &update);
3986c1bbf57SSepherosa Ziehau 		if (update)
3996c1bbf57SSepherosa Ziehau 			to->to_flags &= ~TOF_SACK_REDUNDANT;
4006c1bbf57SSepherosa Ziehau 		if (error)
40101d244e6SSepherosa Ziehau 			break;
40291489f6bSJeffrey Hsu 	}
40391489f6bSJeffrey Hsu }
40491489f6bSJeffrey Hsu 
40591489f6bSJeffrey Hsu void
tcp_sack_update_scoreboard(struct tcpcb * tp,struct tcpopt * to)40691489f6bSJeffrey Hsu tcp_sack_update_scoreboard(struct tcpcb *tp, struct tcpopt *to)
40791489f6bSJeffrey Hsu {
40891489f6bSJeffrey Hsu 	struct scoreboard *scb = &tp->scb;
409a098966fSSepherosa Ziehau 	int rexmt_high_update = 0;
41091489f6bSJeffrey Hsu 
411f616e306SSepherosa Ziehau 	tcp_sack_ack_blocks(tp, tp->snd_una);
41291489f6bSJeffrey Hsu 	tcp_sack_add_blocks(tp, to);
413e2289e66SSepherosa Ziehau 	tcp_sack_update_lostseq(scb, tp->snd_una, tp->t_maxseg,
414e2289e66SSepherosa Ziehau 	    tp->t_rxtthresh);
415a098966fSSepherosa Ziehau 	if (SEQ_LT(tp->rexmt_high, tp->snd_una)) {
41691489f6bSJeffrey Hsu 		tp->rexmt_high = tp->snd_una;
417a098966fSSepherosa Ziehau 		rexmt_high_update = 1;
418a098966fSSepherosa Ziehau 	}
419c7e6499aSSepherosa Ziehau 	if (tp->sack_flags & TSACK_F_SACKRESCUED) {
4208003f426SSepherosa Ziehau 		if (SEQ_LEQ(tp->rexmt_rescue, tp->snd_una)) {
421c7e6499aSSepherosa Ziehau 			tp->sack_flags &= ~TSACK_F_SACKRESCUED;
42221f337caSSepherosa Ziehau 		} else if (tcp_aggressive_rescuesack && rexmt_high_update &&
423a098966fSSepherosa Ziehau 		    SEQ_LT(tp->rexmt_rescue, tp->rexmt_high)) {
424a098966fSSepherosa Ziehau 			/* Drag RescueRxt along with HighRxt */
425a098966fSSepherosa Ziehau 			tp->rexmt_rescue = tp->rexmt_high;
426a098966fSSepherosa Ziehau 		}
427a098966fSSepherosa Ziehau 	}
42891489f6bSJeffrey Hsu }
42991489f6bSJeffrey Hsu 
43091489f6bSJeffrey Hsu /*
43191489f6bSJeffrey Hsu  * Insert SACK block into sender's scoreboard.
43291489f6bSJeffrey Hsu  */
43301d244e6SSepherosa Ziehau static int
insert_block(struct scoreboard * scb,const struct raw_sackblock * raw_sb,boolean_t * update)4346c1bbf57SSepherosa Ziehau insert_block(struct scoreboard *scb, const struct raw_sackblock *raw_sb,
4356c1bbf57SSepherosa Ziehau     boolean_t *update)
43691489f6bSJeffrey Hsu {
43791489f6bSJeffrey Hsu 	struct sackblock *sb, *workingblock;
43891489f6bSJeffrey Hsu 	boolean_t overlap_front;
43991489f6bSJeffrey Hsu 
4406c1bbf57SSepherosa Ziehau 	*update = TRUE;
4412ed615e0SSepherosa Ziehau 	if (TAILQ_EMPTY(&scb->sackblocks)) {
44201d244e6SSepherosa Ziehau 		struct sackblock *newblock;
44391489f6bSJeffrey Hsu 
44401d244e6SSepherosa Ziehau 		KASSERT(scb->nblocks == 0, ("emply scb w/ blocks"));
44501d244e6SSepherosa Ziehau 
446006af0dbSSepherosa Ziehau 		newblock = alloc_sackblock(scb, raw_sb);
44701d244e6SSepherosa Ziehau 		if (newblock == NULL)
44801d244e6SSepherosa Ziehau 			return ENOMEM;
44901d244e6SSepherosa Ziehau 		TAILQ_INSERT_HEAD(&scb->sackblocks, newblock, sblk_list);
45001d244e6SSepherosa Ziehau 		scb->nblocks = 1;
45101d244e6SSepherosa Ziehau 		return 0;
45291489f6bSJeffrey Hsu 	}
45301d244e6SSepherosa Ziehau 
45401d244e6SSepherosa Ziehau 	KASSERT(scb->nblocks > 0, ("insert_block() called w/ no blocks"));
45501d244e6SSepherosa Ziehau 	KASSERT(scb->nblocks <= MAXSAVEDBLOCKS,
45691489f6bSJeffrey Hsu 	    ("too many SACK blocks %d", scb->nblocks));
45791489f6bSJeffrey Hsu 
45801d244e6SSepherosa Ziehau 	overlap_front = sack_block_lookup(scb, raw_sb->rblk_start, &sb);
45991489f6bSJeffrey Hsu 
46091489f6bSJeffrey Hsu 	if (sb == NULL) {
46101d244e6SSepherosa Ziehau 		workingblock = alloc_sackblock_limit(scb, raw_sb);
46201d244e6SSepherosa Ziehau 		if (workingblock == NULL)
46301d244e6SSepherosa Ziehau 			return ENOMEM;
46401d244e6SSepherosa Ziehau 		TAILQ_INSERT_HEAD(&scb->sackblocks, workingblock, sblk_list);
46591489f6bSJeffrey Hsu 		++scb->nblocks;
46691489f6bSJeffrey Hsu 	} else {
46701d244e6SSepherosa Ziehau 		if (overlap_front || sb->sblk_end == raw_sb->rblk_start) {
468f3c063edSSepherosa Ziehau 			tcpstat.tcps_sacksbreused++;
469f3c063edSSepherosa Ziehau 
47001d244e6SSepherosa Ziehau 			/* Extend old block */
47191489f6bSJeffrey Hsu 			workingblock = sb;
472f3c063edSSepherosa Ziehau 			if (SEQ_GT(raw_sb->rblk_end, sb->sblk_end)) {
47301d244e6SSepherosa Ziehau 				sb->sblk_end = raw_sb->rblk_end;
474f3c063edSSepherosa Ziehau 			} else {
475f3c063edSSepherosa Ziehau 				/* Exact match, nothing to consolidate */
4766c1bbf57SSepherosa Ziehau 				*update = FALSE;
477f3c063edSSepherosa Ziehau 				return 0;
478f3c063edSSepherosa Ziehau 			}
47991489f6bSJeffrey Hsu 		} else {
48001d244e6SSepherosa Ziehau 			workingblock = alloc_sackblock_limit(scb, raw_sb);
48101d244e6SSepherosa Ziehau 			if (workingblock == NULL)
48201d244e6SSepherosa Ziehau 				return ENOMEM;
48301d244e6SSepherosa Ziehau 			TAILQ_INSERT_AFTER(&scb->sackblocks, sb, workingblock,
48491489f6bSJeffrey Hsu 			    sblk_list);
48591489f6bSJeffrey Hsu 			++scb->nblocks;
48691489f6bSJeffrey Hsu 		}
48791489f6bSJeffrey Hsu 	}
48891489f6bSJeffrey Hsu 
48991489f6bSJeffrey Hsu 	/* Consolidate right-hand side. */
49091489f6bSJeffrey Hsu 	sb = TAILQ_NEXT(workingblock, sblk_list);
49191489f6bSJeffrey Hsu 	while (sb != NULL &&
49291489f6bSJeffrey Hsu 	    SEQ_GEQ(workingblock->sblk_end, sb->sblk_end)) {
49391489f6bSJeffrey Hsu 		struct sackblock *nextblock;
49491489f6bSJeffrey Hsu 
49591489f6bSJeffrey Hsu 		nextblock = TAILQ_NEXT(sb, sblk_list);
4969e3d6c96SMatthew Dillon 		if (scb->lastfound == sb)
4979e3d6c96SMatthew Dillon 			scb->lastfound = NULL;
49891489f6bSJeffrey Hsu 		/* Remove completely overlapped block */
49991489f6bSJeffrey Hsu 		TAILQ_REMOVE(&scb->sackblocks, sb, sblk_list);
500006af0dbSSepherosa Ziehau 		free_sackblock(scb, sb);
50191489f6bSJeffrey Hsu 		--scb->nblocks;
50291489f6bSJeffrey Hsu 		KASSERT(scb->nblocks > 0,
50391489f6bSJeffrey Hsu 		    ("removed overlapped block: %d blocks left", scb->nblocks));
50491489f6bSJeffrey Hsu 		sb = nextblock;
50591489f6bSJeffrey Hsu 	}
50691489f6bSJeffrey Hsu 	if (sb != NULL &&
50791489f6bSJeffrey Hsu 	    SEQ_GEQ(workingblock->sblk_end, sb->sblk_start)) {
50891489f6bSJeffrey Hsu 		/* Extend new block to cover partially overlapped old block. */
50991489f6bSJeffrey Hsu 		workingblock->sblk_end = sb->sblk_end;
5109e3d6c96SMatthew Dillon 		if (scb->lastfound == sb)
5119e3d6c96SMatthew Dillon 			scb->lastfound = NULL;
51291489f6bSJeffrey Hsu 		TAILQ_REMOVE(&scb->sackblocks, sb, sblk_list);
513006af0dbSSepherosa Ziehau 		free_sackblock(scb, sb);
51491489f6bSJeffrey Hsu 		--scb->nblocks;
51591489f6bSJeffrey Hsu 		KASSERT(scb->nblocks > 0,
51691489f6bSJeffrey Hsu 		    ("removed partial right: %d blocks left", scb->nblocks));
51791489f6bSJeffrey Hsu 	}
51801d244e6SSepherosa Ziehau 	return 0;
51991489f6bSJeffrey Hsu }
52091489f6bSJeffrey Hsu 
52191489f6bSJeffrey Hsu #ifdef DEBUG_SACK_BLOCKS
52291489f6bSJeffrey Hsu static void
tcp_sack_dump_blocks(const struct scoreboard * scb)523aaf34417SSepherosa Ziehau tcp_sack_dump_blocks(const struct scoreboard *scb)
52491489f6bSJeffrey Hsu {
525aaf34417SSepherosa Ziehau 	const struct sackblock *sb;
52691489f6bSJeffrey Hsu 
527a6ec04bcSSascha Wildner 	kprintf("%d blocks:", scb->nblocks);
52891489f6bSJeffrey Hsu 	TAILQ_FOREACH(sb, &scb->sackblocks, sblk_list)
529a6ec04bcSSascha Wildner 		kprintf(" [%u, %u)", sb->sblk_start, sb->sblk_end);
530a6ec04bcSSascha Wildner 	kprintf("\n");
53191489f6bSJeffrey Hsu }
53291489f6bSJeffrey Hsu #else
53391489f6bSJeffrey Hsu static __inline void
tcp_sack_dump_blocks(const struct scoreboard * scb)534aaf34417SSepherosa Ziehau tcp_sack_dump_blocks(const struct scoreboard *scb)
53591489f6bSJeffrey Hsu {
53691489f6bSJeffrey Hsu }
53791489f6bSJeffrey Hsu #endif
53891489f6bSJeffrey Hsu 
53991489f6bSJeffrey Hsu /*
54091489f6bSJeffrey Hsu  * Optimization to quickly determine which packets are lost.
54191489f6bSJeffrey Hsu  */
542e2289e66SSepherosa Ziehau void
tcp_sack_update_lostseq(struct scoreboard * scb,tcp_seq snd_una,u_int maxseg,int rxtthresh)543e2289e66SSepherosa Ziehau tcp_sack_update_lostseq(struct scoreboard *scb, tcp_seq snd_una, u_int maxseg,
5447e4852bbSSepherosa Ziehau     int rxtthresh)
54591489f6bSJeffrey Hsu {
54691489f6bSJeffrey Hsu 	struct sackblock *sb;
54791489f6bSJeffrey Hsu 	int nsackblocks = 0;
54891489f6bSJeffrey Hsu 	int bytes_sacked = 0;
549ffe35e17SSepherosa Ziehau 	int rxtthresh_bytes;
550ffe35e17SSepherosa Ziehau 
551859bc3f7SSepherosa Ziehau 	if (tcp_do_rfc6675)
552ffe35e17SSepherosa Ziehau 		rxtthresh_bytes = (rxtthresh - 1) * maxseg;
553ffe35e17SSepherosa Ziehau 	else
554ffe35e17SSepherosa Ziehau 		rxtthresh_bytes = rxtthresh * maxseg;
55591489f6bSJeffrey Hsu 
55691489f6bSJeffrey Hsu 	sb = TAILQ_LAST(&scb->sackblocks, sackblock_list);
55791489f6bSJeffrey Hsu 	while (sb != NULL) {
55891489f6bSJeffrey Hsu 		++nsackblocks;
55991489f6bSJeffrey Hsu 		bytes_sacked += sb->sblk_end - sb->sblk_start;
5607e4852bbSSepherosa Ziehau 		if (nsackblocks == rxtthresh ||
561ffe35e17SSepherosa Ziehau 		    bytes_sacked >= rxtthresh_bytes) {
56291489f6bSJeffrey Hsu 			scb->lostseq = sb->sblk_start;
56391489f6bSJeffrey Hsu 			return;
56491489f6bSJeffrey Hsu 		}
56591489f6bSJeffrey Hsu 		sb = TAILQ_PREV(sb, sackblock_list, sblk_list);
56691489f6bSJeffrey Hsu 	}
56791489f6bSJeffrey Hsu 	scb->lostseq = snd_una;
56891489f6bSJeffrey Hsu }
56991489f6bSJeffrey Hsu 
57091489f6bSJeffrey Hsu /*
57191489f6bSJeffrey Hsu  * Return whether the given sequence number is considered lost.
57291489f6bSJeffrey Hsu  */
573ffe35e17SSepherosa Ziehau boolean_t
tcp_sack_islost(const struct scoreboard * scb,tcp_seq seqnum)574aaf34417SSepherosa Ziehau tcp_sack_islost(const struct scoreboard *scb, tcp_seq seqnum)
57591489f6bSJeffrey Hsu {
57691489f6bSJeffrey Hsu 	return SEQ_LT(seqnum, scb->lostseq);
57791489f6bSJeffrey Hsu }
57891489f6bSJeffrey Hsu 
57991489f6bSJeffrey Hsu /*
58091489f6bSJeffrey Hsu  * True if at least "amount" has been SACKed.  Used by Early Retransmit.
58191489f6bSJeffrey Hsu  */
58291489f6bSJeffrey Hsu boolean_t
tcp_sack_has_sacked(const struct scoreboard * scb,u_int amount)583aaf34417SSepherosa Ziehau tcp_sack_has_sacked(const struct scoreboard *scb, u_int amount)
58491489f6bSJeffrey Hsu {
585aaf34417SSepherosa Ziehau 	const struct sackblock *sb;
58691489f6bSJeffrey Hsu 	int bytes_sacked = 0;
58791489f6bSJeffrey Hsu 
58891489f6bSJeffrey Hsu 	TAILQ_FOREACH(sb, &scb->sackblocks, sblk_list) {
58991489f6bSJeffrey Hsu 		bytes_sacked += sb->sblk_end - sb->sblk_start;
59091489f6bSJeffrey Hsu 		if (bytes_sacked >= amount)
59191489f6bSJeffrey Hsu 			return TRUE;
59291489f6bSJeffrey Hsu 	}
59391489f6bSJeffrey Hsu 	return FALSE;
59491489f6bSJeffrey Hsu }
59591489f6bSJeffrey Hsu 
59691489f6bSJeffrey Hsu /*
5979a5b142fSJeffrey Hsu  * Number of bytes SACKed below seq.
5989a5b142fSJeffrey Hsu  */
5999a5b142fSJeffrey Hsu int
tcp_sack_bytes_below(const struct scoreboard * scb,tcp_seq seq)600aaf34417SSepherosa Ziehau tcp_sack_bytes_below(const struct scoreboard *scb, tcp_seq seq)
6019a5b142fSJeffrey Hsu {
602aaf34417SSepherosa Ziehau 	const struct sackblock *sb;
6039a5b142fSJeffrey Hsu 	int bytes_sacked = 0;
6049a5b142fSJeffrey Hsu 
6059a5b142fSJeffrey Hsu 	sb = TAILQ_FIRST(&scb->sackblocks);
6069a5b142fSJeffrey Hsu 	while (sb && SEQ_GT(seq, sb->sblk_start)) {
6079a5b142fSJeffrey Hsu 		bytes_sacked += seq_min(seq, sb->sblk_end) - sb->sblk_start;
6089a5b142fSJeffrey Hsu 		sb = TAILQ_NEXT(sb, sblk_list);
6099a5b142fSJeffrey Hsu 	}
6109a5b142fSJeffrey Hsu 	return bytes_sacked;
6119a5b142fSJeffrey Hsu }
6129a5b142fSJeffrey Hsu 
6139a5b142fSJeffrey Hsu /*
61491489f6bSJeffrey Hsu  * Return estimate of the number of bytes outstanding in the network.
61591489f6bSJeffrey Hsu  */
61691489f6bSJeffrey Hsu uint32_t
tcp_sack_compute_pipe(const struct tcpcb * tp)617aaf34417SSepherosa Ziehau tcp_sack_compute_pipe(const struct tcpcb *tp)
61891489f6bSJeffrey Hsu {
619aaf34417SSepherosa Ziehau 	const struct scoreboard *scb = &tp->scb;
620aaf34417SSepherosa Ziehau 	const struct sackblock *sb;
62191489f6bSJeffrey Hsu 	int nlost, nretransmitted;
62291489f6bSJeffrey Hsu 	tcp_seq end;
62391489f6bSJeffrey Hsu 
62491489f6bSJeffrey Hsu 	nlost = tp->snd_max - scb->lostseq;
62591489f6bSJeffrey Hsu 	nretransmitted = tp->rexmt_high - tp->snd_una;
62691489f6bSJeffrey Hsu 
62791489f6bSJeffrey Hsu 	TAILQ_FOREACH(sb, &scb->sackblocks, sblk_list) {
62891489f6bSJeffrey Hsu 		if (SEQ_LT(sb->sblk_start, tp->rexmt_high)) {
62991489f6bSJeffrey Hsu 			end = seq_min(sb->sblk_end, tp->rexmt_high);
63091489f6bSJeffrey Hsu 			nretransmitted -= end - sb->sblk_start;
63191489f6bSJeffrey Hsu 		}
63291489f6bSJeffrey Hsu 		if (SEQ_GEQ(sb->sblk_start, scb->lostseq))
63391489f6bSJeffrey Hsu 			nlost -= sb->sblk_end - sb->sblk_start;
63491489f6bSJeffrey Hsu 	}
63591489f6bSJeffrey Hsu 
63691489f6bSJeffrey Hsu 	return (nlost + nretransmitted);
63791489f6bSJeffrey Hsu }
63891489f6bSJeffrey Hsu 
63991489f6bSJeffrey Hsu /*
64091489f6bSJeffrey Hsu  * Return the sequence number and length of the next segment to transmit
64191489f6bSJeffrey Hsu  * when in Fast Recovery.
64291489f6bSJeffrey Hsu  */
64391489f6bSJeffrey Hsu boolean_t
tcp_sack_nextseg(struct tcpcb * tp,tcp_seq * nextrexmt,uint32_t * plen,boolean_t * rescue)64491489f6bSJeffrey Hsu tcp_sack_nextseg(struct tcpcb *tp, tcp_seq *nextrexmt, uint32_t *plen,
6451bdfd728SSepherosa Ziehau     boolean_t *rescue)
64691489f6bSJeffrey Hsu {
64791489f6bSJeffrey Hsu 	struct scoreboard *scb = &tp->scb;
64891489f6bSJeffrey Hsu 	struct socket *so = tp->t_inpcb->inp_socket;
64991489f6bSJeffrey Hsu 	struct sackblock *sb;
65091489f6bSJeffrey Hsu 	const struct sackblock *lastblock =
65191489f6bSJeffrey Hsu 	    TAILQ_LAST(&scb->sackblocks, sackblock_list);
65291489f6bSJeffrey Hsu 	tcp_seq torexmt;
65308d3c60bSSepherosa Ziehau 	long len, off, sendwin;
65491489f6bSJeffrey Hsu 
65591489f6bSJeffrey Hsu 	/* skip SACKed data */
65691489f6bSJeffrey Hsu 	tcp_sack_skip_sacked(scb, &tp->rexmt_high);
65791489f6bSJeffrey Hsu 
65891489f6bSJeffrey Hsu 	/* Look for lost data. */
65991489f6bSJeffrey Hsu 	torexmt = tp->rexmt_high;
6601bdfd728SSepherosa Ziehau 	*rescue = FALSE;
66191489f6bSJeffrey Hsu 	if (lastblock != NULL) {
66291489f6bSJeffrey Hsu 		if (SEQ_LT(torexmt, lastblock->sblk_end) &&
663ffe35e17SSepherosa Ziehau 		    tcp_sack_islost(scb, torexmt)) {
66491489f6bSJeffrey Hsu sendunsacked:
66591489f6bSJeffrey Hsu 			*nextrexmt = torexmt;
66691489f6bSJeffrey Hsu 			/* If the left-hand edge has been SACKed, pull it in. */
66791489f6bSJeffrey Hsu 			if (sack_block_lookup(scb, torexmt + tp->t_maxseg, &sb))
66891489f6bSJeffrey Hsu 				*plen = sb->sblk_start - torexmt;
66991489f6bSJeffrey Hsu 			else
67091489f6bSJeffrey Hsu 				*plen = tp->t_maxseg;
67191489f6bSJeffrey Hsu 			return TRUE;
67291489f6bSJeffrey Hsu 		}
67391489f6bSJeffrey Hsu 	}
67491489f6bSJeffrey Hsu 
67591489f6bSJeffrey Hsu 	/* See if unsent data available within send window. */
67691489f6bSJeffrey Hsu 	off = tp->snd_max - tp->snd_una;
67708d3c60bSSepherosa Ziehau 	sendwin = min(tp->snd_wnd, tp->snd_bwnd);
67808d3c60bSSepherosa Ziehau 	len = (long) ulmin(so->so_snd.ssb_cc, sendwin) - off;
67991489f6bSJeffrey Hsu 	if (len > 0) {
68091489f6bSJeffrey Hsu 		*nextrexmt = tp->snd_max;	/* Send new data. */
68191489f6bSJeffrey Hsu 		*plen = tp->t_maxseg;
68291489f6bSJeffrey Hsu 		return TRUE;
68391489f6bSJeffrey Hsu 	}
68491489f6bSJeffrey Hsu 
68591489f6bSJeffrey Hsu 	/* We're less certain this data has been lost. */
6861bdfd728SSepherosa Ziehau 	if (lastblock != NULL && SEQ_LT(torexmt, lastblock->sblk_end))
68791489f6bSJeffrey Hsu 		goto sendunsacked;
68891489f6bSJeffrey Hsu 
689a098966fSSepherosa Ziehau 	/* Rescue retransmission */
690859bc3f7SSepherosa Ziehau 	if (tcp_do_rescuesack || tcp_do_rfc6675) {
6911bdfd728SSepherosa Ziehau 		tcpstat.tcps_sackrescue_try++;
692c7e6499aSSepherosa Ziehau 		if (tp->sack_flags & TSACK_F_SACKRESCUED) {
693a098966fSSepherosa Ziehau 			if (!tcp_aggressive_rescuesack)
6941bdfd728SSepherosa Ziehau 				return FALSE;
695a098966fSSepherosa Ziehau 
696a098966fSSepherosa Ziehau 			/*
697a098966fSSepherosa Ziehau 			 * Aggressive variant of the rescue retransmission.
698a098966fSSepherosa Ziehau 			 *
699a098966fSSepherosa Ziehau 			 * The idea of the rescue retransmission is to sustain
700a098966fSSepherosa Ziehau 			 * the ACK clock thus to avoid timeout retransmission.
701a098966fSSepherosa Ziehau 			 *
702a098966fSSepherosa Ziehau 			 * Under some situations, the conservative approach
703a098966fSSepherosa Ziehau 			 * suggested in the draft
704a098966fSSepherosa Ziehau  			 * http://tools.ietf.org/html/
705a098966fSSepherosa Ziehau 			 * draft-nishida-tcpm-rescue-retransmission-00
706a098966fSSepherosa Ziehau 			 * could not sustain ACK clock, since it only allows
707a098966fSSepherosa Ziehau 			 * one rescue retransmission before a cumulative ACK
708a098966fSSepherosa Ziehau 			 * covers the segement transmitted by rescue
709a098966fSSepherosa Ziehau 			 * retransmission.
710a098966fSSepherosa Ziehau 			 *
711a098966fSSepherosa Ziehau 			 * We try to locate the next unSACKed segment which
712a098966fSSepherosa Ziehau 			 * follows the previously sent rescue segment.  If
713a098966fSSepherosa Ziehau 			 * there is no such segment, we loop back to the first
714a098966fSSepherosa Ziehau 			 * unacknowledged segment.
715a098966fSSepherosa Ziehau 			 */
716a098966fSSepherosa Ziehau 
717a098966fSSepherosa Ziehau 			/*
718a098966fSSepherosa Ziehau 			 * Skip SACKed data, but here we follow
719a098966fSSepherosa Ziehau 			 * the last transmitted rescue segment.
720a098966fSSepherosa Ziehau 			 */
721a098966fSSepherosa Ziehau 			torexmt = tp->rexmt_rescue;
722a098966fSSepherosa Ziehau 			tcp_sack_skip_sacked(scb, &torexmt);
723eb5f6cffSSepherosa Ziehau 		}
724a098966fSSepherosa Ziehau 		if (torexmt == tp->snd_max) {
725a098966fSSepherosa Ziehau 			/* Nothing left to retransmit; restart */
726a098966fSSepherosa Ziehau 			torexmt = tp->snd_una;
727a098966fSSepherosa Ziehau 		}
7281bdfd728SSepherosa Ziehau 		*rescue = TRUE;
7291bdfd728SSepherosa Ziehau 		goto sendunsacked;
7301bdfd728SSepherosa Ziehau 	} else if (tcp_do_smartsack && lastblock == NULL) {
731a098966fSSepherosa Ziehau 		tcpstat.tcps_sackrescue_try++;
732a098966fSSepherosa Ziehau 		*rescue = TRUE;
7331bdfd728SSepherosa Ziehau 		goto sendunsacked;
7341bdfd728SSepherosa Ziehau 	}
7351bdfd728SSepherosa Ziehau 
73691489f6bSJeffrey Hsu 	return FALSE;
73791489f6bSJeffrey Hsu }
73891489f6bSJeffrey Hsu 
73991489f6bSJeffrey Hsu /*
74091489f6bSJeffrey Hsu  * Return the next sequence number higher than "*prexmt" that has
74191489f6bSJeffrey Hsu  * not been SACKed.
74291489f6bSJeffrey Hsu  */
74391489f6bSJeffrey Hsu void
tcp_sack_skip_sacked(struct scoreboard * scb,tcp_seq * prexmt)74491489f6bSJeffrey Hsu tcp_sack_skip_sacked(struct scoreboard *scb, tcp_seq *prexmt)
74591489f6bSJeffrey Hsu {
74691489f6bSJeffrey Hsu 	struct sackblock *sb;
74791489f6bSJeffrey Hsu 
74891489f6bSJeffrey Hsu 	/* skip SACKed data */
74991489f6bSJeffrey Hsu 	if (sack_block_lookup(scb, *prexmt, &sb))
75091489f6bSJeffrey Hsu 		*prexmt = sb->sblk_end;
75191489f6bSJeffrey Hsu }
75291489f6bSJeffrey Hsu 
753ccb518eaSSepherosa Ziehau /*
754ccb518eaSSepherosa Ziehau  * The length of the first amount of unSACKed data
755ccb518eaSSepherosa Ziehau  */
756ccb518eaSSepherosa Ziehau uint32_t
tcp_sack_first_unsacked_len(const struct tcpcb * tp)757aaf34417SSepherosa Ziehau tcp_sack_first_unsacked_len(const struct tcpcb *tp)
758ccb518eaSSepherosa Ziehau {
759aaf34417SSepherosa Ziehau 	const struct sackblock *sb;
760ccb518eaSSepherosa Ziehau 
761ccb518eaSSepherosa Ziehau 	sb = TAILQ_FIRST(&tp->scb.sackblocks);
762ccb518eaSSepherosa Ziehau 	if (sb == NULL)
763ccb518eaSSepherosa Ziehau 		return tp->t_maxseg;
764ccb518eaSSepherosa Ziehau 
765ccb518eaSSepherosa Ziehau 	KASSERT(SEQ_LT(tp->snd_una, sb->sblk_start),
766ccb518eaSSepherosa Ziehau 	    ("invalid sb start %u, snd_una %u",
767ccb518eaSSepherosa Ziehau 	     sb->sblk_start, tp->snd_una));
768ccb518eaSSepherosa Ziehau 	return (sb->sblk_start - tp->snd_una);
769ccb518eaSSepherosa Ziehau }
770ccb518eaSSepherosa Ziehau 
77191489f6bSJeffrey Hsu #ifdef later
77291489f6bSJeffrey Hsu void
tcp_sack_save_scoreboard(struct scoreboard * scb)77391489f6bSJeffrey Hsu tcp_sack_save_scoreboard(struct scoreboard *scb)
77491489f6bSJeffrey Hsu {
77591489f6bSJeffrey Hsu 	struct scoreboard *scb = &tp->scb;
77691489f6bSJeffrey Hsu 
77791489f6bSJeffrey Hsu 	scb->sackblocks_prev = scb->sackblocks;
77891489f6bSJeffrey Hsu 	TAILQ_INIT(&scb->sackblocks);
77991489f6bSJeffrey Hsu }
78091489f6bSJeffrey Hsu 
78191489f6bSJeffrey Hsu void
tcp_sack_revert_scoreboard(struct scoreboard * scb,tcp_seq snd_una,u_int maxseg)78291489f6bSJeffrey Hsu tcp_sack_revert_scoreboard(struct scoreboard *scb, tcp_seq snd_una,
78391489f6bSJeffrey Hsu 			   u_int maxseg)
78491489f6bSJeffrey Hsu {
78591489f6bSJeffrey Hsu 	struct sackblock *sb;
78691489f6bSJeffrey Hsu 
78791489f6bSJeffrey Hsu 	scb->sackblocks = scb->sackblocks_prev;
78891489f6bSJeffrey Hsu 	scb->nblocks = 0;
78991489f6bSJeffrey Hsu 	TAILQ_FOREACH(sb, &scb->sackblocks, sblk_list)
79091489f6bSJeffrey Hsu 		++scb->nblocks;
79191489f6bSJeffrey Hsu 	tcp_sack_ack_blocks(scb, snd_una);
79291489f6bSJeffrey Hsu 	scb->lastfound = NULL;
79391489f6bSJeffrey Hsu }
79491489f6bSJeffrey Hsu #endif
79591489f6bSJeffrey Hsu 
79691489f6bSJeffrey Hsu #ifdef DEBUG_SACK_HISTORY
79791489f6bSJeffrey Hsu static void
tcp_sack_dump_history(const char * msg,const struct tcpcb * tp)798aaf34417SSepherosa Ziehau tcp_sack_dump_history(const char *msg, const struct tcpcb *tp)
79991489f6bSJeffrey Hsu {
80091489f6bSJeffrey Hsu 	int i;
80191489f6bSJeffrey Hsu 	static int ndumped;
80291489f6bSJeffrey Hsu 
80391489f6bSJeffrey Hsu 	/* only need a couple of these to debug most problems */
80491489f6bSJeffrey Hsu 	if (++ndumped > 900)
80591489f6bSJeffrey Hsu 		return;
80691489f6bSJeffrey Hsu 
807a6ec04bcSSascha Wildner 	kprintf("%s:\tnsackhistory %d: ", msg, tp->nsackhistory);
80891489f6bSJeffrey Hsu 	for (i = 0; i < tp->nsackhistory; ++i)
809a6ec04bcSSascha Wildner 		kprintf("[%u, %u) ", tp->sackhistory[i].rblk_start,
81091489f6bSJeffrey Hsu 		    tp->sackhistory[i].rblk_end);
811a6ec04bcSSascha Wildner 	kprintf("\n");
81291489f6bSJeffrey Hsu }
81391489f6bSJeffrey Hsu #else
81491489f6bSJeffrey Hsu static __inline void
tcp_sack_dump_history(const char * msg,const struct tcpcb * tp)815aaf34417SSepherosa Ziehau tcp_sack_dump_history(const char *msg, const struct tcpcb *tp)
81691489f6bSJeffrey Hsu {
81791489f6bSJeffrey Hsu }
81891489f6bSJeffrey Hsu #endif
81991489f6bSJeffrey Hsu 
82091489f6bSJeffrey Hsu /*
82191489f6bSJeffrey Hsu  * Remove old SACK blocks from the SACK history that have already been ACKed.
82291489f6bSJeffrey Hsu  */
82391489f6bSJeffrey Hsu static void
tcp_sack_ack_history(struct tcpcb * tp)82491489f6bSJeffrey Hsu tcp_sack_ack_history(struct tcpcb *tp)
82591489f6bSJeffrey Hsu {
82691489f6bSJeffrey Hsu 	int i, nblocks, openslot;
82791489f6bSJeffrey Hsu 
82891489f6bSJeffrey Hsu 	tcp_sack_dump_history("before tcp_sack_ack_history", tp);
82991489f6bSJeffrey Hsu 	nblocks = tp->nsackhistory;
83091489f6bSJeffrey Hsu 	for (i = openslot = 0; i < nblocks; ++i) {
83191489f6bSJeffrey Hsu 		if (SEQ_LEQ(tp->sackhistory[i].rblk_end, tp->rcv_nxt)) {
83291489f6bSJeffrey Hsu 			--tp->nsackhistory;
83391489f6bSJeffrey Hsu 			continue;
83491489f6bSJeffrey Hsu 		}
83591489f6bSJeffrey Hsu 		if (SEQ_LT(tp->sackhistory[i].rblk_start, tp->rcv_nxt))
83691489f6bSJeffrey Hsu 			tp->sackhistory[i].rblk_start = tp->rcv_nxt;
83791489f6bSJeffrey Hsu 		if (i == openslot)
83891489f6bSJeffrey Hsu 			++openslot;
83991489f6bSJeffrey Hsu 		else
84091489f6bSJeffrey Hsu 			tp->sackhistory[openslot++] = tp->sackhistory[i];
84191489f6bSJeffrey Hsu 	}
84291489f6bSJeffrey Hsu 	tcp_sack_dump_history("after tcp_sack_ack_history", tp);
84391489f6bSJeffrey Hsu 	KASSERT(openslot == tp->nsackhistory,
84491489f6bSJeffrey Hsu 	    ("tcp_sack_ack_history miscounted: %d != %d",
84591489f6bSJeffrey Hsu 	    openslot, tp->nsackhistory));
84691489f6bSJeffrey Hsu }
84791489f6bSJeffrey Hsu 
84891489f6bSJeffrey Hsu /*
84991489f6bSJeffrey Hsu  * Add or merge newblock into reported history.
85091489f6bSJeffrey Hsu  * Also remove or update SACK blocks that will be acked.
85191489f6bSJeffrey Hsu  */
85291489f6bSJeffrey Hsu static void
tcp_sack_update_reported_history(struct tcpcb * tp,tcp_seq start,tcp_seq end)85391489f6bSJeffrey Hsu tcp_sack_update_reported_history(struct tcpcb *tp, tcp_seq start, tcp_seq end)
85491489f6bSJeffrey Hsu {
85591489f6bSJeffrey Hsu 	struct raw_sackblock copy[MAX_SACK_REPORT_BLOCKS];
85691489f6bSJeffrey Hsu 	int i, cindex;
85791489f6bSJeffrey Hsu 
85891489f6bSJeffrey Hsu 	tcp_sack_dump_history("before tcp_sack_update_reported_history", tp);
85991489f6bSJeffrey Hsu 	/*
86091489f6bSJeffrey Hsu 	 * Six cases:
86191489f6bSJeffrey Hsu 	 *	0) no overlap
86291489f6bSJeffrey Hsu 	 *	1) newblock == oldblock
86391489f6bSJeffrey Hsu 	 *	2) oldblock contains newblock
86491489f6bSJeffrey Hsu 	 *	3) newblock contains oldblock
86591489f6bSJeffrey Hsu 	 *	4) tail of oldblock overlaps or abuts start of newblock
86691489f6bSJeffrey Hsu 	 *	5) tail of newblock overlaps or abuts head of oldblock
86791489f6bSJeffrey Hsu 	 */
86891489f6bSJeffrey Hsu 	for (i = cindex = 0; i < tp->nsackhistory; ++i) {
86991489f6bSJeffrey Hsu 		struct raw_sackblock *oldblock = &tp->sackhistory[i];
87091489f6bSJeffrey Hsu 		tcp_seq old_start = oldblock->rblk_start;
87191489f6bSJeffrey Hsu 		tcp_seq old_end = oldblock->rblk_end;
87291489f6bSJeffrey Hsu 
87391489f6bSJeffrey Hsu 		if (SEQ_LT(end, old_start) || SEQ_GT(start, old_end)) {
87491489f6bSJeffrey Hsu 			/* Case 0:  no overlap.  Copy old block. */
87591489f6bSJeffrey Hsu 			copy[cindex++] = *oldblock;
87691489f6bSJeffrey Hsu 			continue;
87791489f6bSJeffrey Hsu 		}
87891489f6bSJeffrey Hsu 
87991489f6bSJeffrey Hsu 		if (SEQ_GEQ(start, old_start) && SEQ_LEQ(end, old_end)) {
88091489f6bSJeffrey Hsu 			/* Cases 1 & 2.  Move block to front of history. */
88191489f6bSJeffrey Hsu 			int j;
88291489f6bSJeffrey Hsu 
88391489f6bSJeffrey Hsu 			start = old_start;
88491489f6bSJeffrey Hsu 			end = old_end;
88591489f6bSJeffrey Hsu 			/* no need to check rest of blocks */
88691489f6bSJeffrey Hsu 			for (j = i + 1; j < tp->nsackhistory; ++j)
88791489f6bSJeffrey Hsu 				copy[cindex++] = tp->sackhistory[j];
88891489f6bSJeffrey Hsu 			break;
88991489f6bSJeffrey Hsu 		}
89091489f6bSJeffrey Hsu 
89191489f6bSJeffrey Hsu 		if (SEQ_GEQ(old_end, start) && SEQ_LT(old_start, start)) {
89291489f6bSJeffrey Hsu 			/* Case 4:  extend start of new block. */
89391489f6bSJeffrey Hsu 			start = old_start;
89491489f6bSJeffrey Hsu 		} else if (SEQ_GEQ(end, old_start) && SEQ_GT(old_end, end)) {
89591489f6bSJeffrey Hsu 			/* Case 5: extend end of new block */
89691489f6bSJeffrey Hsu 			end = old_end;
89791489f6bSJeffrey Hsu 		} else {
89891489f6bSJeffrey Hsu 			/* Case 3.  Delete old block by not copying it. */
89991489f6bSJeffrey Hsu 			KASSERT(SEQ_LEQ(start, old_start) &&
90091489f6bSJeffrey Hsu 				SEQ_GEQ(end, old_end),
90191489f6bSJeffrey Hsu 			    ("bad logic: old [%u, %u), new [%u, %u)",
90291489f6bSJeffrey Hsu 			     old_start, old_end, start, end));
90391489f6bSJeffrey Hsu 		}
90491489f6bSJeffrey Hsu 	}
90591489f6bSJeffrey Hsu 
90691489f6bSJeffrey Hsu 	/* insert new block */
90791489f6bSJeffrey Hsu 	tp->sackhistory[0].rblk_start = start;
90891489f6bSJeffrey Hsu 	tp->sackhistory[0].rblk_end = end;
90991489f6bSJeffrey Hsu 	cindex = min(cindex, MAX_SACK_REPORT_BLOCKS - 1);
91091489f6bSJeffrey Hsu 	for (i = 0; i < cindex; ++i)
91191489f6bSJeffrey Hsu 		tp->sackhistory[i + 1] = copy[i];
91291489f6bSJeffrey Hsu 	tp->nsackhistory = cindex + 1;
91391489f6bSJeffrey Hsu 	tcp_sack_dump_history("after tcp_sack_update_reported_history", tp);
91491489f6bSJeffrey Hsu }
91591489f6bSJeffrey Hsu 
91691489f6bSJeffrey Hsu /*
91791489f6bSJeffrey Hsu  * Fill in SACK report to return to data sender.
91891489f6bSJeffrey Hsu  */
91991489f6bSJeffrey Hsu void
tcp_sack_fill_report(struct tcpcb * tp,u_char * opt,u_int * plen)92091489f6bSJeffrey Hsu tcp_sack_fill_report(struct tcpcb *tp, u_char *opt, u_int *plen)
92191489f6bSJeffrey Hsu {
92291489f6bSJeffrey Hsu 	u_int optlen = *plen;
92391489f6bSJeffrey Hsu 	uint32_t *lp = (uint32_t *)(opt + optlen);
92491489f6bSJeffrey Hsu 	uint32_t *olp;
92591489f6bSJeffrey Hsu 	tcp_seq hstart = tp->rcv_nxt, hend;
92691489f6bSJeffrey Hsu 	int nblocks;
92791489f6bSJeffrey Hsu 
92891489f6bSJeffrey Hsu 	KASSERT(TCP_MAXOLEN - optlen >=
92991489f6bSJeffrey Hsu 	    TCPOLEN_SACK_ALIGNED + TCPOLEN_SACK_BLOCK,
93091489f6bSJeffrey Hsu 	    ("no room for SACK header and one block: optlen %d", optlen));
93191489f6bSJeffrey Hsu 
932c7e6499aSSepherosa Ziehau 	if (tp->sack_flags & TSACK_F_DUPSEG)
93302cc2f35SSepherosa Ziehau 		tcpstat.tcps_snddsackopt++;
93402cc2f35SSepherosa Ziehau 	else
93502cc2f35SSepherosa Ziehau 		tcpstat.tcps_sndsackopt++;
93602cc2f35SSepherosa Ziehau 
93791489f6bSJeffrey Hsu 	olp = lp++;
93891489f6bSJeffrey Hsu 	optlen += TCPOLEN_SACK_ALIGNED;
93991489f6bSJeffrey Hsu 
94091489f6bSJeffrey Hsu 	tcp_sack_ack_history(tp);
94191489f6bSJeffrey Hsu 	if (tp->reportblk.rblk_start != tp->reportblk.rblk_end) {
94291489f6bSJeffrey Hsu 		*lp++ = htonl(tp->reportblk.rblk_start);
94391489f6bSJeffrey Hsu 		*lp++ = htonl(tp->reportblk.rblk_end);
94491489f6bSJeffrey Hsu 		optlen += TCPOLEN_SACK_BLOCK;
94591489f6bSJeffrey Hsu 		hstart = tp->reportblk.rblk_start;
94691489f6bSJeffrey Hsu 		hend = tp->reportblk.rblk_end;
947c7e6499aSSepherosa Ziehau 		if (tp->sack_flags & TSACK_F_ENCLOSESEG) {
94891489f6bSJeffrey Hsu 			KASSERT(TCP_MAXOLEN - optlen >= TCPOLEN_SACK_BLOCK,
94991489f6bSJeffrey Hsu 			    ("no room for enclosing SACK block: oplen %d",
95091489f6bSJeffrey Hsu 			    optlen));
95191489f6bSJeffrey Hsu 			*lp++ = htonl(tp->encloseblk.rblk_start);
95291489f6bSJeffrey Hsu 			*lp++ = htonl(tp->encloseblk.rblk_end);
95391489f6bSJeffrey Hsu 			optlen += TCPOLEN_SACK_BLOCK;
95491489f6bSJeffrey Hsu 			hstart = tp->encloseblk.rblk_start;
95591489f6bSJeffrey Hsu 			hend = tp->encloseblk.rblk_end;
95691489f6bSJeffrey Hsu 		}
95791489f6bSJeffrey Hsu 		if (SEQ_GT(hstart, tp->rcv_nxt))
95891489f6bSJeffrey Hsu 			tcp_sack_update_reported_history(tp, hstart, hend);
95991489f6bSJeffrey Hsu 	}
960c7e6499aSSepherosa Ziehau 	if (tcp_do_smartsack && (tp->sack_flags & TSACK_F_SACKLEFT)) {
96191489f6bSJeffrey Hsu 		/* Fill in from left!  Walk re-assembly queue. */
96291489f6bSJeffrey Hsu 		struct tseg_qent *q;
96391489f6bSJeffrey Hsu 
9640f9e45deSSepherosa Ziehau 		q = TAILQ_FIRST(&tp->t_segq);
96591489f6bSJeffrey Hsu 		while (q != NULL &&
96691489f6bSJeffrey Hsu 		    TCP_MAXOLEN - optlen >= TCPOLEN_SACK_BLOCK) {
96791489f6bSJeffrey Hsu 			*lp++ = htonl(q->tqe_th->th_seq);
9683a5d999bSSepherosa Ziehau 			*lp++ = htonl(TCP_SACK_BLKEND(
9693a5d999bSSepherosa Ziehau 			    q->tqe_th->th_seq + q->tqe_len,
9703a5d999bSSepherosa Ziehau 			    q->tqe_th->th_flags));
97191489f6bSJeffrey Hsu 			optlen += TCPOLEN_SACK_BLOCK;
9720f9e45deSSepherosa Ziehau 			q = TAILQ_NEXT(q, tqe_q);
97391489f6bSJeffrey Hsu 		}
97491489f6bSJeffrey Hsu 	} else {
97591489f6bSJeffrey Hsu 		int n = 0;
97691489f6bSJeffrey Hsu 
97791489f6bSJeffrey Hsu 		/* Fill in SACK blocks from right side. */
97891489f6bSJeffrey Hsu 		while (n < tp->nsackhistory &&
97991489f6bSJeffrey Hsu 		    TCP_MAXOLEN - optlen >= TCPOLEN_SACK_BLOCK) {
98091489f6bSJeffrey Hsu 			if (tp->sackhistory[n].rblk_start != hstart) {
98191489f6bSJeffrey Hsu 				*lp++ = htonl(tp->sackhistory[n].rblk_start);
98291489f6bSJeffrey Hsu 				*lp++ = htonl(tp->sackhistory[n].rblk_end);
98391489f6bSJeffrey Hsu 				optlen += TCPOLEN_SACK_BLOCK;
98491489f6bSJeffrey Hsu 			}
98591489f6bSJeffrey Hsu 			++n;
98691489f6bSJeffrey Hsu 		}
98791489f6bSJeffrey Hsu 	}
98891489f6bSJeffrey Hsu 	tp->reportblk.rblk_start = tp->reportblk.rblk_end;
989c7e6499aSSepherosa Ziehau 	tp->sack_flags &=
990c7e6499aSSepherosa Ziehau 	    ~(TSACK_F_DUPSEG | TSACK_F_ENCLOSESEG | TSACK_F_SACKLEFT);
99191489f6bSJeffrey Hsu 	nblocks = (lp - olp - 1) / 2;
99291489f6bSJeffrey Hsu 	*olp = htonl(TCPOPT_SACK_ALIGNED |
99391489f6bSJeffrey Hsu 		     (TCPOLEN_SACK + nblocks * TCPOLEN_SACK_BLOCK));
99491489f6bSJeffrey Hsu 	*plen = optlen;
99591489f6bSJeffrey Hsu }
996