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