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