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