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