]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
Update modules to show real dependencies on the BaseMemoryLib and MemoryAllocationLib
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Tcp4Dxe / Tcp4Output.c
CommitLineData
8a67d61d 1/** @file\r
dfc1f033 2 TCP output process routines.\r
3 \r
4Copyright (c) 2005 - 2006, Intel Corporation<BR>\r
8a67d61d 5All rights reserved. This program and the accompanying materials\r
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
448 ASSERT (Cur != Head);\r
449\r
450 //\r
451 // Return the buffer if it can be returned without\r
452 // adjustment:\r
453 //\r
454 if ((Seg->Seq == Seq) &&\r
455 TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&\r
456 !NET_BUF_SHARED (Node)) {\r
457\r
458 NET_GET_REF (Node);\r
459 return Node;\r
460 }\r
461\r
462 //\r
463 // Create a new buffer and copy data there.\r
464 //\r
465 Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
466\r
467 if (Nbuf == NULL) {\r
468 return NULL;\r
469 }\r
470\r
471 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
472\r
473 Flag = Seg->Flag;\r
474 End = Seg->End;\r
475\r
476 if (TCP_SEQ_LT (Seq + Len, Seg->End)) {\r
477 End = Seq + Len;\r
478 }\r
479\r
480 CopyLen = TCP_SUB_SEQ (End, Seq);\r
481 Offset = TCP_SUB_SEQ (Seq, Seg->Seq);\r
482\r
483 //\r
484 // If SYN is set and out of the range, clear the flag.\r
485 // Becuase the sequence of the first byte is SEG.SEQ+1,\r
486 // adjust Offset by -1. If SYN is in the range, copy\r
487 // one byte less.\r
488 //\r
489 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
490\r
491 if (TCP_SEQ_LT (Seg->Seq, Seq)) {\r
492\r
493 TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);\r
494 Offset--;\r
495 } else {\r
496\r
497 CopyLen--;\r
498 }\r
499 }\r
500\r
501 //\r
502 // If FIN is set and in the range, copy one byte less,\r
503 // and if it is out of the range, clear the flag.\r
504 //\r
505 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
506\r
507 if (Seg->End == End) {\r
508\r
509 CopyLen--;\r
510 } else {\r
511\r
512 TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
513 }\r
514 }\r
515\r
516 ASSERT (CopyLen >= 0);\r
517\r
518 //\r
519 // copy data to the segment\r
520 //\r
85511ddf 521 if (CopyLen != 0) {\r
8a67d61d 522 Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);\r
120db52c 523 ASSERT (Data != NULL);\r
8a67d61d 524\r
525 if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {\r
526 goto OnError;\r
527 }\r
528 }\r
529\r
e48e37fc 530 CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));\r
8a67d61d 531\r
532 TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
533 TCPSEG_NETBUF (Nbuf)->End = End;\r
534 TCPSEG_NETBUF (Nbuf)->Flag = Flag;\r
535\r
536 return Nbuf;\r
537\r
538OnError:\r
539 NetbufFree (Nbuf);\r
540 return NULL;\r
541}\r
542\r
543\r
544/**\r
545 Get a segment from the Tcb's socket buffer.\r
546\r
547 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
548 @param Seq The sequence number of the segment.\r
549 @param Len The maximum length of the segment.\r
550\r
551 @return Pointer to the segment, if NULL some error occurred.\r
552\r
553**/\r
554NET_BUF *\r
555TcpGetSegmentSock (\r
556 IN TCP_CB *Tcb,\r
557 IN TCP_SEQNO Seq,\r
558 IN UINT32 Len\r
559 )\r
560{\r
561 NET_BUF *Nbuf;\r
562 UINT8 *Data;\r
563 UINT32 DataGet;\r
564\r
120db52c 565 ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
8a67d61d 566\r
567 Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
568\r
569 if (Nbuf == NULL) {\r
e48e37fc 570 DEBUG ((EFI_D_ERROR, "TcpGetSegmentSock: failed to allocate "\r
0e549d5b 571 "a netbuf for TCB %p\n",Tcb));\r
8a67d61d 572\r
573 return NULL;\r
574 }\r
575\r
576 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
577\r
578 DataGet = 0;\r
579\r
85511ddf 580 if (Len != 0) {\r
8a67d61d 581 //\r
582 // copy data to the segment.\r
583 //\r
584 Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);\r
120db52c 585 ASSERT (Data != NULL);\r
8a67d61d 586\r
587 DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);\r
588 }\r
589\r
590 NET_GET_REF (Nbuf);\r
591\r
592 TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
593 TCPSEG_NETBUF (Nbuf)->End = Seq + Len;\r
594\r
e48e37fc 595 InsertTailList (&(Tcb->SndQue), &(Nbuf->List));\r
8a67d61d 596\r
597 if (DataGet != 0) {\r
598\r
599 SockDataSent (Tcb->Sk, DataGet);\r
600 }\r
601\r
602 return Nbuf;\r
603}\r
604\r
605\r
606/**\r
607 Get a segment starting from sequence Seq of a maximum\r
608 length of Len.\r
609\r
610 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
611 @param Seq The sequence number of the segment.\r
612 @param Len The maximum length of the segment.\r
613\r
614 @return Pointer to the segment, if NULL some error occurred.\r
615\r
616**/\r
617NET_BUF *\r
618TcpGetSegment (\r
619 IN TCP_CB *Tcb,\r
620 IN TCP_SEQNO Seq,\r
621 IN UINT32 Len\r
622 )\r
623{\r
624 NET_BUF *Nbuf;\r
625\r
120db52c 626 ASSERT (Tcb != NULL);\r
8a67d61d 627\r
628 //\r
629 // Compare the SndNxt with the max sequence number sent.\r
630 //\r
631 if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {\r
632\r
633 Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
634 } else {\r
635\r
636 Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);\r
637 }\r
638\r
120db52c 639 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
8a67d61d 640 return Nbuf;\r
641}\r
642\r
643\r
644/**\r
645 Retransmit the segment from sequence Seq.\r
646\r
647 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
648 @param Seq The sequence number of the segment to be retransmitted.\r
649\r
650 @retval 0 Retransmission succeeded.\r
651 @retval -1 Error condition occurred.\r
652\r
653**/\r
654INTN\r
655TcpRetransmit (\r
656 IN TCP_CB *Tcb,\r
657 IN TCP_SEQNO Seq\r
658 )\r
659{\r
660 NET_BUF *Nbuf;\r
661 UINT32 Len;\r
662\r
663 //\r
664 // Compute the maxium length of retransmission. It is\r
665 // limited by three factors:\r
666 // 1. Less than SndMss\r
667 // 2. must in the current send window\r
668 // 3. will not change the boundaries of queued segments.\r
669 //\r
670 if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {\r
e48e37fc 671 DEBUG ((EFI_D_WARN, "TcpRetransmit: retransmission cancelled "\r
0e549d5b 672 "because send window too small for TCB %p\n", Tcb));\r
8a67d61d 673\r
674 return 0;\r
675 }\r
676\r
677 Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);\r
36ee91ca 678 Len = MIN (Len, Tcb->SndMss);\r
8a67d61d 679\r
680 Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
681 if (Nbuf == NULL) {\r
682 return -1;\r
683 }\r
684\r
120db52c 685 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
8a67d61d 686\r
687 if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
688 goto OnError;\r
689 }\r
690\r
691 //\r
692 // The retransmitted buffer may be on the SndQue,\r
693 // trim TCP head because all the buffer on SndQue\r
694 // are headless.\r
695 //\r
120db52c 696 ASSERT (Nbuf->Tcp != NULL);\r
8a67d61d 697 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
698 Nbuf->Tcp = NULL;\r
699\r
700 NetbufFree (Nbuf);\r
701 return 0;\r
702\r
703OnError:\r
704 if (Nbuf != NULL) {\r
705 NetbufFree (Nbuf);\r
706 }\r
707\r
708 return -1;\r
709}\r
710\r
711\r
712/**\r
713 Check whether to send data/SYN/FIN and piggy back an ACK.\r
714\r
715 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
716 @param Force Whether to ignore the sender's SWS avoidance algorithm and send\r
717 out data by force.\r
718\r
719 @return The number of bytes sent.\r
720\r
721**/\r
722INTN\r
723TcpToSendData (\r
77f00155 724 IN OUT TCP_CB *Tcb,\r
725 IN INTN Force\r
8a67d61d 726 )\r
727{\r
728 UINT32 Len;\r
729 INTN Sent;\r
730 UINT8 Flag;\r
731 NET_BUF *Nbuf;\r
732 TCP_SEG *Seg;\r
733 TCP_SEQNO Seq;\r
734 TCP_SEQNO End;\r
735\r
120db52c 736 ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));\r
8a67d61d 737\r
738 Sent = 0;\r
739\r
740 if ((Tcb->State == TCP_CLOSED) ||\r
741 TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {\r
742\r
743 return 0;\r
744 }\r
745\r
746SEND_AGAIN:\r
747 //\r
748 // compute how much data can be sent\r
749 //\r
750 Len = TcpDataToSend (Tcb, Force);\r
751 Seq = Tcb->SndNxt;\r
752\r
753 Flag = mTcpOutFlag[Tcb->State];\r
754\r
85511ddf 755 if ((Flag & TCP_FLG_SYN) != 0) {\r
8a67d61d 756\r
757 Seq = Tcb->Iss;\r
758 Len = 0;\r
759 }\r
760\r
761 //\r
762 // only send a segment without data if SYN or\r
763 // FIN is set.\r
764 //\r
85511ddf 765 if ((Len == 0) && \r
766 ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) {\r
8a67d61d 767 return Sent;\r
768 }\r
769\r
770 Nbuf = TcpGetSegment (Tcb, Seq, Len);\r
771\r
772 if (Nbuf == NULL) {\r
e48e37fc 773 DEBUG (\r
774 (EFI_D_ERROR,\r
0e549d5b 775 "TcpToSendData: failed to get a segment for TCB %p\n",\r
8a67d61d 776 Tcb)\r
777 );\r
778\r
779 goto OnError;\r
780 }\r
781\r
782 Seg = TCPSEG_NETBUF (Nbuf);\r
783\r
784 //\r
785 // Set the TcpSeg in Nbuf.\r
786 //\r
787 Len = Nbuf->TotalSize;\r
788 End = Seq + Len;\r
789 if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {\r
790 End++;\r
791 }\r
792\r
85511ddf 793 if ((Flag & TCP_FLG_FIN) != 0) {\r
8a67d61d 794 //\r
795 // Send FIN if all data is sent, and FIN is\r
796 // in the window\r
797 //\r
798 if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&\r
799 (GET_SND_DATASIZE (Tcb->Sk) == 0) &&\r
120db52c 800 TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)) {\r
8a67d61d 801\r
dfc1f033 802 DEBUG (\r
803 (EFI_D_INFO, \r
804 "TcpToSendData: send FIN "\r
805 "to peer for TCB %p in state %s\n", \r
806 Tcb, \r
807 mTcpStateName[Tcb->State])\r
808 );\r
8a67d61d 809\r
810 End++;\r
811 } else {\r
812 TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
813 }\r
814 }\r
815\r
816 Seg->Seq = Seq;\r
817 Seg->End = End;\r
818 Seg->Flag = Flag;\r
819\r
120db52c 820 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
821 ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);\r
8a67d61d 822\r
823 //\r
824 // don't send an empty segment here.\r
825 //\r
826 if (Seg->End == Seg->Seq) {\r
e48e37fc 827 DEBUG ((EFI_D_WARN, "TcpToSendData: created a empty"\r
0e549d5b 828 " segment for TCB %p, free it now\n", Tcb));\r
8a67d61d 829\r
830 NetbufFree (Nbuf);\r
831 return Sent;\r
832 }\r
833\r
834 if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
8a67d61d 835 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
836 Nbuf->Tcp = NULL;\r
837\r
85511ddf 838 if ((Flag & TCP_FLG_FIN) != 0) {\r
8a67d61d 839 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
840 }\r
841\r
842 goto OnError;\r
843 }\r
844\r
845 Sent += TCP_SUB_SEQ (End, Seq);\r
846\r
847 //\r
848 // All the buffer in the SndQue is headless\r
849 //\r
120db52c 850 ASSERT (Nbuf->Tcp != NULL);\r
8a67d61d 851\r
852 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
853 Nbuf->Tcp = NULL;\r
854\r
855 NetbufFree (Nbuf);\r
856\r
857 //\r
858 // update status in TCB\r
859 //\r
860 Tcb->DelayedAck = 0;\r
861\r
85511ddf 862 if ((Flag & TCP_FLG_FIN) != 0) {\r
8a67d61d 863 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
864 }\r
865\r
866 if (TCP_SEQ_GT (End, Tcb->SndNxt)) {\r
867 Tcb->SndNxt = End;\r
868 }\r
869\r
870 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
871 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
872 }\r
873\r
874 //\r
875 // Enable RTT measurement only if not in retransmit.\r
876 // Karn's algorithm reqires not to update RTT when in loss.\r
877 //\r
878 if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&\r
879 !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
880\r
e48e37fc 881 DEBUG ((EFI_D_INFO, "TcpToSendData: set RTT measure "\r
0e549d5b 882 "sequence %d for TCB %p\n", Seq, Tcb));\r
8a67d61d 883\r
884 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
885 Tcb->RttSeq = Seq;\r
886 Tcb->RttMeasure = 0;\r
887 }\r
888\r
889 if (Len == Tcb->SndMss) {\r
890 goto SEND_AGAIN;\r
891 }\r
892\r
893 return Sent;\r
894\r
895OnError:\r
896 if (Nbuf != NULL) {\r
897 NetbufFree (Nbuf);\r
898 }\r
899\r
900 return Sent;\r
901}\r
902\r
903\r
904/**\r
905 Send an ACK immediately.\r
906\r
907 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
908\r
8a67d61d 909**/\r
910VOID\r
911TcpSendAck (\r
77f00155 912 IN OUT TCP_CB *Tcb\r
8a67d61d 913 )\r
914{\r
915 NET_BUF *Nbuf;\r
916 TCP_SEG *Seg;\r
917\r
918 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
919\r
920 if (Nbuf == NULL) {\r
921 return;\r
922 }\r
923\r
924 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
925\r
926 Seg = TCPSEG_NETBUF (Nbuf);\r
927 Seg->Seq = Tcb->SndNxt;\r
928 Seg->End = Tcb->SndNxt;\r
929 Seg->Flag = TCP_FLG_ACK;\r
930\r
931 if (TcpTransmitSegment (Tcb, Nbuf) == 0) {\r
932 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
933 Tcb->DelayedAck = 0;\r
934 }\r
935\r
936 NetbufFree (Nbuf);\r
937}\r
938\r
939\r
940/**\r
120db52c 941 Send a zero probe segment. It can be used by keepalive and zero window probe.\r
8a67d61d 942\r
943 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
944\r
945 @retval 0 The zero probe segment was sent out successfully.\r
946 @retval other Error condition occurred.\r
947\r
948**/\r
949INTN\r
950TcpSendZeroProbe (\r
77f00155 951 IN OUT TCP_CB *Tcb\r
8a67d61d 952 )\r
953{\r
954 NET_BUF *Nbuf;\r
955 TCP_SEG *Seg;\r
956 INTN Result;\r
957\r
958 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
959\r
960 if (Nbuf == NULL) {\r
961 return -1;\r
962 }\r
963\r
964 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
965\r
966 //\r
967 // SndNxt-1 is out of window. The peer should respond\r
968 // with an ACK.\r
969 //\r
970 Seg = TCPSEG_NETBUF (Nbuf);\r
971 Seg->Seq = Tcb->SndNxt - 1;\r
972 Seg->End = Tcb->SndNxt - 1;\r
973 Seg->Flag = TCP_FLG_ACK;\r
974\r
975 Result = TcpTransmitSegment (Tcb, Nbuf);\r
976 NetbufFree (Nbuf);\r
977\r
978 return Result;\r
979}\r
980\r
981\r
982/**\r
983 Check whether to send an ACK or delayed ACK.\r
984\r
985 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
986\r
8a67d61d 987**/\r
988VOID\r
989TcpToSendAck (\r
77f00155 990 IN OUT TCP_CB *Tcb\r
8a67d61d 991 )\r
992{\r
4eb65aff 993 UINT32 TcpNow;\r
994\r
995 TcpNow = TcpRcvWinNow (Tcb);\r
8a67d61d 996 //\r
997 // Generally, TCP should send a delayed ACK unless:\r
998 // 1. ACK at least every other FULL sized segment received,\r
999 // 2. Packets received out of order\r
1000 // 3. Receiving window is open\r
1001 //\r
1002 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) ||\r
1003 (Tcb->DelayedAck >= 1) ||\r
120db52c 1004 (TcpNow > TcpRcvWinOld (Tcb))) {\r
8a67d61d 1005 TcpSendAck (Tcb);\r
1006 return;\r
1007 }\r
1008\r
e48e37fc 1009 DEBUG ((EFI_D_INFO, "TcpToSendAck: scheduled a delayed"\r
0e549d5b 1010 " ACK for TCB %p\n", Tcb));\r
8a67d61d 1011\r
1012 //\r
1013 // schedule a delayed ACK\r
1014 //\r
1015 Tcb->DelayedAck++;\r
1016}\r
1017\r
1018\r
1019/**\r
1020 Send a RESET segment in response to the segment received.\r
1021\r
1022 @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.\r
1023 @param Head TCP header of the segment that triggers the reset.\r
1024 @param Len Length of the segment that triggers the reset.\r
1025 @param Local Local IP address.\r
1026 @param Remote Remote peer's IP address.\r
1027\r
1028 @retval 0 A reset is sent or no need to send it.\r
1029 @retval -1 No reset is sent.\r
1030\r
1031**/\r
1032INTN\r
1033TcpSendReset (\r
1034 IN TCP_CB *Tcb,\r
1035 IN TCP_HEAD *Head,\r
1036 IN INT32 Len,\r
1037 IN UINT32 Local,\r
1038 IN UINT32 Remote\r
1039 )\r
1040{\r
1041 NET_BUF *Nbuf;\r
1042 TCP_HEAD *Nhead;\r
1043 UINT16 HeadSum;\r
1044\r
1045 //\r
1046 // Don't respond to a Reset with reset\r
1047 //\r
120db52c 1048 if ((Head->Flag & TCP_FLG_RST) != 0) {\r
8a67d61d 1049 return 0;\r
1050 }\r
1051\r
1052 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
1053\r
1054 if (Nbuf == NULL) {\r
1055 return -1;\r
1056 }\r
1057\r
1058 Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
1059 Nbuf,\r
1060 sizeof (TCP_HEAD),\r
1061 NET_BUF_TAIL\r
1062 );\r
1063\r
1064 ASSERT (Nhead != NULL);\r
1065\r
1066 Nbuf->Tcp = Nhead;\r
1067 Nhead->Flag = TCP_FLG_RST;\r
1068\r
1069 //\r
1070 // Derive Seq/ACK from the segment if no TCB\r
1071 // associated with it, otherwise from the Tcb\r
1072 //\r
1073 if (Tcb == NULL) {\r
1074\r
1075 if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {\r
1076 Nhead->Seq = Head->Ack;\r
1077 Nhead->Ack = 0;\r
1078 } else {\r
1079 Nhead->Seq = 0;\r
1080 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1081 Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);\r
1082 }\r
1083 } else {\r
1084\r
1085 Nhead->Seq = HTONL (Tcb->SndNxt);\r
1086 Nhead->Ack = HTONL (Tcb->RcvNxt);\r
1087 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1088 }\r
1089\r
1090 Nhead->SrcPort = Head->DstPort;\r
1091 Nhead->DstPort = Head->SrcPort;\r
1092 Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2);\r
1093 Nhead->Res = 0;\r
1094 Nhead->Wnd = HTONS (0xFFFF);\r
1095 Nhead->Checksum = 0;\r
1096 Nhead->Urg = 0;\r
1097\r
1098 HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0);\r
1099 Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);\r
1100\r
1101 TcpSendIpPacket (Tcb, Nbuf, Local, Remote);\r
1102\r
1103 NetbufFree (Nbuf);\r
1104 return 0;\r
1105}\r
1106\r
1107\r
1108/**\r
1109 Verify that the segment is in good shape.\r
1110\r
1111 @param Nbuf Buffer that contains the segment to be checked.\r
1112\r
1113 @retval 0 The segment is broken.\r
1114 @retval 1 The segment is in good shape.\r
1115\r
1116**/\r
1117INTN\r
1118TcpVerifySegment (\r
1119 IN NET_BUF *Nbuf\r
1120 )\r
1121{\r
1122 TCP_HEAD *Head;\r
1123 TCP_SEG *Seg;\r
1124 UINT32 Len;\r
1125\r
1126 if (Nbuf == NULL) {\r
1127 return 1;\r
1128 }\r
1129\r
1130 NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
1131\r
1132 Seg = TCPSEG_NETBUF (Nbuf);\r
1133 Len = Nbuf->TotalSize;\r
1134 Head = Nbuf->Tcp;\r
1135\r
1136 if (Head != NULL) {\r
1137 if (Head->Flag != Seg->Flag) {\r
1138 return 0;\r
1139 }\r
1140\r
1141 Len -= (Head->HeadLen << 2);\r
1142 }\r
1143\r
1144 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
1145 Len++;\r
1146 }\r
1147\r
1148 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
1149 Len++;\r
1150 }\r
1151\r
1152 if (Seg->Seq + Len != Seg->End) {\r
1153 return 0;\r
1154 }\r
1155\r
1156 return 1;\r
1157}\r
1158\r
1159\r
1160/**\r
1161 Verify that all the segments in SndQue are in good shape.\r
1162\r
1163 @param Head Pointer to the head node of the SndQue.\r
1164\r
1165 @retval 0 At least one segment is broken.\r
1166 @retval 1 All segments in the specific queue are in good shape.\r
1167\r
1168**/\r
1169INTN\r
1170TcpCheckSndQue (\r
e48e37fc 1171 IN LIST_ENTRY *Head\r
8a67d61d 1172 )\r
1173{\r
e48e37fc 1174 LIST_ENTRY *Entry;\r
8a67d61d 1175 NET_BUF *Nbuf;\r
1176 TCP_SEQNO Seq;\r
1177\r
e48e37fc 1178 if (IsListEmpty (Head)) {\r
8a67d61d 1179 return 1;\r
1180 }\r
1181 //\r
1182 // Initialize the Seq\r
1183 //\r
1184 Entry = Head->ForwardLink;\r
1185 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1186 Seq = TCPSEG_NETBUF (Nbuf)->Seq;\r
1187\r
1188 NET_LIST_FOR_EACH (Entry, Head) {\r
1189 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1190\r
1191 if (TcpVerifySegment (Nbuf) == 0) {\r
1192 return 0;\r
1193 }\r
1194\r
1195 //\r
1196 // All the node in the SndQue should has:\r
1197 // SEG.SEQ = LAST_SEG.END\r
1198 //\r
1199 if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {\r
1200 return 0;\r
1201 }\r
1202\r
1203 Seq = TCPSEG_NETBUF (Nbuf)->End;\r
1204 }\r
1205\r
1206 return 1;\r
1207}\r