1 /*
2 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 * Description: implementation for TCP SACK APIs
15 * Author: none
16 * Create: 2020
17 */
18
19 #include "lwip/tcp_sack.h"
20 #include "lwip/priv/tcp_priv.h"
21
22 #include <string.h>
23
24 #if LWIP_SACK
25
26 #define MAX_NUM_SACK_OPTS 4
27 #define MAX_TCPOPT_LEN 40
28 #define MAX_NUM_SACK_BLOCK 4
29
30 #define BYTES_FOR_OPTION_BLOCK 8
31 #define BYTES_FOR_SACK_LENGTH_OPTION 2
32 #define U32_COUNT_ONE_OPTION_BLOCK 2 // BYTES_FOR_OPTION_BLOCK/sizeof(u32_t)
33 #define BYTES_FOR_SACK_KIND_AND_TYPE 4
34
35 struct tcp_sack_block {
36 u32_t start_seq;
37 u32_t end_seq;
38 };
39
40 /* g_sack_blocks holds the received sack blocks for the processing packet */
41 struct tcp_sack_block g_sack_blocks[MAX_NUM_SACK_BLOCK];
42
43 static int tcp_sack_is_sackblock_valid(const struct tcp_pcb *pcb, u32_t start_seq,
44 u32_t end_seq, u32_t ackno);
45 extern err_t
46 tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif);
47
48 /*
49 * Called when a new TCP connection is established (i.e when
50 * a SYN or SYN-ACK is received)
51 *
52 * @param pcb the new pcb in which the SYN/ SYN-ACK segment has arrived
53 * @return void
54 *
55 * @note this function is called only when the connection is newly established
56 * and not in the later stages.
57 */
58 void
tcp_pcb_reset_sack_seq(struct tcp_pcb * pcb)59 tcp_pcb_reset_sack_seq(struct tcp_pcb *pcb)
60 {
61 pcb->sack_seq = NULL;
62 }
63
64 #if TCP_QUEUE_OOSEQ
65 /**
66 * Called when a TCP segment is received. This function checks and updates the
67 * SACK blocks to be sent if Out Of Seq packets are present. If not, then send
68 * just an ACK
69 *
70 * @param pcb the pcb in which the segment has arrived
71 * @return void
72 *
73 */
74 void
tcp_update_sack_for_received_ooseq_segs(struct tcp_pcb * pcb)75 tcp_update_sack_for_received_ooseq_segs(struct tcp_pcb *pcb)
76 {
77 struct tcp_seg *cur = pcb->ooseq;
78 struct _sack_seq *ptr, *ptr1, tmp_sack_seq;
79
80 LWIP_ASSERT("pcb->sack_seq != NULL\n", pcb->sack_seq == NULL);
81 if (pcb->flags & TF_SACK) {
82 for (ptr = pcb->sack_seq, cur = pcb->ooseq; cur != NULL; cur = cur->next) {
83 if (ptr == NULL) {
84 ptr = (struct _sack_seq *)mem_malloc(sizeof(struct _sack_seq));
85 LWIP_ERROR("mem_malloc failed.\n", ptr != NULL, goto mem_malloc_err1;);
86 pcb->sack_seq = ptr;
87 ptr->left = cur->tcphdr->seqno;
88 ptr->right = cur->tcphdr->seqno + cur->len;
89 ptr->order = cur->order;
90 ptr->next = NULL;
91 } else {
92 if (cur->tcphdr->seqno == ptr->right) {
93 ptr->right += cur->len;
94 ptr->order = (cur->order > ptr->order) ? cur->order : ptr->order;
95 } else if (TCP_SEQ_GT(cur->tcphdr->seqno, ptr->right)) {
96 pcb->sack_seq = (struct _sack_seq *)mem_malloc(sizeof(struct _sack_seq));
97 LWIP_ERROR("mem_malloc failed.\n", pcb->sack_seq != NULL, goto mem_malloc_err1;);
98 pcb->sack_seq->next = ptr;
99 ptr = pcb->sack_seq;
100 ptr->left = cur->tcphdr->seqno;
101 ptr->right = cur->tcphdr->seqno + cur->len;
102 ptr->order = cur->order;
103 }
104 }
105 }
106 for (ptr = pcb->sack_seq; ptr != NULL; ptr = ptr->next) {
107 for (ptr1 = ptr->next; ptr1 != NULL; ptr1 = ptr1->next) {
108 if (ptr1->order > ptr->order) {
109 (void)memcpy_s(&tmp_sack_seq, sizeof(struct _sack_seq), ptr1, sizeof(struct _sack_seq));
110
111 (void)memcpy_s(ptr1, sizeof(struct _sack_seq), ptr, sizeof(struct _sack_seq));
112 ptr1->next = tmp_sack_seq.next;
113
114 tmp_sack_seq.next = ptr->next;
115 (void)memcpy_s(ptr, sizeof(struct _sack_seq), &tmp_sack_seq, sizeof(struct _sack_seq));
116 }
117 }
118 }
119 }
120 mem_malloc_err1:
121 if (pcb->ooseq != NULL) {
122 (void)tcp_send_empty_ack(pcb);
123 } else {
124 /* Acknowledge the segment(s). */
125 tcp_ack(pcb);
126 }
127 }
128 #endif /* TCP_QUEUE_OOSEQ */
129
130 /**
131 * Called by tcp_create_segment to initialize SACK related fields in the segment
132 *
133 * @param seg the newly created segment
134 * @return void
135 *
136 */
137 void
tcp_update_sack_fields_for_new_seg(struct tcp_seg * seg)138 tcp_update_sack_fields_for_new_seg(struct tcp_seg *seg)
139 {
140 seg->state = TF_SEG_NONE;
141 }
142
143 /**
144 * Enqueues the TCP SACK Options Permitted option for transmission
145 *
146 * @param pcb the pcb connection which is used for transmission
147 * @param optflags the options to be sent while transmission
148 * @return void
149 *
150 */
151 void
tcp_enqueue_flags_sack(const struct tcp_pcb * pcb,u8_t * optflags)152 tcp_enqueue_flags_sack(const struct tcp_pcb *pcb, u8_t *optflags)
153 {
154 if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_SACK)) {
155 /* In a <SYN,ACK> (sent in state SYN_RCVD), the tcp sack option may only
156 * be sent if we received a tcp sack option from the remote host. */
157 (*optflags) |= TF_SEG_OPTS_SACK_PERMITTED;
158 }
159 }
160
161 /**
162 * Builds the SACK Options Permitted Option in SYN/ SYN-ACK message based on
163 * TF_SEG_OPTS_SACK_PERMITTED flag.
164 *
165 * @param opts options filled in the tcp header
166 * @return void
167 *
168 */
169 void
tcp_build_sack_permitted_option(u32_t * opts)170 tcp_build_sack_permitted_option(u32_t *opts)
171 {
172 /* RFC2018 Kind:04 Length:02 NOP:01 NOP:01 */
173 opts[0] = htonl(SACK_SYNC_PERMITTED_OPTION);
174 }
175
176 /**
177 * Calculates the number of SACK blocks allowed to be sent
178 *
179 * @param pcb the pcb which is used for transmission
180 * @param optlen the length of the TCP Options filled so far
181 * @return cnt the number of SACK Blocks allowed to be sent in the options
182 *
183 */
184 u8_t
tcp_get_sack_block_count_for_send(const struct tcp_pcb * pcb,u8_t optlen)185 tcp_get_sack_block_count_for_send(const struct tcp_pcb *pcb, u8_t optlen)
186 {
187 u8_t cnt = 0;
188 struct _sack_seq *ptr;
189 u8_t len;
190
191 len = BYTES_FOR_SACK_KIND_AND_TYPE + optlen;
192
193 ptr = pcb->sack_seq;
194
195 while (ptr != NULL) {
196 len = len + BYTES_FOR_OPTION_BLOCK;
197 if ((cnt < MAX_NUM_SACK_OPTS) && (len < MAX_TCPOPT_LEN)) {
198 cnt++;
199 ptr = ptr->next;
200 } else {
201 break;
202 }
203 }
204 return cnt;
205 }
206
207 /**
208 * Fills the TCP Options to be sent, with the SACK blocks received
209 * starting from the latest Out Of Sequence segment received
210 *
211 * @param pcb the pcb which is used for transmission
212 * @param cnt maximum number of SACK Blocks allowed to be filled in the options
213 * @param tcphdr Pointer to the TCP Header where the options are to be filled
214 * @return void
215 *
216 */
tcp_build_sack_option(struct tcp_pcb * pcb,u8_t cnt,u32_t * options)217 void tcp_build_sack_option(struct tcp_pcb *pcb, u8_t cnt, u32_t *options)
218 {
219 struct _sack_seq *ptr;
220 int index;
221 if (cnt > 0) {
222 options[0] = cnt > MAX_NUM_SACK_OPTS ? PP_HTONL(SACK_OPTIONS) :
223 PP_HTONL((SACK_OPTIONS | ((cnt * BYTES_FOR_OPTION_BLOCK) + BYTES_FOR_SACK_LENGTH_OPTION)));
224
225 #if LWIP_SACK_DATA_SEG_PIGGYBACK
226 for (index = 1, ptr = pcb->sack_seq; (index <= (cnt * U32_COUNT_ONE_OPTION_BLOCK)) && (ptr != NULL);
227 ptr = ptr->next)
228 #else
229 for (index = 1, ptr = pcb->sack_seq; (index <= (cnt * U32_COUNT_ONE_OPTION_BLOCK)); ptr = ptr->next)
230 #endif
231 {
232 options[index++] = htonl(ptr->left);
233 options[index++] = htonl(ptr->right);
234 }
235 #if LWIP_SACK_DATA_SEG_PIGGYBACK
236 /* reset the remaining space for sack options */
237 while ((index <= (cnt * U32_COUNT_ONE_OPTION_BLOCK))) {
238 options[index++] = 0;
239 options[index++] = 0;
240 }
241 #else
242 if (pcb->sack_seq != NULL) {
243 do {
244 ptr = pcb->sack_seq->next;
245 mem_free(pcb->sack_seq);
246 pcb->sack_seq = ptr;
247 }
248 while (pcb->sack_seq != NULL);
249 pcb->sack_seq = NULL;
250 }
251 #endif
252 }
253 }
254
255 /**
256 * Enables SACK feature in the connection PCB, if SACK permitted option
257 * is recieved
258 *
259 * Called from tcp_parseopt()
260 *
261 * @param pcb the tcp_pcb for which a segment arrived
262 */
263 void
tcp_parseopt_sack_permitted(struct tcp_pcb * pcb)264 tcp_parseopt_sack_permitted(struct tcp_pcb *pcb)
265 {
266 pcb->flags |= TF_SACK;
267 }
268
269 /**
270 * Initializes sack variables in PCB during connection
271 *
272 * Called from tcp_connect()
273 *
274 * @param pcb the tcp_pcb of the connection
275 */
276
tcp_connect_update_sack(struct tcp_pcb * pcb,u32_t iss)277 void tcp_connect_update_sack(struct tcp_pcb *pcb, u32_t iss)
278 {
279 pcb->sack_seq = NULL;
280 pcb->flags |= TF_SACK;
281 pcb->recovery_point = iss - 1;
282 pcb->pipe = 0;
283 pcb->high_sacked = iss - 1;
284 pcb->high_data = iss - 1;
285 pcb->high_rxt = iss - 1;
286 pcb->rescue_rxt = iss - 1;
287
288 #if LWIP_FACK_THRESHOLD_BASED_FR
289 pcb->fack = iss - 1;
290 #endif /* LWIP_FACK_THRESHOLD_BASED_FR */
291 #if LWIP_TCP_TLP_SUPPORT
292 LWIP_TCP_TLP_CLEAR_VARS(pcb);
293 #endif /* LWIP_TCP_TLP_SUPPORT */
294 }
295
296 /**
297 * Parses SACK option and updates the global variable g_sack_blocks
298 * and it will be used in tcp_receive
299 *
300 * Called from tcp_parseopt()
301 *
302 * @param pcb the tcp_pcb for which a segment arrived
303 */
304 u32_t
tcp_parseopt_sack(const u8_t * opts,u16_t c)305 tcp_parseopt_sack(const u8_t *opts, u16_t c)
306 {
307 u8_t l, i;
308 u32_t left_wnd_edge, right_wnd_edge;
309
310 for (l = opts[c + 1] - BYTES_FOR_SACK_LENGTH_OPTION,
311 c += U32_COUNT_ONE_OPTION_BLOCK, i = 0; l > 0;
312 l -= BYTES_FOR_OPTION_BLOCK, i++) {
313 if (memcpy_s(&left_wnd_edge, sizeof(left_wnd_edge), opts + c, sizeof(u32_t)) != EOK) {
314 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_parseopt_sack: error in memcpy_s\n"));
315 return 0;
316 }
317 left_wnd_edge = ntohl(left_wnd_edge);
318 c += sizeof(u32_t);
319
320 if (memcpy_s(&right_wnd_edge, sizeof(right_wnd_edge), opts + c, sizeof(u32_t)) != EOK) {
321 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_parseopt_sack: error in memcpy_s\n"));
322 return 0;
323 }
324
325 right_wnd_edge = ntohl(right_wnd_edge);
326 c += sizeof(u32_t);
327
328 g_sack_blocks[i].start_seq = left_wnd_edge;
329 g_sack_blocks[i].end_seq = right_wnd_edge;
330 }
331 return i;
332 }
333
334 /**
335 * This function checks whether received sack blocks are valid or not
336 *
337 * Called by tcp_sack_update()
338 *
339 * @param pcb the tcp_pcb of this connection
340 * @param start_seq the left edge of the sack block needs to be valided
341 * @param end_seq the right edge of the sack block needs to be valided
342 * @param ackno the cumulative ack received in this segment
343 *
344 * @return 1 if its valid sack block otherwise returns 0
345 */
346 static int
tcp_sack_is_sackblock_valid(const struct tcp_pcb * pcb,u32_t start_seq,u32_t end_seq,u32_t ackno)347 tcp_sack_is_sackblock_valid(const struct tcp_pcb *pcb, u32_t start_seq, u32_t end_seq, u32_t ackno)
348 {
349 /* start greated than end */
350 if TCP_SEQ_GEQ(start_seq, end_seq) {
351 return 0;
352 }
353
354 if (ackno < pcb->lastack) {
355 /* Need to consider lastack if ack received now is less than lastack */
356 ackno = pcb->lastack;
357 }
358
359 /* Check for finding valid sack block */
360 if ((TCP_SEQ_GT(start_seq, ackno)) && (TCP_SEQ_LEQ(end_seq, pcb->snd_nxt))) {
361 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_is_sackblock_valid: Valid SACK block received "
362 "(%"U32_F", %"U32_F"), ackno %"U32_F"\n", start_seq, end_seq, ackno));
363 return 1;
364 }
365
366 return 0;
367 }
368
369 /**
370 * Update () function implementation as per RFC 6675.
371 * This routine checks received SACK and updates its score board.
372 * Here state variable in tcp_pcb structure is used as score board.
373 *
374 * Called by tcp_receive()
375 *
376 * @param pcb the tcp_pcb of this connection
377 * @param ackno the cumulative ack received in this segment
378 *
379 * @return the number of unack segments SACKed in the new sack block received, otherwise returns 0
380 */
381 u32_t
tcp_sack_update(struct tcp_pcb * pcb,u32_t ackno)382 tcp_sack_update(struct tcp_pcb *pcb, u32_t ackno)
383 {
384 u32_t i;
385 struct tcp_seg *seg = NULL;
386 u32_t seqnum_lnt;
387 u32_t new_sack_blocks = 0;
388
389 for (i = 0; i < pcb->num_sacks; i++) {
390 if (tcp_sack_is_sackblock_valid(pcb, g_sack_blocks[i].start_seq, g_sack_blocks[i].end_seq, ackno)) {
391 for (seg = pcb->unacked; seg != NULL; seg = seg->next) {
392 if (TCP_SEQ_GEQ(ntohl(seg->tcphdr->seqno), g_sack_blocks[i].start_seq) &&
393 TCP_SEQ_LEQ(ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), g_sack_blocks[i].end_seq)) {
394 if ((i < MAX_NUM_SACK_BLOCK - 1) &&
395 (g_sack_blocks[i].start_seq > pcb->lastack) &&
396 (g_sack_blocks[i].start_seq > g_sack_blocks[i + 1].start_seq) &&
397 (g_sack_blocks[i].end_seq <= g_sack_blocks[i + 1].end_seq)) {
398 new_sack_blocks = 1;
399 }
400 if (!(seg->state & TF_SEG_SACKED)) {
401 seg->state |= TF_SEG_SACKED;
402 pcb->sacked++;
403 new_sack_blocks = 1;
404 seqnum_lnt = ntohl(seg->tcphdr->seqno) + (u32_t)(TCP_TCPLEN(seg));
405 /* If received SACK block is the highest received sack block then store it */
406 if (TCP_SEQ_GT(seqnum_lnt, pcb->high_sacked)) {
407 pcb->high_sacked = seqnum_lnt;
408 }
409 #if LWIP_SACK_PERF_OPT
410 if (TCP_SEQ_GT(seg->pkt_trans_seq_cntr, pcb->high_sacked_pkt_seq)) {
411 /* keep one higher than last sent */
412 pcb->high_sacked_pkt_seq = seg->pkt_trans_seq_cntr;
413 }
414 #endif /* LWIP_SACK_PERF_OPT */
415
416 #if LWIP_FACK_THRESHOLD_BASED_FR
417 if (TCP_SEQ_GT(seqnum_lnt, pcb->fack)) {
418 pcb->fack = seqnum_lnt;
419 }
420 #endif /* LWIP_FACK_THRESHOLD_BASED_FR */
421 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_update: New SACK block received "
422 "(%"U32_F", %"U32_F")\n", g_sack_blocks[i].start_seq, g_sack_blocks[i].end_seq));
423 }
424 }
425 }
426 }
427 }
428
429 if (new_sack_blocks) {
430 pcb->high_data = pcb->snd_nxt - 1;
431 }
432 return new_sack_blocks;
433 }
434
435 /**
436 * IsLost () function implementation as per RFC 6675.
437 * This routine checks whether the particular seg from unacked list is lost or not.
438 *
439 * Called by tcp_sack_set_pipe() and tcp_sack_get_next_seg().
440 *
441 * @param pcb the tcp_pcb of this connection
442 * @param seg the segment from unacked list which needs to be found whether
443 * its lost or not
444 *
445 * @return 1 on success and 0 on failure
446 */
447 int
tcp_sack_is_lost(const struct tcp_pcb * pcb,struct tcp_seg * seg)448 tcp_sack_is_lost(const struct tcp_pcb *pcb, struct tcp_seg *seg)
449 {
450 struct tcp_seg *lseg;
451 u32_t sacked_seg_len = 0;
452 u32_t discontiguous_blocks = 0;
453
454 if ((seg == NULL) || (seg->state & TF_SEG_SACKED)) {
455 /* If this seg is already sacked, then its not lost. so return failure */
456 return 0;
457 }
458
459 #if LWIP_FACK_THRESHOLD_BASED_FR
460 /* as per Forward ACK, the segment whose distance was 3 * MSS far more than the most forward
461 sequence (pcb->fack) should be deemed lost
462 */
463 if ((pcb->fack - (ntohl(seg->tcphdr->seqno) + (u32_t)(TCP_TCPLEN(seg)) - 1)) > (DUPACK_THRESH * pcb->mss)) {
464 return 1;
465 }
466 #endif /* LWIP_FACK_THRESHOLD_BASED_FR */
467
468 /* As per IsLost () definition in RFC 6675 */
469 for (lseg = seg->next; lseg != NULL; lseg = lseg->next) {
470 if (lseg->state & TF_SEG_SACKED) {
471 sacked_seg_len += TCP_TCPLEN(lseg);
472 discontiguous_blocks++;
473 if ((discontiguous_blocks == DUPACK_THRESH) || (sacked_seg_len > ((DUPACK_THRESH - 1) * pcb->mss))) {
474 return 1;
475 }
476 }
477 }
478
479 return 0;
480 }
481
482 /**
483 * SetPipe() function implementation as per RFC 6675.
484 * This routine traverses the sequence space from HighACK to HighData
485 * and MUST set the "pipe" variable to an estimate of the number of
486 * octets that are currently in transit between the TCP sender and
487 * the TCP receiver.
488 *
489 * Called by tcp_sack_based_fast_rexmit_alg() and tcp_receive().
490 *
491 * @param pcb the tcp_pcb of this connection
492 */
493 void
tcp_sack_set_pipe(struct tcp_pcb * pcb)494 tcp_sack_set_pipe(struct tcp_pcb *pcb)
495 {
496 struct tcp_seg *seg;
497 int seg_lost;
498
499 pcb->pipe = 0;
500
501 for (seg = pcb->unacked; seg != NULL; seg = seg->next) {
502 /* Pipe needs to be incremented for the retransmitted segments */
503 /* As per point b, in SetPipe() definition in Section 4 of RFC 6675 */
504 if (seg->state & TF_SEG_SACKED) {
505 continue;
506 }
507
508 if (seg->state & TF_SEG_RETRANSMITTED) {
509 pcb->pipe += TCP_TCPLEN(seg);
510 }
511
512 seg_lost = tcp_sack_is_lost(pcb, seg);
513 /* Pipe needs to be incremented for the segments which are not lost */
514 /* As per point a, in SetPipe() definition in Section 4 of RFC 6675 */
515 if (!seg_lost) {
516 pcb->pipe += TCP_TCPLEN(seg);
517 }
518 }
519
520 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_set_pipe: Pipe is %"U32_F"\n", pcb->pipe));
521 }
522
523 /* Smallest unsacked seg which meets below 3 criteria */
524 /* 1.a seg is greater than HighRxt */
525 /* 1.b seg is lesser than highest octet covered by any received SACK */
526 /* 1.c IsLost(2) returns true */
527 /* As per point 1, in NextSeg() of RFC 6675 */
528 static struct tcp_seg *
tcp_sack_next_if_unsacked_and_lost(struct tcp_pcb * pcb,u32_t next_seg_type)529 tcp_sack_next_if_unsacked_and_lost(struct tcp_pcb *pcb, u32_t next_seg_type)
530 {
531 struct tcp_seg *seg = NULL;
532 for (seg = pcb->next_seg_for_lr;
533 ((seg != NULL) && (seg->tcphdr != NULL) && (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), pcb->high_sacked)));
534 seg = seg->next) {
535 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_get_next_seg: UNSACKED_AND_LOST_SEG "
536 "check on seg %"U32_F"\n", (u32_t)ntohl(seg->tcphdr->seqno)));
537 /* 1.a and 1.c are done here. 1.b is already done in for loop */
538 if ((!(seg->state & TF_SEG_SACKED)) &&
539 TCP_SEQ_GT(ntohl(seg->tcphdr->seqno), pcb->high_rxt) && /* 1.a */
540 (tcp_sack_is_lost(pcb, seg))) { /* 1.c */
541 #if DRIVER_STATUS_CHECK
542 if (pcb->drv_status == DRV_NOT_READY) {
543 seg->seg_type = next_seg_type;
544 }
545 #else
546 (void)next_seg_type;
547 #endif
548 return seg;
549 }
550 }
551 return NULL;
552 }
553
554 /* Smallest unsacked seg which meets below 2 cirteria */
555 /* 1.a seg is greater than HighRxt */
556 /* 1.b seg is lesser than highest octet covered by any received SACK */
557 /* As per point 3, in NextSeg() of RFC 6675 */
558 static struct tcp_seg *
tcp_sack_next_if_unsacked(struct tcp_pcb * pcb,u32_t next_seg_type)559 tcp_sack_next_if_unsacked(struct tcp_pcb *pcb, u32_t next_seg_type)
560 {
561 struct tcp_seg *seg = NULL;
562 for (seg = pcb->next_seg_for_lr;
563 ((seg != NULL) && (seg->tcphdr != NULL) && (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), pcb->high_sacked)));
564 seg = seg->next) {
565 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_get_next_seg: UNSACKED_SEG "
566 "check on seg %"U32_F"\n", (u32_t)ntohl(seg->tcphdr->seqno)));
567 /* 1.a is done here. 1.b is already done in for loop */
568 if (((seg->state & TF_SEG_SACKED) == 0) &&
569 TCP_SEQ_GT(ntohl(seg->tcphdr->seqno), pcb->high_rxt)) {
570 #if DRIVER_STATUS_CHECK
571 if (pcb->drv_status == DRV_NOT_READY) {
572 seg->seg_type = next_seg_type;
573 }
574 #else
575 (void)next_seg_type;
576 #endif
577 return seg;
578 }
579 }
580 return NULL;
581 }
582
583 /* If HighACK > RescueRxt then return the seg */
584 /* As per point 4, in NextSeg() of RFC 6675 */
585 static struct tcp_seg *
tcp_sack_next_if_rescue(struct tcp_pcb * pcb,u32_t next_seg_type)586 tcp_sack_next_if_rescue(struct tcp_pcb *pcb, u32_t next_seg_type)
587 {
588 struct tcp_seg *seg = NULL;
589 if (TCP_SEQ_GT(pcb->lastack, pcb->rescue_rxt)) {
590 for (seg = pcb->next_seg_for_lr; seg != NULL; seg = seg->next) {
591 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_get_next_seg: RESCUE_RX_SEG "
592 "check on seg %"U32_F"\n", (u32_t)ntohl(seg->tcphdr->seqno)));
593 if (((seg->state & TF_SEG_SACKED) == 0) && ((seg->state & TF_SEG_RETRANSMITTED) == 0)) {
594 /* Then set RescueRxt to Recovery point */
595 pcb->rescue_rxt = pcb->recovery_point;
596 #if DRIVER_STATUS_CHECK
597 if (pcb->drv_status == DRV_NOT_READY) {
598 seg->seg_type = next_seg_type;
599 }
600 #else
601 (void)next_seg_type;
602 #endif
603 return seg;
604 }
605 }
606 }
607 return NULL;
608 }
609 /**
610 * NextSeg() function implementation as per RFC 6675.
611 * This routine finds the next segment that can be retransmitted
612 * during loss recovery phase based on SACK and pipe.
613 *
614 * Called by tcp_sack_based_loss_recovery_alg().
615 *
616 * @param pcb the tcp_pcb of this connection
617 * @param next_seg_typ the type of next segment which needs to be found
618 *
619 * @result returns valid segment if its found or it returns NULL
620 */
621 struct tcp_seg *
tcp_sack_get_next_seg(struct tcp_pcb * pcb,u32_t next_seg_type)622 tcp_sack_get_next_seg(struct tcp_pcb *pcb, u32_t next_seg_type)
623 {
624 switch (next_seg_type) {
625 case UNSACKED_AND_LOST_SEG:
626 return tcp_sack_next_if_unsacked_and_lost(pcb, next_seg_type);
627 case UNSACKED_SEG:
628 return tcp_sack_next_if_unsacked(pcb, next_seg_type);
629 case RESCUE_RX_SEG:
630 return tcp_sack_next_if_rescue(pcb, next_seg_type);
631 default:
632 return NULL;
633 }
634 }
635
636 #if LWIP_SACK_PERF_OPT
tcp_sack_rexmit_lost_rexmitted(struct tcp_pcb * pcb)637 void tcp_sack_rexmit_lost_rexmitted(struct tcp_pcb *pcb)
638 {
639 struct tcp_seg *seg = NULL;
640 struct tcp_sack_fast_rxmited *fr_seg = NULL;
641
642 for (fr_seg = pcb->fr_segs; fr_seg != NULL; fr_seg = fr_seg->next) {
643 seg = fr_seg->seg;
644 if ((seg != NULL) && (!(seg->state & TF_SEG_SACKED)) && (seg->state & TF_SEG_RETRANSMITTED) &&
645 (TCP_SEQ_GEQ(pcb->high_sacked_pkt_seq, seg->pkt_trans_seq_cntr + 1))) {
646 (void)tcp_output_segment(seg, pcb, NULL);
647 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_rexmit_lost_rexmitted: Retransmitted a "
648 "possibly lost retranmsitted seg %"U32_F" of length %"U32_F" \n",
649 ntohl(seg->tcphdr->seqno), TCP_TCPLEN(seg)));
650
651 seg->state |= TF_SEG_RETRANSMITTED;
652
653 /* Pipe needs to be incremented for the retransmitted seg
654 * As per point C.4 in Section 5 of RFC 6675
655 * No need to change pipe, since it was not considered as lost,
656 * No change in calucation as it is retransmitted again
657 * FRLR state, so no need to increment nrtx
658 * Don't take any rtt measurements after retransmitting. */
659 pcb->rttest = 0;
660 }
661 }
662
663 return;
664 }
665 #endif
666
667 /**
668 * Implemntation of loss recovery algorithm as per RFC 6675.
669 *
670 * Called by tcp_receive().
671 *
672 * @param pcb the tcp_pcb of this connection
673 */
674 void
tcp_sack_based_loss_recovery_alg(struct tcp_pcb * pcb)675 tcp_sack_based_loss_recovery_alg(struct tcp_pcb *pcb)
676 {
677 struct tcp_seg *seg;
678 u32_t next_seg_type;
679 #if LWIP_SACK_PERF_OPT
680 struct tcp_sack_fast_rxmited *fast_retx = NULL;
681 #endif
682
683 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: Entered SACK "
684 "based loss recovery phase\n"));
685
686 #if LWIP_SACK_PERF_OPT
687 tcp_sack_rexmit_lost_rexmitted(pcb);
688 #endif
689
690 /* Move the next_seg_for_lr to point first unsacked and not retransmitted seg */
691 for (seg = pcb->next_seg_for_lr; seg != NULL; seg = seg->next) {
692 if (!(seg->state & TF_SEG_SACKED)) {
693 break;
694 }
695 }
696
697 pcb->next_seg_for_lr = seg;
698
699 /* If nothing is there, then no need to do Loss recovery */
700 if (!pcb->next_seg_for_lr) {
701 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: No seg "
702 "available for loss recovery\n"));
703 return;
704 }
705
706 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: Next seg for "
707 "loss recovery is %"U32_F"\n", ntohl(pcb->next_seg_for_lr->tcphdr->seqno)));
708
709 while ((pcb->cwnd >= pcb->pipe) && ((pcb->cwnd - pcb->pipe) >= pcb->mss)) {
710 next_seg_type = UNSACKED_AND_LOST_SEG;
711 do {
712 switch (next_seg_type) {
713 case UNSACKED_AND_LOST_SEG:
714 /* Try to find unsacked and lost seg */
715 seg = tcp_sack_get_next_seg(pcb, UNSACKED_AND_LOST_SEG);
716 if (seg) {
717 #if DRIVER_STATUS_CHECK
718 /* Send the segment out only if the driver is ready */
719 if (pcb->drv_status == DRV_READY)
720 #endif
721 {
722 (void)tcp_output_segment(seg, pcb, NULL);
723 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: Retransmitted a "
724 "seg %"U32_F" of length %"U32_F" which is UNSACKED_AND_LOST_SEG\n",
725 ntohl(seg->tcphdr->seqno), TCP_TCPLEN(seg)));
726 }
727 #if DRIVER_STATUS_CHECK
728 else {
729 LWIP_DEBUGF(DRV_STS_DEBUG, ("tcp_sack_based_loss_recovery_alg: Driver busy for "
730 "seg %"U32_F" of length %"U32_F" which is UNSACKED_AND_LOST_SEG\n",
731 ntohl(seg->tcphdr->seqno), TCP_TCPLEN(seg)));
732 }
733 #endif
734 } else {
735 next_seg_type = UNSENT_SEG;
736 }
737 break;
738 case UNSENT_SEG:
739 if (pcb->unsent) {
740 seg = pcb->unsent;
741 /* If unsent data available and receiver's advertise window allows */
742 /* then return that segment */
743 /* As per point 2, in NextSeg() of RFC 6675 */
744 (void)tcp_output(pcb);
745
746 if (TCP_SEQ_GT(pcb->snd_nxt, pcb->high_data + 1)) {
747 /* If any of the data octets send are above HighData */
748 /* HighData must be updated */
749 /* As per point C.3 in Section 5 of RFC 6675 */
750 pcb->high_data = pcb->snd_nxt - 1;
751 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: Transmitted an "
752 "UNSENT_SEG, send next is %"U32_F"\n", pcb->snd_nxt));
753 } else {
754 /* tcp_output didn't send any new seg */
755 seg = NULL;
756 next_seg_type = UNSACKED_SEG;
757 }
758 } else {
759 seg = NULL;
760 next_seg_type = UNSACKED_SEG;
761 }
762 break;
763 case UNSACKED_SEG:
764 seg = tcp_sack_get_next_seg(pcb, UNSACKED_SEG);
765 if (seg) {
766 #if DRIVER_STATUS_CHECK
767 /* Send the segment out only if the driver is ready */
768 if (pcb->drv_status == DRV_READY)
769 #endif
770 {
771 (void)tcp_output_segment(seg, pcb, NULL);
772 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: Retransmitted a "
773 "seg %"U32_F" of length %"U32_F" which is UNSACKED_SEG\n",
774 ntohl(seg->tcphdr->seqno), TCP_TCPLEN(seg)));
775 }
776 #if DRIVER_STATUS_CHECK
777 else {
778 LWIP_DEBUGF(DRV_STS_DEBUG, ("tcp_sack_based_loss_recovery_alg: Driver busy for "
779 "seg %"U32_F" of length %"U32_F" which is UNSACKED_SEG\n",
780 ntohl(seg->tcphdr->seqno), TCP_TCPLEN(seg)));
781 }
782 #endif
783 } else {
784 next_seg_type = RESCUE_RX_SEG;
785 }
786 break;
787 case RESCUE_RX_SEG:
788 seg = tcp_sack_get_next_seg(pcb, RESCUE_RX_SEG);
789 if (seg) {
790 #if DRIVER_STATUS_CHECK
791 /* Send the segment out only if the driver is ready */
792 if (pcb->drv_status == DRV_READY)
793 #endif
794 {
795 (void)tcp_output_segment(seg, pcb, NULL);
796 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: Retransmitted a "
797 "seg %"U32_F" of length %"U32_F" which is RESCUE_RX_SEG\n",
798 ntohl(seg->tcphdr->seqno), TCP_TCPLEN(seg)));
799 }
800 #if DRIVER_STATUS_CHECK
801 else {
802 LWIP_DEBUGF(DRV_STS_DEBUG, ("tcp_sack_based_loss_recovery_alg: Driver busy for "
803 "seg %"U32_F" of length %"U32_F" which is RESCUE_RX_SEG\n",
804 ntohl(seg->tcphdr->seqno), TCP_TCPLEN(seg)));
805 }
806 #endif
807 } else {
808 next_seg_type = 0;
809 }
810 break;
811 default:
812 break;
813 }
814 } while ((seg == NULL) && (next_seg_type != 0));
815
816 /* If no segment has been retransmitted, then return from here */
817 if (!seg) {
818 break;
819 }
820
821 /* If any of the data octets send are below HighData */
822 /* HighRxt needs to be set to the highest seq no of the retransmitted seg */
823 /* As per point C.2 in Section 5 of RFC 6675 */
824 if ((next_seg_type == UNSACKED_AND_LOST_SEG)
825 || (next_seg_type == UNSACKED_SEG)
826 || (next_seg_type == RESCUE_RX_SEG)) {
827 pcb->high_rxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg) - 1;
828 pcb->next_seg_for_lr = seg->next;
829 /* Enabling TF_SEG_RETRANSMITTED */
830 /* This will be required to count pipe */
831 seg->state |= TF_SEG_RETRANSMITTED;
832
833 #if LWIP_SACK_PERF_OPT
834 fast_retx = (struct tcp_sack_fast_rxmited *)mem_malloc(sizeof(struct tcp_sack_fast_rxmited));
835 if (fast_retx != NULL) {
836 fast_retx->seg = seg;
837 fast_retx->next = NULL;
838 /* If no segments were fast retransmitted previously, then last_frseg will be NULL */
839 if (pcb->last_frseg == NULL) {
840 pcb->fr_segs = fast_retx;
841 /* Update last_frseg to this segment */
842 pcb->last_frseg = pcb->fr_segs;
843 } else {
844 /* Append the segment to the latest fast retransmitted segment */
845 pcb->last_frseg->next = fast_retx;
846 pcb->last_frseg = pcb->last_frseg->next;
847 }
848 } else {
849 LWIP_DEBUGF(TCP_SACK_DEBUG, ("Memory allocation failure for fr_segs\n"));
850 }
851 #endif
852
853 /* Pipe needs to be incremented for the retransmitted seg */
854 /* As per point C.4 in Section 5 of RFC 6675 */
855 pcb->pipe += (u32_t)(TCP_TCPLEN(seg));
856
857 /* Don't take any rtt measurements after retransmitting. */
858 pcb->rttest = 0;
859 }
860
861 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: Pipe is %"U32_F
862 " RecoveryPoint is %"U32_F" HighSacked is %"U32_F" HighData is %"U32_F
863 " HighRxt is %"U32_F" RescueRxt is %"U32_F"\n", pcb->pipe, pcb->recovery_point,
864 pcb->high_sacked, pcb->high_data, pcb->high_rxt, pcb->rescue_rxt));
865 }
866
867 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_loss_recovery_alg: Leaving SACK "
868 "based loss recovery phase\n"));
869 }
870
871 /**
872 * Initiates fast retransmit as per point 4 in Section 5 of RFC 6675
873 * It initialize recovery_point and enters fast retransmit.
874 *
875 * Called by tcp_receive() for fast retramsmit in case of SACK.
876 *
877 * @param pcb the tcp_pcb for which to retransmit the first unacked segment
878 */
879 void
tcp_sack_based_fast_rexmit_alg(struct tcp_pcb * pcb)880 tcp_sack_based_fast_rexmit_alg(struct tcp_pcb *pcb)
881 {
882 tcpwnd_size_t ssthresh;
883
884 /* If it entered to retrasnmit timeout from loss recovery, then it should not enter again to loss recovery phase */
885 /* untill we receive a cumulative ack for recovery_point */
886 if ((!(pcb->flags & TF_IN_SACK_FRLR)) && (!(pcb->flags & TF_IN_SACK_RTO))) {
887 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_fast_rexmit_alg: Started SACK based "
888 "fast retransmit\n"));
889 /* Assigning HighData to recovery_point, If cumulative ACK is received */
890 /* for this recovery_point, then that shoud be the end of loss recovery phase */
891 /* As per point 4.1 in section 5 of RFC 6675 */
892 pcb->recovery_point = pcb->high_data;
893
894 /* As per point 4.2 in section 5 of RFC 6675 */
895 /* Set ssthresh to half of the minimum of the current
896 * cwnd and the advertised window */
897
898 #if LWIP_SACK_CWND_OPT
899 pcb->recover_cwnd = pcb->cwnd;
900 pcb->recover_ssthresh = pcb->ssthresh;
901 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_fast_rexmit_alg: recover_cwnd is %"TCPWNDSIZE_F""
902 "and ssthresh is %"TCPWNDSIZE_F"\n", pcb->cwnd, pcb->ssthresh));
903 #endif
904 if (pcb->cwnd > pcb->snd_wnd) {
905 pcb->ssthresh = (tcpwnd_size_t)(((u64_t)pcb->snd_wnd * 7) >> 3);
906 } else {
907 pcb->ssthresh = (tcpwnd_size_t)(((u64_t)pcb->cwnd * 7) >> 3);
908 }
909
910 /* The minimum value for ssthresh should be 2 MSS */
911 if (pcb->ssthresh < 2 * pcb->mss) {
912 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_fast_rexmit_alg: The minimum "
913 "value for ssthresh %"TCPWNDSIZE_F" should be min 2 mss %"TCPWNDSIZE_F"\n",
914 pcb->ssthresh, 2 * pcb->mss));
915 pcb->ssthresh = 2 * pcb->mss;
916 }
917
918 pcb->cwnd = pcb->ssthresh;
919 if (pcb->cwnd < pcb->iw) {
920 pcb->cwnd = pcb->iw;
921 }
922
923 TCP_CALC_SSTHRESH(pcb, ssthresh, INITIAL_SSTHRESH, pcb->mss);
924 if (pcb->ssthresh < ssthresh) {
925 pcb->ssthresh = ssthresh;
926 }
927
928 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_fast_rexmit_alg: cwnd is %"TCPWNDSIZE_F
929 " ssthresh is %"TCPWNDSIZE_F"\n", pcb->cwnd, pcb->ssthresh));
930
931 /* Retransmit the first data segment, starting with sequence number HighACK + 1 */
932 /* As per point 4.3 in section 5 of RFC 6675 */
933 #if DRIVER_STATUS_CHECK
934 /* Send the segment out only if the driver is ready */
935 if (pcb->drv_status == DRV_READY) {
936 (void)tcp_output_segment(pcb->unacked, pcb, NULL);
937 } else {
938 pcb->unacked->seg_type = FAST_RETX_SEG;
939 LWIP_DEBUGF(DRV_STS_DEBUG, ("tcp_sack_based_fast_rexmit_alg: Driver busy so delaying.\n"));
940 }
941 #else
942 (void)tcp_output_segment(pcb->unacked, pcb, NULL);
943 #endif
944
945 /* Set both HighRxt and RescueRxt to the highest sequence number in the retransmitted segment. */
946 /* As per point 4.3 in Section 5 of RFC 6675. */
947 pcb->high_rxt = ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked) - 1;
948 pcb->rescue_rxt = pcb->high_rxt;
949 pcb->next_seg_for_lr = pcb->unacked->next;
950
951 /* Enabling TF_SEG_RETRANSMITTED */
952 /* This will be required to count pipe */
953 pcb->unacked->state |= TF_SEG_RETRANSMITTED;
954
955 #if LWIP_SACK_PERF_OPT
956 if (pcb->fr_segs != NULL) {
957 /* Free if an segments are stored in the fr_segs. Ideally there
958 should be no segments at this instance */
959 tcp_fr_segs_free(pcb->fr_segs);
960 }
961
962 pcb->fr_segs = (struct tcp_sack_fast_rxmited *)mem_malloc(sizeof(struct tcp_sack_fast_rxmited));
963 if (pcb->fr_segs != NULL) {
964 pcb->fr_segs->seg = pcb->unacked;
965 pcb->fr_segs->next = NULL;
966 pcb->last_frseg = pcb->fr_segs;
967 } else {
968 LWIP_DEBUGF(TCP_SACK_DEBUG, ("Memory allocation failure for fr_segs\n"));
969 }
970 #endif
971
972 /* Similar to tcp_rexmit() function */
973 ++pcb->nrtx;
974
975 /* Don't take any rtt measurements after retransmitting. */
976 pcb->rttest = 0;
977
978 LWIP_DEBUGF(TCP_SACK_DEBUG, ("tcp_sack_based_fast_rexmit_alg: Pipe is %"U32_F
979 " RecoveryPoint is %"U32_F" HighSacked is %"U32_F" HighData is %"U32_F
980 " HighRxt is %"U32_F" RescueRxt is %"U32_F"\n", pcb->pipe, pcb->recovery_point,
981 pcb->high_sacked, pcb->high_data, pcb->high_rxt, pcb->rescue_rxt));
982
983 pcb->flags |= TF_IN_SACK_FRLR;
984 }
985 }
986
987 #endif
988