]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/TcpDxe/TcpOutput.c
NetworkPkg: Addressing TCP Window Retraction when window scale factor is used.
[mirror_edk2.git] / NetworkPkg / TcpDxe / TcpOutput.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 TCP output process routines.\r
3\r
3696ceae 4 Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>\r
a3bcde70
HT
5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php.\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "TcpMain.h"\r
17\r
18UINT8 mTcpOutFlag[] = {\r
19 0, // TCP_CLOSED\r
20 0, // TCP_LISTEN\r
21 TCP_FLG_SYN, // TCP_SYN_SENT\r
22 TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD\r
23 TCP_FLG_ACK, // TCP_ESTABLISHED\r
24 TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1\r
25 TCP_FLG_ACK, // TCP_FIN_WAIT_2\r
26 TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING\r
27 TCP_FLG_ACK, // TCP_TIME_WAIT\r
28 TCP_FLG_ACK, // TCP_CLOSE_WAIT\r
29 TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK\r
30};\r
31\r
32/**\r
33 Compute the sequence space left in the old receive window.\r
34\r
35 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
36\r
37 @return The sequence space left in the old receive window.\r
38\r
39**/\r
40UINT32\r
41TcpRcvWinOld (\r
42 IN TCP_CB *Tcb\r
43 )\r
44{\r
45 UINT32 OldWin;\r
46\r
47 OldWin = 0;\r
48\r
49 if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {\r
50\r
51 OldWin = TCP_SUB_SEQ (\r
52 Tcb->RcvWl2 + Tcb->RcvWnd,\r
53 Tcb->RcvNxt\r
54 );\r
55 }\r
56\r
57 return OldWin;\r
58}\r
59\r
60/**\r
61 Compute the current receive window.\r
62\r
63 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
64\r
65 @return The size of the current receive window, in bytes.\r
66\r
67**/\r
68UINT32\r
69TcpRcvWinNow (\r
70 IN TCP_CB *Tcb\r
71 )\r
72{\r
73 SOCKET *Sk;\r
74 UINT32 Win;\r
75 UINT32 Increase;\r
76 UINT32 OldWin;\r
77\r
78 Sk = Tcb->Sk;\r
79 ASSERT (Sk != NULL);\r
80\r
81 OldWin = TcpRcvWinOld (Tcb);\r
82\r
83 Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF);\r
84\r
85 Increase = 0;\r
86 if (Win > OldWin) {\r
87 Increase = Win - OldWin;\r
88 }\r
89\r
90 //\r
91 // Receiver's SWS: don't advertise a bigger window\r
92 // unless it can be increased by at least one Mss or\r
93 // half of the receive buffer.\r
94 //\r
95 if ((Increase > Tcb->SndMss) || (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {\r
96\r
97 return Win;\r
98 }\r
99\r
100 return OldWin;\r
101}\r
102\r
103/**\r
104 Compute the value to fill in the window size field of the outgoing segment.\r
105\r
106 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
107 @param[in] Syn The flag to indicate whether the outgoing segment\r
108 is a SYN segment.\r
109\r
110 @return The value of the local receive window size used to fill the outgoing segment.\r
111\r
112**/\r
113UINT16\r
114TcpComputeWnd (\r
115 IN OUT TCP_CB *Tcb,\r
116 IN BOOLEAN Syn\r
117 )\r
118{\r
119 UINT32 Wnd;\r
120\r
121 //\r
122 // RFC requires that initial window not be scaled\r
123 //\r
124 if (Syn) {\r
125\r
126 Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
127 } else {\r
128\r
129 Wnd = TcpRcvWinNow (Tcb);\r
130\r
131 Tcb->RcvWnd = Wnd;\r
132 }\r
133\r
134 Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff);\r
135 return NTOHS ((UINT16) Wnd);\r
136}\r
137\r
138/**\r
139 Get the maximum SndNxt.\r
140\r
141 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
142\r
143 @return The sequence number of the maximum SndNxt.\r
144\r
145**/\r
146TCP_SEQNO\r
147TcpGetMaxSndNxt (\r
148 IN TCP_CB *Tcb\r
149 )\r
150{\r
151 LIST_ENTRY *Entry;\r
152 NET_BUF *Nbuf;\r
153\r
154 if (IsListEmpty (&Tcb->SndQue)) {\r
155 return Tcb->SndNxt;\r
156 }\r
157\r
158 Entry = Tcb->SndQue.BackLink;\r
159 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
160\r
161 ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));\r
162 return TCPSEG_NETBUF (Nbuf)->End;\r
163}\r
164\r
165/**\r
166 Compute how much data to send.\r
167\r
168 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
169 @param[in] Force If TRUE, to ignore the sender's SWS avoidance algorithm and send\r
170 out data by force.\r
171\r
172 @return The length of the data can be sent. If 0, no data can be sent.\r
173\r
174**/\r
175UINT32\r
176TcpDataToSend (\r
177 IN TCP_CB *Tcb,\r
178 IN INTN Force\r
179 )\r
180{\r
181 SOCKET *Sk;\r
182 UINT32 Win;\r
183 UINT32 Len;\r
184 UINT32 Left;\r
185 UINT32 Limit;\r
186\r
187 Sk = Tcb->Sk;\r
188 ASSERT (Sk != NULL);\r
189\r
190 //\r
191 // TCP should NOT send data beyond the send window\r
192 // and congestion window. The right edge of send\r
193 // window is defined as SND.WL2 + SND.WND. The right\r
194 // edge of congestion window is defined as SND.UNA +\r
195 // CWND.\r
196 //\r
197 Win = 0;\r
198 Limit = Tcb->SndWl2 + Tcb->SndWnd;\r
199\r
200 if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {\r
201\r
202 Limit = Tcb->SndUna + Tcb->CWnd;\r
203 }\r
204\r
205 if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {\r
206 Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);\r
207 }\r
208\r
209 //\r
210 // The data to send contains two parts: the data on the\r
211 // socket send queue, and the data on the TCB's send\r
212 // buffer. The later can be non-zero if the peer shrinks\r
213 // its advertised window.\r
214 //\r
215 Left = GET_SND_DATASIZE (Sk) + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);\r
216\r
217 Len = MIN (Win, Left);\r
218\r
219 if (Len > Tcb->SndMss) {\r
220 Len = Tcb->SndMss;\r
221 }\r
222\r
223 if ((Force != 0)|| (Len == 0 && Left == 0)) {\r
224 return Len;\r
225 }\r
226\r
227 if (Len == 0 && Left != 0) {\r
228 goto SetPersistTimer;\r
229 }\r
230\r
231 //\r
232 // Sender's SWS avoidance: Don't send a small segment unless\r
233 // a)A full-sized segment can be sent,\r
234 // b)At least one-half of the maximum sized windows that\r
235 // the other end has ever advertised.\r
236 // c)It can send everything it has, and either it isn't\r
237 // expecting an ACK, or the Nagle algorithm is disabled.\r
238 //\r
239 if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {\r
240\r
241 return Len;\r
242 }\r
243\r
244 if ((Len == Left) &&\r
245 ((Tcb->SndNxt == Tcb->SndUna) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))\r
246 ) {\r
247\r
248 return Len;\r
249 }\r
250\r
251 //\r
252 // RFC1122 suggests to set a timer when SWSA forbids TCP\r
253 // sending more data, and combines it with a probe timer.\r
254 //\r
255SetPersistTimer:\r
256 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
257\r
258 DEBUG (\r
259 (EFI_D_WARN,\r
260 "TcpDataToSend: enter persistent state for TCB %p\n",\r
261 Tcb)\r
262 );\r
263\r
264 if (!Tcb->ProbeTimerOn) {\r
265 TcpSetProbeTimer (Tcb);\r
266 }\r
267 }\r
268\r
269 return 0;\r
270}\r
271\r
272/**\r
273 Build the TCP header of the TCP segment and transmit the segment by IP.\r
274\r
275 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
276 @param[in] Nbuf Pointer to the buffer containing the segment to be\r
277 sent out.\r
278\r
279 @retval 0 The segment was sent out successfully.\r
280 @retval -1 An error condition occurred.\r
281\r
282**/\r
283INTN\r
284TcpTransmitSegment (\r
285 IN OUT TCP_CB *Tcb,\r
286 IN NET_BUF *Nbuf\r
287 )\r
288{\r
289 UINT16 Len;\r
290 TCP_HEAD *Head;\r
291 TCP_SEG *Seg;\r
292 BOOLEAN Syn;\r
293 UINT32 DataLen;\r
294\r
295 ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0));\r
296\r
297 DataLen = Nbuf->TotalSize;\r
298\r
299 Seg = TCPSEG_NETBUF (Nbuf);\r
300 Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);\r
301\r
302 if (Syn) {\r
303\r
304 Len = TcpSynBuildOption (Tcb, Nbuf);\r
305 } else {\r
306\r
307 Len = TcpBuildOption (Tcb, Nbuf);\r
308 }\r
309\r
310 ASSERT ((Len % 4 == 0) && (Len <= 40));\r
311\r
312 Len += sizeof (TCP_HEAD);\r
313\r
314 Head = (TCP_HEAD *) NetbufAllocSpace (\r
315 Nbuf,\r
316 sizeof (TCP_HEAD),\r
317 NET_BUF_HEAD\r
318 );\r
319\r
320 ASSERT (Head != NULL);\r
321\r
322 Nbuf->Tcp = Head;\r
323\r
324 Head->SrcPort = Tcb->LocalEnd.Port;\r
325 Head->DstPort = Tcb->RemoteEnd.Port;\r
326 Head->Seq = NTOHL (Seg->Seq);\r
327 Head->Ack = NTOHL (Tcb->RcvNxt);\r
328 Head->HeadLen = (UINT8) (Len >> 2);\r
329 Head->Res = 0;\r
330 Head->Wnd = TcpComputeWnd (Tcb, Syn);\r
331 Head->Checksum = 0;\r
332\r
333 //\r
334 // Check whether to set the PSH flag.\r
335 //\r
336 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);\r
337\r
338 if (DataLen != 0) {\r
339 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&\r
340 TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)\r
341 ) {\r
342\r
343 TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);\r
344 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);\r
345\r
346 } else if ((Seg->End == Tcb->SndNxt) && (GET_SND_DATASIZE (Tcb->Sk) == 0)) {\r
347\r
348 TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);\r
349 }\r
350 }\r
351\r
352 //\r
353 // Check whether to set the URG flag and the urgent pointer.\r
354 //\r
355 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);\r
356\r
357 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {\r
358\r
359 TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);\r
360\r
361 if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {\r
362\r
363 Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);\r
364 } else {\r
365\r
366 Seg->Urg = (UINT16) MIN (\r
367 TCP_SUB_SEQ (Tcb->SndUp,\r
368 Seg->Seq),\r
369 0xffff\r
370 );\r
371 }\r
372 }\r
373\r
374 Head->Flag = Seg->Flag;\r
375 Head->Urg = NTOHS (Seg->Urg);\r
376 Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
377\r
378 //\r
379 // Update the TCP session's control information.\r
380 //\r
381 Tcb->RcvWl2 = Tcb->RcvNxt;\r
382 if (Syn) {\r
383 Tcb->RcvWnd = NTOHS (Head->Wnd);\r
384 }\r
385\r
386 //\r
387 // Clear the delayedack flag.\r
388 //\r
389 Tcb->DelayedAck = 0;\r
390\r
391 return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion);\r
392}\r
393\r
394/**\r
395 Get a segment from the Tcb's SndQue.\r
396\r
397 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
398 @param[in] Seq The sequence number of the segment.\r
399 @param[in] Len The maximum length of the segment.\r
400\r
401 @return Pointer to the segment. If NULL, some error occurred.\r
402\r
403**/\r
404NET_BUF *\r
405TcpGetSegmentSndQue (\r
406 IN TCP_CB *Tcb,\r
407 IN TCP_SEQNO Seq,\r
408 IN UINT32 Len\r
409 )\r
410{\r
411 LIST_ENTRY *Head;\r
412 LIST_ENTRY *Cur;\r
413 NET_BUF *Node;\r
414 TCP_SEG *Seg;\r
415 NET_BUF *Nbuf;\r
416 TCP_SEQNO End;\r
417 UINT8 *Data;\r
418 UINT8 Flag;\r
419 INT32 Offset;\r
420 INT32 CopyLen;\r
421\r
422 ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));\r
423\r
424 //\r
425 // Find the segment that contains the Seq.\r
426 //\r
427 Head = &Tcb->SndQue;\r
428\r
429 Node = NULL;\r
430 Seg = NULL;\r
431\r
432 NET_LIST_FOR_EACH (Cur, Head) {\r
433 Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
434 Seg = TCPSEG_NETBUF (Node);\r
435\r
436 if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {\r
437\r
438 break;\r
439 }\r
440 }\r
441\r
442 if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) {\r
443 return NULL;\r
444 }\r
445\r
446 //\r
447 // Return the buffer if it can be returned without\r
448 // adjustment:\r
449 //\r
450 if ((Seg->Seq == Seq) &&\r
451 TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&\r
452 !NET_BUF_SHARED (Node)\r
453 ) {\r
454\r
455 NET_GET_REF (Node);\r
456 return Node;\r
457 }\r
458\r
459 //\r
460 // Create a new buffer and copy data there.\r
461 //\r
462 Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
463\r
464 if (Nbuf == NULL) {\r
465 return NULL;\r
466 }\r
467\r
468 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
469\r
470 Flag = Seg->Flag;\r
471 End = Seg->End;\r
472\r
473 if (TCP_SEQ_LT (Seq + Len, Seg->End)) {\r
474 End = Seq + Len;\r
475 }\r
476\r
477 CopyLen = TCP_SUB_SEQ (End, Seq);\r
478 Offset = TCP_SUB_SEQ (Seq, Seg->Seq);\r
479\r
480 //\r
481 // If SYN is set and out of the range, clear the flag.\r
482 // Becuase the sequence of the first byte is SEG.SEQ+1,\r
483 // adjust Offset by -1. If SYN is in the range, copy\r
484 // one byte less.\r
485 //\r
486 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
487\r
488 if (TCP_SEQ_LT (Seg->Seq, Seq)) {\r
489\r
490 TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);\r
491 Offset--;\r
492 } else {\r
493\r
494 CopyLen--;\r
495 }\r
496 }\r
497\r
498 //\r
499 // If FIN is set and in the range, copy one byte less,\r
500 // and if it is out of the range, clear the flag.\r
501 //\r
502 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
503\r
504 if (Seg->End == End) {\r
505\r
506 CopyLen--;\r
507 } else {\r
508\r
509 TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
510 }\r
511 }\r
512\r
513 ASSERT (CopyLen >= 0);\r
514\r
515 //\r
516 // Copy data to the segment\r
517 //\r
518 if (CopyLen != 0) {\r
519 Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);\r
520 ASSERT (Data != NULL);\r
521\r
522 if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {\r
523 goto OnError;\r
524 }\r
525 }\r
526\r
527 CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));\r
528\r
529 TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
530 TCPSEG_NETBUF (Nbuf)->End = End;\r
531 TCPSEG_NETBUF (Nbuf)->Flag = Flag;\r
532\r
533 return Nbuf;\r
534\r
535OnError:\r
536 NetbufFree (Nbuf);\r
537 return NULL;\r
538}\r
539\r
540/**\r
541 Get a segment from the Tcb's socket buffer.\r
542\r
543 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
544 @param[in] Seq The sequence number of the segment.\r
545 @param[in] Len The maximum length of the segment.\r
546\r
547 @return Pointer to the segment. If NULL, some error occurred.\r
548\r
549**/\r
550NET_BUF *\r
551TcpGetSegmentSock (\r
552 IN TCP_CB *Tcb,\r
553 IN TCP_SEQNO Seq,\r
554 IN UINT32 Len\r
555 )\r
556{\r
557 NET_BUF *Nbuf;\r
558 UINT8 *Data;\r
559 UINT32 DataGet;\r
560\r
561 ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
562\r
563 Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
564\r
565 if (Nbuf == NULL) {\r
566 DEBUG (\r
567 (EFI_D_ERROR,\r
568 "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n",\r
569 Tcb)\r
570 );\r
571\r
572 return NULL;\r
573 }\r
574\r
575 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
576\r
577 DataGet = 0;\r
578\r
579 if (Len != 0) {\r
580 //\r
581 // copy data to the segment.\r
582 //\r
583 Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);\r
584 ASSERT (Data != NULL);\r
585\r
586 DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);\r
587 }\r
588\r
589 NET_GET_REF (Nbuf);\r
590\r
591 TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
592 TCPSEG_NETBUF (Nbuf)->End = Seq + Len;\r
593\r
594 InsertTailList (&(Tcb->SndQue), &(Nbuf->List));\r
595\r
596 if (DataGet != 0) {\r
597\r
598 SockDataSent (Tcb->Sk, DataGet);\r
599 }\r
600\r
601 return Nbuf;\r
602}\r
603\r
604/**\r
605 Get a segment starting from sequence Seq of a maximum\r
606 length of Len.\r
607\r
608 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
609 @param[in] Seq The sequence number of the segment.\r
610 @param[in] Len The maximum length of the segment.\r
611\r
612 @return Pointer to the segment. If NULL, some error occurred.\r
613\r
614**/\r
615NET_BUF *\r
616TcpGetSegment (\r
617 IN TCP_CB *Tcb,\r
618 IN TCP_SEQNO Seq,\r
619 IN UINT32 Len\r
620 )\r
621{\r
622 NET_BUF *Nbuf;\r
623\r
624 ASSERT (Tcb != NULL);\r
625\r
626 //\r
627 // Compare the SndNxt with the max sequence number sent.\r
628 //\r
629 if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {\r
630\r
631 Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
632 } else {\r
633\r
634 Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);\r
635 }\r
636\r
637 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
638 return Nbuf;\r
639}\r
640\r
641/**\r
642 Retransmit the segment from sequence Seq.\r
643\r
644 @param[in] Tcb Pointer to the TCP_CB of this TCP instance.\r
645 @param[in] Seq The sequence number of the segment to be retransmitted.\r
646\r
647 @retval 0 Retransmission succeeded.\r
648 @retval -1 Error condition occurred.\r
649\r
650**/\r
651INTN\r
652TcpRetransmit (\r
653 IN TCP_CB *Tcb,\r
654 IN TCP_SEQNO Seq\r
655 )\r
656{\r
657 NET_BUF *Nbuf;\r
658 UINT32 Len;\r
659\r
660 //\r
661 // Compute the maxium length of retransmission. It is\r
662 // limited by three factors:\r
663 // 1. Less than SndMss\r
664 // 2. Must in the current send window\r
665 // 3. Will not change the boundaries of queued segments.\r
666 //\r
3696ceae
FS
667\r
668 //\r
669 // Handle the Window Retraction if TCP window scale is enabled according to RFC7323:\r
670 // On first retransmission, or if the sequence number is out of\r
671 // window by less than 2^Rcv.Wind.Shift, then do normal\r
672 // retransmission(s) without regard to the receiver window as long\r
673 // as the original segment was in window when it was sent.\r
674 //\r
675 if ((Tcb->SndWndScale != 0) &&\r
676 (TCP_SEQ_GT (Seq, Tcb->RetxmitSeqMax) || TCP_SEQ_BETWEEN (Tcb->SndWl2 + Tcb->SndWnd, Seq, Tcb->SndWl2 + Tcb->SndWnd + (1 << Tcb->SndWndScale)))) {\r
677 Len = TCP_SUB_SEQ (Tcb->SndNxt, Seq);\r
678 DEBUG (\r
679 (EFI_D_WARN,\r
680 "TcpRetransmit: retransmission without regard to the receiver window for TCB %p\n",\r
681 Tcb)\r
682 );\r
683 \r
684 } else if (TCP_SEQ_GEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {\r
685 Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);\r
686 \r
687 } else {\r
a3bcde70
HT
688 DEBUG (\r
689 (EFI_D_WARN,\r
690 "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n",\r
691 Tcb)\r
692 );\r
693\r
694 return 0;\r
695 }\r
696\r
3696ceae 697 Len = MIN (Len, Tcb->SndMss);\r
a3bcde70 698\r
3696ceae 699 Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
a3bcde70
HT
700 if (Nbuf == NULL) {\r
701 return -1;\r
702 }\r
703\r
704 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
705\r
706 if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
707 goto OnError;\r
708 }\r
709\r
3696ceae
FS
710 if (TCP_SEQ_GT (Seq, Tcb->RetxmitSeqMax)) {\r
711 Tcb->RetxmitSeqMax = Seq;\r
712 }\r
713\r
a3bcde70
HT
714 //\r
715 // The retransmitted buffer may be on the SndQue,\r
716 // trim TCP head because all the buffers on SndQue\r
717 // are headless.\r
718 //\r
719 ASSERT (Nbuf->Tcp != NULL);\r
720 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
721 Nbuf->Tcp = NULL;\r
722\r
723 NetbufFree (Nbuf);\r
724 return 0;\r
725\r
726OnError:\r
727 if (Nbuf != NULL) {\r
728 NetbufFree (Nbuf);\r
729 }\r
730\r
731 return -1;\r
732}\r
733\r
734/**\r
735 Verify that all the segments in SndQue are in good shape.\r
736\r
737 @param[in] Head Pointer to the head node of the SndQue.\r
738\r
739 @retval 0 At least one segment is broken.\r
740 @retval 1 All segments in the specific queue are in good shape.\r
741\r
742**/\r
743INTN\r
744TcpCheckSndQue (\r
745 IN LIST_ENTRY *Head\r
746 )\r
747{\r
748 LIST_ENTRY *Entry;\r
749 NET_BUF *Nbuf;\r
750 TCP_SEQNO Seq;\r
751\r
752 if (IsListEmpty (Head)) {\r
753 return 1;\r
754 }\r
755 //\r
756 // Initialize the Seq.\r
757 //\r
758 Entry = Head->ForwardLink;\r
759 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
760 Seq = TCPSEG_NETBUF (Nbuf)->Seq;\r
761\r
762 NET_LIST_FOR_EACH (Entry, Head) {\r
763 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
764\r
765 if (TcpVerifySegment (Nbuf) == 0) {\r
766 return 0;\r
767 }\r
768\r
769 //\r
770 // All the node in the SndQue should has:\r
771 // SEG.SEQ = LAST_SEG.END\r
772 //\r
773 if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {\r
774 return 0;\r
775 }\r
776\r
777 Seq = TCPSEG_NETBUF (Nbuf)->End;\r
778 }\r
779\r
780 return 1;\r
781}\r
782\r
783/**\r
784 Check whether to send data/SYN/FIN and piggyback an ACK.\r
785\r
786 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
787 @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm\r
788 and send out data by force.\r
789\r
790 @return The number of bytes sent.\r
791\r
792**/\r
793INTN\r
794TcpToSendData (\r
795 IN OUT TCP_CB *Tcb,\r
796 IN INTN Force\r
797 )\r
798{\r
799 UINT32 Len;\r
800 INTN Sent;\r
801 UINT8 Flag;\r
802 NET_BUF *Nbuf;\r
803 TCP_SEG *Seg;\r
804 TCP_SEQNO Seq;\r
805 TCP_SEQNO End;\r
806\r
807 ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));\r
808\r
809 Sent = 0;\r
810\r
811 if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {\r
812\r
813 return 0;\r
814 }\r
815\r
816 do {\r
817 //\r
818 // Compute how much data can be sent\r
819 //\r
820 Len = TcpDataToSend (Tcb, Force);\r
821 Seq = Tcb->SndNxt;\r
822\r
e72b4097 823 ASSERT ((Tcb->State) < (ARRAY_SIZE (mTcpOutFlag)));\r
a3bcde70
HT
824 Flag = mTcpOutFlag[Tcb->State];\r
825\r
826 if ((Flag & TCP_FLG_SYN) != 0) {\r
827\r
828 Seq = Tcb->Iss;\r
829 Len = 0;\r
830 }\r
831\r
832 //\r
833 // Only send a segment without data if SYN or\r
834 // FIN is set.\r
835 //\r
836 if ((Len == 0) && ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) {\r
837 return Sent;\r
838 }\r
839\r
840 Nbuf = TcpGetSegment (Tcb, Seq, Len);\r
841\r
842 if (Nbuf == NULL) {\r
843 DEBUG (\r
844 (EFI_D_ERROR,\r
845 "TcpToSendData: failed to get a segment for TCB %p\n",\r
846 Tcb)\r
847 );\r
848\r
849 goto OnError;\r
850 }\r
851\r
852 Seg = TCPSEG_NETBUF (Nbuf);\r
853\r
854 //\r
855 // Set the TcpSeg in Nbuf.\r
856 //\r
857 Len = Nbuf->TotalSize;\r
858 End = Seq + Len;\r
859 if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {\r
860 End++;\r
861 }\r
862\r
863 if ((Flag & TCP_FLG_FIN) != 0) {\r
864 //\r
865 // Send FIN if all data is sent, and FIN is\r
866 // in the window\r
867 //\r
868 if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&\r
869 (GET_SND_DATASIZE (Tcb->Sk) == 0) &&\r
870 TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)\r
871 ) {\r
872 DEBUG (\r
f3612a8d 873 (EFI_D_NET,\r
a3bcde70
HT
874 "TcpToSendData: send FIN to peer for TCB %p in state %s\n",\r
875 Tcb,\r
876 mTcpStateName[Tcb->State])\r
877 );\r
878\r
879 End++;\r
880 } else {\r
881 TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
882 }\r
883 }\r
884\r
885 Seg->Seq = Seq;\r
886 Seg->End = End;\r
887 Seg->Flag = Flag;\r
888\r
889 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
890 ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);\r
891\r
892 //\r
893 // Don't send an empty segment here.\r
894 //\r
895 if (Seg->End == Seg->Seq) {\r
896 DEBUG (\r
897 (EFI_D_WARN,\r
898 "TcpToSendData: created a empty segment for TCB %p, free it now\n",\r
899 Tcb)\r
900 );\r
901\r
902 NetbufFree (Nbuf);\r
903 return Sent;\r
904 }\r
905\r
906 if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
907 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
908 Nbuf->Tcp = NULL;\r
909\r
910 if ((Flag & TCP_FLG_FIN) != 0) {\r
911 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
912 }\r
913\r
914 goto OnError;\r
915 }\r
916\r
917 Sent += TCP_SUB_SEQ (End, Seq);\r
918\r
919 //\r
920 // All the buffers in the SndQue are headless.\r
921 //\r
922 ASSERT (Nbuf->Tcp != NULL);\r
923\r
924 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
925 Nbuf->Tcp = NULL;\r
926\r
927 NetbufFree (Nbuf);\r
928\r
929 //\r
930 // Update the status in TCB.\r
931 //\r
932 Tcb->DelayedAck = 0;\r
933\r
934 if ((Flag & TCP_FLG_FIN) != 0) {\r
935 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
936 }\r
937\r
938 if (TCP_SEQ_GT (End, Tcb->SndNxt)) {\r
939 Tcb->SndNxt = End;\r
940 }\r
941\r
942 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
943 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
944 }\r
945\r
946 //\r
947 // Enable RTT measurement only if not in retransmit.\r
948 // Karn's algorithm requires not to update RTT when in loss.\r
949 //\r
950 if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
951\r
952 DEBUG (\r
f3612a8d 953 (EFI_D_NET,\r
a3bcde70
HT
954 "TcpToSendData: set RTT measure sequence %d for TCB %p\n",\r
955 Seq,\r
956 Tcb)\r
957 );\r
958\r
959 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
960 Tcb->RttSeq = Seq;\r
961 Tcb->RttMeasure = 0;\r
962 }\r
963\r
964 } while (Len == Tcb->SndMss);\r
965\r
966 return Sent;\r
967\r
968OnError:\r
969 if (Nbuf != NULL) {\r
970 NetbufFree (Nbuf);\r
971 }\r
972\r
973 return Sent;\r
974}\r
975\r
976/**\r
977 Send an ACK immediately.\r
978\r
979 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
980\r
981**/\r
982VOID\r
983TcpSendAck (\r
984 IN OUT TCP_CB *Tcb\r
985 )\r
986{\r
987 NET_BUF *Nbuf;\r
988 TCP_SEG *Seg;\r
989\r
990 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
991\r
992 if (Nbuf == NULL) {\r
993 return;\r
994 }\r
995\r
996 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
997\r
998 Seg = TCPSEG_NETBUF (Nbuf);\r
999 Seg->Seq = Tcb->SndNxt;\r
1000 Seg->End = Tcb->SndNxt;\r
1001 Seg->Flag = TCP_FLG_ACK;\r
1002\r
1003 if (TcpTransmitSegment (Tcb, Nbuf) == 0) {\r
1004 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
1005 Tcb->DelayedAck = 0;\r
1006 }\r
1007\r
1008 NetbufFree (Nbuf);\r
1009}\r
1010\r
1011/**\r
1012 Send a zero probe segment. It can be used by keepalive and zero window probe.\r
1013\r
1014 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
1015\r
1016 @retval 0 The zero probe segment was sent out successfully.\r
1017 @retval other An error condition occurred.\r
1018\r
1019**/\r
1020INTN\r
1021TcpSendZeroProbe (\r
1022 IN OUT TCP_CB *Tcb\r
1023 )\r
1024{\r
1025 NET_BUF *Nbuf;\r
1026 TCP_SEG *Seg;\r
1027 INTN Result;\r
1028\r
1029 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
1030\r
1031 if (Nbuf == NULL) {\r
1032 return -1;\r
1033 }\r
1034\r
1035 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
1036\r
1037 //\r
1038 // SndNxt-1 is out of window. The peer should respond\r
1039 // with an ACK.\r
1040 //\r
1041 Seg = TCPSEG_NETBUF (Nbuf);\r
1042 Seg->Seq = Tcb->SndNxt - 1;\r
1043 Seg->End = Tcb->SndNxt - 1;\r
1044 Seg->Flag = TCP_FLG_ACK;\r
1045\r
1046 Result = TcpTransmitSegment (Tcb, Nbuf);\r
1047 NetbufFree (Nbuf);\r
1048\r
1049 return Result;\r
1050}\r
1051\r
1052/**\r
1053 Check whether to send an ACK or delayed ACK.\r
1054\r
1055 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
1056\r
1057**/\r
1058VOID\r
1059TcpToSendAck (\r
1060 IN OUT TCP_CB *Tcb\r
1061 )\r
1062{\r
1063 UINT32 TcpNow;\r
1064\r
1065 //\r
1066 // Generally, TCP should send a delayed ACK unless:\r
1067 // 1. ACK at least every other FULL sized segment received.\r
1068 // 2. Packets received out of order.\r
1069 // 3. Receiving window is open.\r
1070 //\r
1071 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Tcb->DelayedAck >= 1)) {\r
1072 TcpSendAck (Tcb);\r
1073 return;\r
1074 }\r
1075\r
1076 TcpNow = TcpRcvWinNow (Tcb);\r
1077\r
1078 if (TcpNow > TcpRcvWinOld (Tcb)) {\r
1079 TcpSendAck (Tcb);\r
1080 return;\r
1081 }\r
1082\r
1083 DEBUG (\r
f3612a8d 1084 (EFI_D_NET,\r
a3bcde70
HT
1085 "TcpToSendAck: scheduled a delayed ACK for TCB %p\n",\r
1086 Tcb)\r
1087 );\r
1088\r
1089 //\r
1090 // Schedule a delayed ACK.\r
1091 //\r
1092 Tcb->DelayedAck++;\r
1093}\r
1094\r
1095/**\r
1096 Send a RESET segment in response to the segment received.\r
1097\r
1098 @param[in] Tcb Pointer to the TCP_CB of this TCP instance. May be NULL.\r
1099 @param[in] Head TCP header of the segment that triggers the reset.\r
1100 @param[in] Len Length of the segment that triggers the reset.\r
1101 @param[in] Local Local IP address.\r
1102 @param[in] Remote Remote peer's IP address.\r
1103 @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack,\r
1104 IP_VERSION_6 indicates TCP is running on IP6 stack.\r
1105\r
1106 @retval 0 A reset was sent or there is no need to send it.\r
1107 @retval -1 No reset is sent.\r
1108\r
1109**/\r
1110INTN\r
1111TcpSendReset (\r
1112 IN TCP_CB *Tcb,\r
1113 IN TCP_HEAD *Head,\r
1114 IN INT32 Len,\r
1115 IN EFI_IP_ADDRESS *Local,\r
1116 IN EFI_IP_ADDRESS *Remote,\r
1117 IN UINT8 Version\r
1118 )\r
1119{\r
1120 NET_BUF *Nbuf;\r
1121 TCP_HEAD *Nhead;\r
1122 UINT16 HeadSum;\r
1123\r
1124 //\r
1125 // Don't respond to a Reset with reset.\r
1126 //\r
1127 if ((Head->Flag & TCP_FLG_RST) != 0) {\r
1128 return 0;\r
1129 }\r
1130\r
1131 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
1132\r
1133 if (Nbuf == NULL) {\r
1134 return -1;\r
1135 }\r
1136\r
1137 Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
1138 Nbuf,\r
1139 sizeof (TCP_HEAD),\r
1140 NET_BUF_TAIL\r
1141 );\r
1142\r
1143 ASSERT (Nhead != NULL);\r
1144\r
1145 Nbuf->Tcp = Nhead;\r
1146 Nhead->Flag = TCP_FLG_RST;\r
1147\r
1148 //\r
1149 // Derive Seq/ACK from the segment if no TCB\r
1150 // is associated with it, otherwise derive from the Tcb.\r
1151 //\r
1152 if (Tcb == NULL) {\r
1153\r
1154 if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {\r
1155 Nhead->Seq = Head->Ack;\r
1156 Nhead->Ack = 0;\r
1157 } else {\r
1158 Nhead->Seq = 0;\r
1159 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1160 Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);\r
1161 }\r
1162 } else {\r
1163\r
1164 Nhead->Seq = HTONL (Tcb->SndNxt);\r
1165 Nhead->Ack = HTONL (Tcb->RcvNxt);\r
1166 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1167 }\r
1168\r
1169 Nhead->SrcPort = Head->DstPort;\r
1170 Nhead->DstPort = Head->SrcPort;\r
1171 Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);\r
1172 Nhead->Res = 0;\r
1173 Nhead->Wnd = HTONS (0xFFFF);\r
1174 Nhead->Checksum = 0;\r
1175 Nhead->Urg = 0;\r
1176\r
1177 if (Version == IP_VERSION_4) {\r
1178 HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0);\r
1179 } else {\r
1180 HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0);\r
1181 }\r
1182\r
1183 Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);\r
1184\r
1185 TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version);\r
1186\r
1187 NetbufFree (Nbuf);\r
1188\r
1189 return 0;\r
1190}\r
1191\r
1192/**\r
1193 Verify that the segment is in good shape.\r
1194\r
1195 @param[in] Nbuf The buffer that contains the segment to be checked.\r
1196\r
1197 @retval 0 The segment is broken.\r
1198 @retval 1 The segment is in good shape.\r
1199\r
1200**/\r
1201INTN\r
1202TcpVerifySegment (\r
1203 IN NET_BUF *Nbuf\r
1204 )\r
1205{\r
1206 TCP_HEAD *Head;\r
1207 TCP_SEG *Seg;\r
1208 UINT32 Len;\r
1209\r
1210 if (Nbuf == NULL) {\r
1211 return 1;\r
1212 }\r
1213\r
1214 NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
1215\r
1216 Seg = TCPSEG_NETBUF (Nbuf);\r
1217 Len = Nbuf->TotalSize;\r
1218 Head = Nbuf->Tcp;\r
1219\r
1220 if (Head != NULL) {\r
1221 if (Head->Flag != Seg->Flag) {\r
1222 return 0;\r
1223 }\r
1224\r
1225 Len -= (Head->HeadLen << 2);\r
1226 }\r
1227\r
1228 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
1229 Len++;\r
1230 }\r
1231\r
1232 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
1233 Len++;\r
1234 }\r
1235\r
1236 if (Seg->Seq + Len != Seg->End) {\r
1237 return 0;\r
1238 }\r
1239\r
1240 return 1;\r
1241}\r
1242\r