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