• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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