]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
MdeModulePkg: Clean up source files
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Tcp4Dxe / Tcp4Output.c
CommitLineData
8a67d61d 1/** @file\r
dfc1f033 2 TCP output process routines.\r
d1102dba
LG
3\r
4Copyright (c) 2005 - 2018, 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
ca12a0c8
FS
674\r
675 //\r
676 // Handle the Window Retraction if TCP window scale is enabled according to RFC7323:\r
677 // On first retransmission, or if the sequence number is out of\r
678 // window by less than 2^Rcv.Wind.Shift, then do normal\r
679 // retransmission(s) without regard to the receiver window as long\r
680 // as the original segment was in window when it was sent.\r
681 //\r
682 if ((Tcb->SndWndScale != 0) &&\r
683 (TCP_SEQ_GT (Seq, Tcb->RetxmitSeqMax) || TCP_SEQ_BETWEEN (Tcb->SndWl2 + Tcb->SndWnd, Seq, Tcb->SndWl2 + Tcb->SndWnd + (1 << Tcb->SndWndScale)))) {\r
684 Len = TCP_SUB_SEQ (Tcb->SndNxt, Seq);\r
685 DEBUG (\r
686 (EFI_D_WARN,\r
687 "TcpRetransmit: retransmission without regard to the receiver window for TCB %p\n",\r
688 Tcb)\r
689 );\r
d1102dba 690\r
ca12a0c8
FS
691 } else if (TCP_SEQ_GEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {\r
692 Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);\r
d1102dba 693\r
ca12a0c8
FS
694 } else {\r
695 DEBUG (\r
696 (EFI_D_WARN,\r
697 "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n",\r
698 Tcb)\r
699 );\r
8a67d61d 700\r
701 return 0;\r
702 }\r
d1102dba 703\r
ca12a0c8 704 Len = MIN (Len, Tcb->SndMss);\r
8a67d61d 705\r
ca12a0c8 706 Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
8a67d61d 707 if (Nbuf == NULL) {\r
708 return -1;\r
709 }\r
710\r
120db52c 711 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
8a67d61d 712\r
713 if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
714 goto OnError;\r
715 }\r
d1102dba 716\r
ca12a0c8
FS
717 if (TCP_SEQ_GT (Seq, Tcb->RetxmitSeqMax)) {\r
718 Tcb->RetxmitSeqMax = Seq;\r
719 }\r
8a67d61d 720\r
721 //\r
722 // The retransmitted buffer may be on the SndQue,\r
723 // trim TCP head because all the buffer on SndQue\r
724 // are headless.\r
725 //\r
120db52c 726 ASSERT (Nbuf->Tcp != NULL);\r
8a67d61d 727 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
728 Nbuf->Tcp = NULL;\r
729\r
730 NetbufFree (Nbuf);\r
731 return 0;\r
732\r
733OnError:\r
734 if (Nbuf != NULL) {\r
735 NetbufFree (Nbuf);\r
736 }\r
737\r
738 return -1;\r
739}\r
740\r
741\r
742/**\r
743 Check whether to send data/SYN/FIN and piggy back an ACK.\r
744\r
745 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
746 @param Force Whether to ignore the sender's SWS avoidance algorithm and send\r
747 out data by force.\r
748\r
749 @return The number of bytes sent.\r
750\r
751**/\r
752INTN\r
753TcpToSendData (\r
77f00155 754 IN OUT TCP_CB *Tcb,\r
755 IN INTN Force\r
8a67d61d 756 )\r
757{\r
758 UINT32 Len;\r
759 INTN Sent;\r
760 UINT8 Flag;\r
761 NET_BUF *Nbuf;\r
762 TCP_SEG *Seg;\r
763 TCP_SEQNO Seq;\r
764 TCP_SEQNO End;\r
765\r
120db52c 766 ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN));\r
8a67d61d 767\r
768 Sent = 0;\r
769\r
770 if ((Tcb->State == TCP_CLOSED) ||\r
771 TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {\r
772\r
773 return 0;\r
774 }\r
775\r
776SEND_AGAIN:\r
777 //\r
778 // compute how much data can be sent\r
779 //\r
780 Len = TcpDataToSend (Tcb, Force);\r
781 Seq = Tcb->SndNxt;\r
782\r
052d7c31 783 ASSERT ((Tcb->State) < (ARRAY_SIZE (mTcpOutFlag)));\r
8a67d61d 784 Flag = mTcpOutFlag[Tcb->State];\r
785\r
85511ddf 786 if ((Flag & TCP_FLG_SYN) != 0) {\r
8a67d61d 787\r
788 Seq = Tcb->Iss;\r
789 Len = 0;\r
790 }\r
791\r
792 //\r
793 // only send a segment without data if SYN or\r
794 // FIN is set.\r
795 //\r
d1102dba 796 if ((Len == 0) &&\r
85511ddf 797 ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) {\r
8a67d61d 798 return Sent;\r
799 }\r
800\r
801 Nbuf = TcpGetSegment (Tcb, Seq, Len);\r
802\r
803 if (Nbuf == NULL) {\r
e48e37fc 804 DEBUG (\r
805 (EFI_D_ERROR,\r
0e549d5b 806 "TcpToSendData: failed to get a segment for TCB %p\n",\r
8a67d61d 807 Tcb)\r
808 );\r
809\r
810 goto OnError;\r
811 }\r
812\r
813 Seg = TCPSEG_NETBUF (Nbuf);\r
814\r
815 //\r
816 // Set the TcpSeg in Nbuf.\r
817 //\r
818 Len = Nbuf->TotalSize;\r
819 End = Seq + Len;\r
820 if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {\r
821 End++;\r
822 }\r
823\r
85511ddf 824 if ((Flag & TCP_FLG_FIN) != 0) {\r
8a67d61d 825 //\r
826 // Send FIN if all data is sent, and FIN is\r
827 // in the window\r
828 //\r
829 if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&\r
830 (GET_SND_DATASIZE (Tcb->Sk) == 0) &&\r
120db52c 831 TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)) {\r
8a67d61d 832\r
dfc1f033 833 DEBUG (\r
d1102dba
LG
834 (EFI_D_NET,\r
835 "TcpToSendData: send FIN "\r
836 "to peer for TCB %p in state %s\n",\r
837 Tcb,\r
dfc1f033 838 mTcpStateName[Tcb->State])\r
839 );\r
8a67d61d 840\r
841 End++;\r
842 } else {\r
843 TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
844 }\r
845 }\r
846\r
847 Seg->Seq = Seq;\r
848 Seg->End = End;\r
849 Seg->Flag = Flag;\r
850\r
120db52c 851 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
852 ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0);\r
8a67d61d 853\r
854 //\r
855 // don't send an empty segment here.\r
856 //\r
857 if (Seg->End == Seg->Seq) {\r
e48e37fc 858 DEBUG ((EFI_D_WARN, "TcpToSendData: created a empty"\r
0e549d5b 859 " segment for TCB %p, free it now\n", Tcb));\r
8a67d61d 860\r
861 NetbufFree (Nbuf);\r
862 return Sent;\r
863 }\r
864\r
865 if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
8a67d61d 866 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
867 Nbuf->Tcp = NULL;\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 goto OnError;\r
874 }\r
875\r
876 Sent += TCP_SUB_SEQ (End, Seq);\r
877\r
878 //\r
879 // All the buffer in the SndQue is headless\r
880 //\r
120db52c 881 ASSERT (Nbuf->Tcp != NULL);\r
8a67d61d 882\r
883 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
884 Nbuf->Tcp = NULL;\r
885\r
886 NetbufFree (Nbuf);\r
887\r
888 //\r
889 // update status in TCB\r
890 //\r
891 Tcb->DelayedAck = 0;\r
892\r
85511ddf 893 if ((Flag & TCP_FLG_FIN) != 0) {\r
8a67d61d 894 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
895 }\r
896\r
897 if (TCP_SEQ_GT (End, Tcb->SndNxt)) {\r
898 Tcb->SndNxt = End;\r
899 }\r
900\r
901 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
902 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
903 }\r
904\r
905 //\r
906 // Enable RTT measurement only if not in retransmit.\r
907 // Karn's algorithm reqires not to update RTT when in loss.\r
908 //\r
909 if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&\r
910 !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
911\r
dea6914d 912 DEBUG ((EFI_D_NET, "TcpToSendData: set RTT measure "\r
0e549d5b 913 "sequence %d for TCB %p\n", Seq, Tcb));\r
8a67d61d 914\r
915 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
916 Tcb->RttSeq = Seq;\r
917 Tcb->RttMeasure = 0;\r
918 }\r
919\r
920 if (Len == Tcb->SndMss) {\r
921 goto SEND_AGAIN;\r
922 }\r
923\r
924 return Sent;\r
925\r
926OnError:\r
927 if (Nbuf != NULL) {\r
928 NetbufFree (Nbuf);\r
929 }\r
930\r
931 return Sent;\r
932}\r
933\r
934\r
935/**\r
936 Send an ACK immediately.\r
937\r
938 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
939\r
8a67d61d 940**/\r
941VOID\r
942TcpSendAck (\r
77f00155 943 IN OUT TCP_CB *Tcb\r
8a67d61d 944 )\r
945{\r
946 NET_BUF *Nbuf;\r
947 TCP_SEG *Seg;\r
948\r
949 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
950\r
951 if (Nbuf == NULL) {\r
952 return;\r
953 }\r
954\r
955 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
956\r
957 Seg = TCPSEG_NETBUF (Nbuf);\r
958 Seg->Seq = Tcb->SndNxt;\r
959 Seg->End = Tcb->SndNxt;\r
960 Seg->Flag = TCP_FLG_ACK;\r
961\r
962 if (TcpTransmitSegment (Tcb, Nbuf) == 0) {\r
963 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
964 Tcb->DelayedAck = 0;\r
965 }\r
966\r
967 NetbufFree (Nbuf);\r
968}\r
969\r
970\r
971/**\r
120db52c 972 Send a zero probe segment. It can be used by keepalive and zero window probe.\r
8a67d61d 973\r
974 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
975\r
976 @retval 0 The zero probe segment was sent out successfully.\r
977 @retval other Error condition occurred.\r
978\r
979**/\r
980INTN\r
981TcpSendZeroProbe (\r
77f00155 982 IN OUT TCP_CB *Tcb\r
8a67d61d 983 )\r
984{\r
985 NET_BUF *Nbuf;\r
986 TCP_SEG *Seg;\r
987 INTN Result;\r
988\r
989 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
990\r
991 if (Nbuf == NULL) {\r
992 return -1;\r
993 }\r
994\r
995 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
996\r
997 //\r
998 // SndNxt-1 is out of window. The peer should respond\r
999 // with an ACK.\r
1000 //\r
1001 Seg = TCPSEG_NETBUF (Nbuf);\r
1002 Seg->Seq = Tcb->SndNxt - 1;\r
1003 Seg->End = Tcb->SndNxt - 1;\r
1004 Seg->Flag = TCP_FLG_ACK;\r
1005\r
1006 Result = TcpTransmitSegment (Tcb, Nbuf);\r
1007 NetbufFree (Nbuf);\r
1008\r
1009 return Result;\r
1010}\r
1011\r
1012\r
1013/**\r
1014 Check whether to send an ACK or delayed ACK.\r
1015\r
1016 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
1017\r
8a67d61d 1018**/\r
1019VOID\r
1020TcpToSendAck (\r
77f00155 1021 IN OUT TCP_CB *Tcb\r
8a67d61d 1022 )\r
1023{\r
4eb65aff 1024 UINT32 TcpNow;\r
1025\r
1026 TcpNow = TcpRcvWinNow (Tcb);\r
8a67d61d 1027 //\r
1028 // Generally, TCP should send a delayed ACK unless:\r
1029 // 1. ACK at least every other FULL sized segment received,\r
1030 // 2. Packets received out of order\r
1031 // 3. Receiving window is open\r
1032 //\r
1033 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) ||\r
1034 (Tcb->DelayedAck >= 1) ||\r
120db52c 1035 (TcpNow > TcpRcvWinOld (Tcb))) {\r
8a67d61d 1036 TcpSendAck (Tcb);\r
1037 return;\r
1038 }\r
1039\r
dea6914d 1040 DEBUG ((EFI_D_NET, "TcpToSendAck: scheduled a delayed"\r
0e549d5b 1041 " ACK for TCB %p\n", Tcb));\r
8a67d61d 1042\r
1043 //\r
1044 // schedule a delayed ACK\r
1045 //\r
1046 Tcb->DelayedAck++;\r
1047}\r
1048\r
1049\r
1050/**\r
1051 Send a RESET segment in response to the segment received.\r
1052\r
1053 @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.\r
1054 @param Head TCP header of the segment that triggers the reset.\r
1055 @param Len Length of the segment that triggers the reset.\r
1056 @param Local Local IP address.\r
1057 @param Remote Remote peer's IP address.\r
1058\r
1059 @retval 0 A reset is sent or no need to send it.\r
1060 @retval -1 No reset is sent.\r
1061\r
1062**/\r
1063INTN\r
1064TcpSendReset (\r
1065 IN TCP_CB *Tcb,\r
1066 IN TCP_HEAD *Head,\r
1067 IN INT32 Len,\r
1068 IN UINT32 Local,\r
1069 IN UINT32 Remote\r
1070 )\r
1071{\r
1072 NET_BUF *Nbuf;\r
1073 TCP_HEAD *Nhead;\r
1074 UINT16 HeadSum;\r
1075\r
1076 //\r
1077 // Don't respond to a Reset with reset\r
1078 //\r
120db52c 1079 if ((Head->Flag & TCP_FLG_RST) != 0) {\r
8a67d61d 1080 return 0;\r
1081 }\r
1082\r
1083 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
1084\r
1085 if (Nbuf == NULL) {\r
1086 return -1;\r
1087 }\r
1088\r
1089 Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
1090 Nbuf,\r
1091 sizeof (TCP_HEAD),\r
1092 NET_BUF_TAIL\r
1093 );\r
1094\r
1095 ASSERT (Nhead != NULL);\r
1096\r
1097 Nbuf->Tcp = Nhead;\r
1098 Nhead->Flag = TCP_FLG_RST;\r
1099\r
1100 //\r
1101 // Derive Seq/ACK from the segment if no TCB\r
1102 // associated with it, otherwise from the Tcb\r
1103 //\r
1104 if (Tcb == NULL) {\r
1105\r
1106 if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {\r
1107 Nhead->Seq = Head->Ack;\r
1108 Nhead->Ack = 0;\r
1109 } else {\r
1110 Nhead->Seq = 0;\r
1111 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1112 Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);\r
1113 }\r
1114 } else {\r
1115\r
1116 Nhead->Seq = HTONL (Tcb->SndNxt);\r
1117 Nhead->Ack = HTONL (Tcb->RcvNxt);\r
1118 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1119 }\r
1120\r
1121 Nhead->SrcPort = Head->DstPort;\r
1122 Nhead->DstPort = Head->SrcPort;\r
c9325700 1123 Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2);\r
8a67d61d 1124 Nhead->Res = 0;\r
1125 Nhead->Wnd = HTONS (0xFFFF);\r
1126 Nhead->Checksum = 0;\r
1127 Nhead->Urg = 0;\r
1128\r
1129 HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0);\r
1130 Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);\r
1131\r
1132 TcpSendIpPacket (Tcb, Nbuf, Local, Remote);\r
1133\r
1134 NetbufFree (Nbuf);\r
1135 return 0;\r
1136}\r
1137\r
1138\r
1139/**\r
1140 Verify that the segment is in good shape.\r
1141\r
1142 @param Nbuf Buffer that contains the segment to be checked.\r
1143\r
1144 @retval 0 The segment is broken.\r
1145 @retval 1 The segment is in good shape.\r
1146\r
1147**/\r
1148INTN\r
1149TcpVerifySegment (\r
1150 IN NET_BUF *Nbuf\r
1151 )\r
1152{\r
1153 TCP_HEAD *Head;\r
1154 TCP_SEG *Seg;\r
1155 UINT32 Len;\r
1156\r
1157 if (Nbuf == NULL) {\r
1158 return 1;\r
1159 }\r
1160\r
1161 NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
1162\r
1163 Seg = TCPSEG_NETBUF (Nbuf);\r
1164 Len = Nbuf->TotalSize;\r
1165 Head = Nbuf->Tcp;\r
1166\r
1167 if (Head != NULL) {\r
1168 if (Head->Flag != Seg->Flag) {\r
1169 return 0;\r
1170 }\r
1171\r
1172 Len -= (Head->HeadLen << 2);\r
1173 }\r
1174\r
1175 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
1176 Len++;\r
1177 }\r
1178\r
1179 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
1180 Len++;\r
1181 }\r
1182\r
1183 if (Seg->Seq + Len != Seg->End) {\r
1184 return 0;\r
1185 }\r
1186\r
1187 return 1;\r
1188}\r
1189\r
1190\r
1191/**\r
1192 Verify that all the segments in SndQue are in good shape.\r
1193\r
1194 @param Head Pointer to the head node of the SndQue.\r
1195\r
1196 @retval 0 At least one segment is broken.\r
1197 @retval 1 All segments in the specific queue are in good shape.\r
1198\r
1199**/\r
1200INTN\r
1201TcpCheckSndQue (\r
e48e37fc 1202 IN LIST_ENTRY *Head\r
8a67d61d 1203 )\r
1204{\r
e48e37fc 1205 LIST_ENTRY *Entry;\r
8a67d61d 1206 NET_BUF *Nbuf;\r
1207 TCP_SEQNO Seq;\r
1208\r
e48e37fc 1209 if (IsListEmpty (Head)) {\r
8a67d61d 1210 return 1;\r
1211 }\r
1212 //\r
1213 // Initialize the Seq\r
1214 //\r
1215 Entry = Head->ForwardLink;\r
1216 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1217 Seq = TCPSEG_NETBUF (Nbuf)->Seq;\r
1218\r
1219 NET_LIST_FOR_EACH (Entry, Head) {\r
1220 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1221\r
1222 if (TcpVerifySegment (Nbuf) == 0) {\r
1223 return 0;\r
1224 }\r
1225\r
1226 //\r
1227 // All the node in the SndQue should has:\r
1228 // SEG.SEQ = LAST_SEG.END\r
1229 //\r
1230 if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {\r
1231 return 0;\r
1232 }\r
1233\r
1234 Seq = TCPSEG_NETBUF (Nbuf)->End;\r
1235 }\r
1236\r
1237 return 1;\r
1238}\r