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