]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c
MdeModulePkg: Add wnd scale check before shrinking window.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Tcp4Dxe / Tcp4Input.c
... / ...
CommitLineData
1/** @file\r
2 TCP input process routines.\r
3\r
4Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR>\r
5This 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
8http://opensource.org/licenses/bsd-license.php<BR>\r
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
13**/\r
14\r
15\r
16#include "Tcp4Main.h"\r
17\r
18\r
19/**\r
20 Check whether the sequence number of the incoming segment is acceptable.\r
21\r
22 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
23 @param Seg Pointer to the incoming segment.\r
24\r
25 @retval 1 The sequence number is acceptable.\r
26 @retval 0 The sequence number is not acceptable.\r
27\r
28**/\r
29INTN\r
30TcpSeqAcceptable (\r
31 IN TCP_CB *Tcb,\r
32 IN TCP_SEG *Seg\r
33 )\r
34{\r
35 return (TCP_SEQ_LEQ (Tcb->RcvNxt, Seg->End) &&\r
36 TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd));\r
37}\r
38\r
39\r
40/**\r
41 NewReno fast recovery, RFC3782.\r
42\r
43 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
44 @param Seg Segment that triggers the fast recovery.\r
45\r
46**/\r
47VOID\r
48TcpFastRecover (\r
49 IN OUT TCP_CB *Tcb,\r
50 IN TCP_SEG *Seg\r
51 )\r
52{\r
53 UINT32 FlightSize;\r
54 UINT32 Acked;\r
55\r
56 //\r
57 // Step 1: Three duplicate ACKs and not in fast recovery\r
58 //\r
59 if (Tcb->CongestState != TCP_CONGEST_RECOVER) {\r
60\r
61 //\r
62 // Step 1A: Invoking fast retransmission.\r
63 //\r
64 FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
65\r
66 Tcb->Ssthresh = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss));\r
67 Tcb->Recover = Tcb->SndNxt;\r
68\r
69 Tcb->CongestState = TCP_CONGEST_RECOVER;\r
70 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
71\r
72 //\r
73 // Step 2: Entering fast retransmission\r
74 //\r
75 TcpRetransmit (Tcb, Tcb->SndUna);\r
76 Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss;\r
77\r
78 DEBUG ((EFI_D_NET, "TcpFastRecover: enter fast retransmission"\r
79 " for TCB %p, recover point is %d\n", Tcb, Tcb->Recover));\r
80 return;\r
81 }\r
82\r
83 //\r
84 // During fast recovery, execute Step 3, 4, 5 of RFC3782\r
85 //\r
86 if (Seg->Ack == Tcb->SndUna) {\r
87\r
88 //\r
89 // Step 3: Fast Recovery,\r
90 // If this is a duplicated ACK, increse Cwnd by SMSS.\r
91 //\r
92\r
93 // Step 4 is skipped here only to be executed later\r
94 // by TcpToSendData\r
95 //\r
96 Tcb->CWnd += Tcb->SndMss;\r
97 DEBUG ((EFI_D_NET, "TcpFastRecover: received another"\r
98 " duplicated ACK (%d) for TCB %p\n", Seg->Ack, Tcb));\r
99\r
100 } else {\r
101\r
102 //\r
103 // New data is ACKed, check whether it is a\r
104 // full ACK or partial ACK\r
105 //\r
106 if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) {\r
107\r
108 //\r
109 // Step 5 - Full ACK:\r
110 // deflate the congestion window, and exit fast recovery\r
111 //\r
112 FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
113\r
114 Tcb->CWnd = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss);\r
115\r
116 Tcb->CongestState = TCP_CONGEST_OPEN;\r
117 DEBUG ((EFI_D_NET, "TcpFastRecover: received a full ACK(%d)"\r
118 " for TCB %p, exit fast recovery\n", Seg->Ack, Tcb));\r
119\r
120 } else {\r
121\r
122 //\r
123 // Step 5 - Partial ACK:\r
124 // fast retransmit the first unacknowledge field\r
125 // , then deflate the CWnd\r
126 //\r
127 TcpRetransmit (Tcb, Seg->Ack);\r
128 Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna);\r
129\r
130 //\r
131 // Deflate the CWnd by the amount of new data\r
132 // ACKed by SEG.ACK. If more than one SMSS data\r
133 // is ACKed, add back SMSS byte to CWnd after\r
134 //\r
135 if (Acked >= Tcb->SndMss) {\r
136 Acked -= Tcb->SndMss;\r
137\r
138 }\r
139\r
140 Tcb->CWnd -= Acked;\r
141\r
142 DEBUG ((EFI_D_NET, "TcpFastRecover: received a partial"\r
143 " ACK(%d) for TCB %p\n", Seg->Ack, Tcb));\r
144\r
145 }\r
146 }\r
147}\r
148\r
149\r
150/**\r
151 NewReno fast loss recovery, RFC3792.\r
152\r
153 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
154 @param Seg Segment that triggers the fast loss recovery.\r
155\r
156**/\r
157VOID\r
158TcpFastLossRecover (\r
159 IN OUT TCP_CB *Tcb,\r
160 IN TCP_SEG *Seg\r
161 )\r
162{\r
163 if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
164\r
165 //\r
166 // New data is ACKed, check whether it is a\r
167 // full ACK or partial ACK\r
168 //\r
169 if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) {\r
170\r
171 //\r
172 // Full ACK: exit the loss recovery.\r
173 //\r
174 Tcb->LossTimes = 0;\r
175 Tcb->CongestState = TCP_CONGEST_OPEN;\r
176\r
177 DEBUG ((EFI_D_NET, "TcpFastLossRecover: received a "\r
178 "full ACK(%d) for TCB %p\n", Seg->Ack, Tcb));\r
179\r
180 } else {\r
181\r
182 //\r
183 // Partial ACK:\r
184 // fast retransmit the first unacknowledge field.\r
185 //\r
186 TcpRetransmit (Tcb, Seg->Ack);\r
187 DEBUG ((EFI_D_NET, "TcpFastLossRecover: received a "\r
188 "partial ACK(%d) for TCB %p\n", Seg->Ack, Tcb));\r
189 }\r
190 }\r
191}\r
192\r
193\r
194/**\r
195 Compute the RTT as specified in RFC2988.\r
196\r
197 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
198 @param Measure Currently measured RTT in heart beats.\r
199\r
200**/\r
201VOID\r
202TcpComputeRtt (\r
203 IN OUT TCP_CB *Tcb,\r
204 IN UINT32 Measure\r
205 )\r
206{\r
207 INT32 Var;\r
208\r
209 //\r
210 // Step 2.3: Compute the RTO for subsequent RTT measurement.\r
211 //\r
212 if (Tcb->SRtt != 0) {\r
213\r
214 Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT);\r
215\r
216 if (Var < 0) {\r
217 Var = -Var;\r
218 }\r
219\r
220 Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2;\r
221 Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure;\r
222\r
223 } else {\r
224 //\r
225 // Step 2.2: compute the first RTT measure\r
226 //\r
227 Tcb->SRtt = Measure << TCP_RTT_SHIFT;\r
228 Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1);\r
229 }\r
230\r
231 Tcb->Rto = (Tcb->SRtt + MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT;\r
232\r
233 //\r
234 // Step 2.4: Limit the RTO to at least 1 second\r
235 // Step 2.5: Limit the RTO to a maxium value that\r
236 // is at least 60 second\r
237 //\r
238 if (Tcb->Rto < TCP_RTO_MIN) {\r
239 Tcb->Rto = TCP_RTO_MIN;\r
240\r
241 } else if (Tcb->Rto > TCP_RTO_MAX) {\r
242 Tcb->Rto = TCP_RTO_MAX;\r
243\r
244 }\r
245\r
246 DEBUG ((EFI_D_NET, "TcpComputeRtt: new RTT for TCB %p"\r
247 " computed SRTT: %d RTTVAR: %d RTO: %d\n",\r
248 Tcb, Tcb->SRtt, Tcb->RttVar, Tcb->Rto));\r
249\r
250}\r
251\r
252\r
253/**\r
254 Trim the data, SYN and FIN to fit into the window defined by Left and Right.\r
255\r
256 @param Nbuf Buffer that contains received TCP segment without IP header.\r
257 @param Left The sequence number of the window's left edge.\r
258 @param Right The sequence number of the window's right edge.\r
259\r
260**/\r
261VOID\r
262TcpTrimSegment (\r
263 IN NET_BUF *Nbuf,\r
264 IN TCP_SEQNO Left,\r
265 IN TCP_SEQNO Right\r
266 )\r
267{\r
268 TCP_SEG *Seg;\r
269 TCP_SEQNO Urg;\r
270 UINT32 Drop;\r
271\r
272 Seg = TCPSEG_NETBUF (Nbuf);\r
273\r
274 //\r
275 // If the segment is completely out of window,\r
276 // truncate every thing, include SYN and FIN.\r
277 //\r
278 if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) {\r
279\r
280 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);\r
281 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);\r
282\r
283 Seg->Seq = Seg->End;\r
284 NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD);\r
285 return;\r
286 }\r
287\r
288 //\r
289 // Adjust the buffer header\r
290 //\r
291 if (TCP_SEQ_LT (Seg->Seq, Left)) {\r
292\r
293 Drop = TCP_SUB_SEQ (Left, Seg->Seq);\r
294 Urg = Seg->Seq + Seg->Urg;\r
295 Seg->Seq = Left;\r
296\r
297 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
298 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);\r
299 Drop--;\r
300 }\r
301\r
302 //\r
303 // Adjust the urgent point\r
304 //\r
305 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) {\r
306\r
307 if (TCP_SEQ_LT (Urg, Seg->Seq)) {\r
308\r
309 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);\r
310 } else {\r
311 Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq);\r
312 }\r
313 }\r
314\r
315 if (Drop != 0) {\r
316 NetbufTrim (Nbuf, Drop, NET_BUF_HEAD);\r
317 }\r
318 }\r
319\r
320 //\r
321 // Adjust the buffer tail\r
322 //\r
323 if (TCP_SEQ_GT (Seg->End, Right)) {\r
324\r
325 Drop = TCP_SUB_SEQ (Seg->End, Right);\r
326 Seg->End = Right;\r
327\r
328 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
329 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);\r
330 Drop--;\r
331 }\r
332\r
333 if (Drop != 0) {\r
334 NetbufTrim (Nbuf, Drop, NET_BUF_TAIL);\r
335 }\r
336 }\r
337\r
338 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
339}\r
340\r
341\r
342/**\r
343 Trim off the data outside the tcb's receive window.\r
344\r
345 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
346 @param Nbuf Pointer to the NET_BUF containing the received tcp segment.\r
347\r
348**/\r
349VOID\r
350TcpTrimInWnd (\r
351 IN TCP_CB *Tcb,\r
352 IN NET_BUF *Nbuf\r
353 )\r
354{\r
355 TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd);\r
356}\r
357\r
358\r
359/**\r
360 Process the data and FIN flag, check whether to deliver\r
361 data to the socket layer.\r
362\r
363 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
364\r
365 @retval 0 No error occurred to deliver data.\r
366 @retval -1 Error condition occurred. Proper response is to reset the\r
367 connection.\r
368\r
369**/\r
370INTN\r
371TcpDeliverData (\r
372 IN OUT TCP_CB *Tcb\r
373 )\r
374{\r
375 LIST_ENTRY *Entry;\r
376 NET_BUF *Nbuf;\r
377 TCP_SEQNO Seq;\r
378 TCP_SEG *Seg;\r
379 UINT32 Urgent;\r
380\r
381 ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
382\r
383 //\r
384 // make sure there is some data queued,\r
385 // and TCP is in a proper state\r
386 //\r
387 if (IsListEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) {\r
388\r
389 return 0;\r
390 }\r
391\r
392 //\r
393 // Deliver data to the socket layer\r
394 //\r
395 Entry = Tcb->RcvQue.ForwardLink;\r
396 Seq = Tcb->RcvNxt;\r
397\r
398 while (Entry != &Tcb->RcvQue) {\r
399 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
400 Seg = TCPSEG_NETBUF (Nbuf);\r
401\r
402 ASSERT (TcpVerifySegment (Nbuf) != 0);\r
403 ASSERT (Nbuf->Tcp == NULL);\r
404\r
405 if (TCP_SEQ_GT (Seg->Seq, Seq)) {\r
406 break;\r
407 }\r
408\r
409 Entry = Entry->ForwardLink;\r
410 Seq = Seg->End;\r
411 Tcb->RcvNxt = Seq;\r
412\r
413 RemoveEntryList (&Nbuf->List);\r
414\r
415 //\r
416 // RFC793 Eighth step: process FIN in sequence\r
417 //\r
418 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
419\r
420 //\r
421 // The peer sends to us junky data after FIN,\r
422 // reset the connection.\r
423 //\r
424 if (!IsListEmpty (&Tcb->RcvQue)) {\r
425 DEBUG ((EFI_D_ERROR, "TcpDeliverData: data received after"\r
426 " FIN from peer of TCB %p, reset connection\n", Tcb));\r
427\r
428 NetbufFree (Nbuf);\r
429 return -1;\r
430 }\r
431\r
432 DEBUG ((EFI_D_NET, "TcpDeliverData: processing FIN "\r
433 "from peer of TCB %p\n", Tcb));\r
434\r
435 switch (Tcb->State) {\r
436 case TCP_SYN_RCVD:\r
437 case TCP_ESTABLISHED:\r
438\r
439 TcpSetState (Tcb, TCP_CLOSE_WAIT);\r
440 break;\r
441\r
442 case TCP_FIN_WAIT_1:\r
443\r
444 if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
445\r
446 TcpSetState (Tcb, TCP_CLOSING);\r
447 break;\r
448 }\r
449\r
450 //\r
451 // fall through\r
452 //\r
453 case TCP_FIN_WAIT_2:\r
454\r
455 TcpSetState (Tcb, TCP_TIME_WAIT);\r
456 TcpClearAllTimer (Tcb);\r
457\r
458 if (Tcb->TimeWaitTimeout != 0) {\r
459\r
460 TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
461 } else {\r
462\r
463 DEBUG ((EFI_D_WARN, "Connection closed immediately "\r
464 "because app disables TIME_WAIT timer for %p\n", Tcb));\r
465\r
466 TcpSendAck (Tcb);\r
467 TcpClose (Tcb);\r
468 }\r
469 break;\r
470\r
471 case TCP_CLOSE_WAIT:\r
472 case TCP_CLOSING:\r
473 case TCP_LAST_ACK:\r
474 case TCP_TIME_WAIT:\r
475 //\r
476 // The peer sends to us junk FIN byte. Discard\r
477 // the buffer then reset the connection\r
478 //\r
479 NetbufFree (Nbuf);\r
480 return -1;\r
481 default:\r
482 break;\r
483 }\r
484\r
485 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
486\r
487 Seg->End--;\r
488 }\r
489\r
490 //\r
491 // Don't delay the ack if PUSH flag is on.\r
492 //\r
493 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) {\r
494\r
495 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
496 }\r
497\r
498 if (Nbuf->TotalSize != 0) {\r
499 Urgent = 0;\r
500\r
501 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&\r
502 TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)) {\r
503\r
504 if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) {\r
505 Urgent = Nbuf->TotalSize;\r
506 } else {\r
507 Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1;\r
508 }\r
509 }\r
510\r
511 SockDataRcvd (Tcb->Sk, Nbuf, Urgent);\r
512 }\r
513\r
514 if (TCP_FIN_RCVD (Tcb->State)) {\r
515\r
516 SockNoMoreData (Tcb->Sk);\r
517 }\r
518\r
519 NetbufFree (Nbuf);\r
520 }\r
521\r
522 return 0;\r
523}\r
524\r
525\r
526/**\r
527 Store the data into the reassemble queue.\r
528\r
529 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
530 @param Nbuf Pointer to the buffer containing the data to be queued.\r
531\r
532**/\r
533VOID\r
534TcpQueueData (\r
535 IN OUT TCP_CB *Tcb,\r
536 IN NET_BUF *Nbuf\r
537 )\r
538{\r
539 TCP_SEG *Seg;\r
540 LIST_ENTRY *Head;\r
541 LIST_ENTRY *Prev;\r
542 LIST_ENTRY *Cur;\r
543 NET_BUF *Node;\r
544\r
545 ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL));\r
546\r
547 NET_GET_REF (Nbuf);\r
548\r
549 Seg = TCPSEG_NETBUF (Nbuf);\r
550 Head = &Tcb->RcvQue;\r
551\r
552 //\r
553 // Fast path to process normal case. That is,\r
554 // no out-of-order segments are received.\r
555 //\r
556 if (IsListEmpty (Head)) {\r
557\r
558 InsertTailList (Head, &Nbuf->List);\r
559 return;\r
560 }\r
561\r
562 //\r
563 // Find the point to insert the buffer\r
564 //\r
565 for (Prev = Head, Cur = Head->ForwardLink;\r
566 Cur != Head;\r
567 Prev = Cur, Cur = Cur->ForwardLink) {\r
568\r
569 Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
570\r
571 if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) {\r
572 break;\r
573 }\r
574 }\r
575\r
576 //\r
577 // Check whether the current segment overlaps with the\r
578 // previous segment.\r
579 //\r
580 if (Prev != Head) {\r
581 Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);\r
582\r
583 if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) {\r
584\r
585 if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) {\r
586\r
587 NetbufFree (Nbuf);\r
588 return;\r
589 }\r
590\r
591 TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End);\r
592 }\r
593 }\r
594\r
595 InsertHeadList (Prev, &Nbuf->List);\r
596\r
597 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
598\r
599 //\r
600 // Check the segments after the insert point.\r
601 //\r
602 while (Cur != Head) {\r
603 Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
604\r
605 if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) {\r
606\r
607 Cur = Cur->ForwardLink;\r
608\r
609 RemoveEntryList (&Node->List);\r
610 NetbufFree (Node);\r
611 continue;\r
612 }\r
613\r
614 if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) {\r
615\r
616 if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) {\r
617\r
618 RemoveEntryList (&Nbuf->List);\r
619 NetbufFree (Nbuf);\r
620 return ;\r
621 }\r
622\r
623 TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq);\r
624 break;\r
625 }\r
626\r
627 Cur = Cur->ForwardLink;\r
628 }\r
629}\r
630\r
631\r
632/**\r
633 Ajust the send queue or the retransmit queue.\r
634\r
635 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
636 @param Ack The acknowledge seuqence number of the received segment.\r
637\r
638**/\r
639VOID\r
640TcpAdjustSndQue (\r
641 IN TCP_CB *Tcb,\r
642 IN TCP_SEQNO Ack\r
643 )\r
644{\r
645 LIST_ENTRY *Head;\r
646 LIST_ENTRY *Cur;\r
647 NET_BUF *Node;\r
648 TCP_SEG *Seg;\r
649\r
650 Head = &Tcb->SndQue;\r
651 Cur = Head->ForwardLink;\r
652\r
653 while (Cur != Head) {\r
654 Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
655 Seg = TCPSEG_NETBUF (Node);\r
656\r
657 if (TCP_SEQ_GEQ (Seg->Seq, Ack)) {\r
658 break;\r
659 }\r
660\r
661 //\r
662 // Remove completely ACKed segments\r
663 //\r
664 if (TCP_SEQ_LEQ (Seg->End, Ack)) {\r
665 Cur = Cur->ForwardLink;\r
666\r
667 RemoveEntryList (&Node->List);\r
668 NetbufFree (Node);\r
669 continue;\r
670 }\r
671\r
672 TcpTrimSegment (Node, Ack, Seg->End);\r
673 break;\r
674 }\r
675}\r
676\r
677\r
678/**\r
679 Process the received TCP segments.\r
680\r
681 @param Nbuf Buffer that contains received TCP segment without IP header.\r
682 @param Src Source address of the segment, or the peer's IP address.\r
683 @param Dst Destination address of the segment, or the local end's IP\r
684 address.\r
685\r
686 @retval 0 Segment is processed successfully. It is either accepted or\r
687 discarded. But no connection is reset by the segment.\r
688 @retval -1 A connection is reset by the segment.\r
689\r
690**/\r
691INTN\r
692TcpInput (\r
693 IN NET_BUF *Nbuf,\r
694 IN UINT32 Src,\r
695 IN UINT32 Dst\r
696 )\r
697{\r
698 TCP_CB *Tcb;\r
699 TCP_CB *Parent;\r
700 TCP_OPTION Option;\r
701 TCP_HEAD *Head;\r
702 INT32 Len;\r
703 TCP_SEG *Seg;\r
704 TCP_SEQNO Right;\r
705 TCP_SEQNO Urg;\r
706 INT32 Usable;\r
707 \r
708 NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
709\r
710 Parent = NULL;\r
711 Tcb = NULL;\r
712\r
713 Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
714 ASSERT (Head != NULL);\r
715\r
716 if (Nbuf->TotalSize < sizeof (TCP_HEAD)) {\r
717 DEBUG ((EFI_D_NET, "TcpInput: received a malformed packet\n"));\r
718 goto DISCARD;\r
719 }\r
720 \r
721 Len = Nbuf->TotalSize - (Head->HeadLen << 2);\r
722\r
723 if ((Head->HeadLen < 5) || (Len < 0) ||\r
724 (TcpChecksum (Nbuf, NetPseudoHeadChecksum (Src, Dst, 6, 0)) != 0)) {\r
725\r
726 DEBUG ((EFI_D_NET, "TcpInput: received a malformed packet\n"));\r
727 goto DISCARD;\r
728 }\r
729\r
730 if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) {\r
731 Len++;\r
732 }\r
733\r
734 if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) {\r
735 Len++;\r
736 }\r
737\r
738 Tcb = TcpLocateTcb (\r
739 Head->DstPort,\r
740 Dst,\r
741 Head->SrcPort,\r
742 Src,\r
743 (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)\r
744 );\r
745\r
746 if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) {\r
747 DEBUG ((EFI_D_NET, "TcpInput: send reset because no TCB found\n"));\r
748\r
749 Tcb = NULL;\r
750 goto SEND_RESET;\r
751 }\r
752\r
753 Seg = TcpFormatNetbuf (Tcb, Nbuf);\r
754\r
755 //\r
756 // RFC1122 recommended reaction to illegal option\r
757 // (in fact, an illegal option length) is reset.\r
758 //\r
759 if (TcpParseOption (Nbuf->Tcp, &Option) == -1) {\r
760 DEBUG ((EFI_D_ERROR, "TcpInput: reset the peer because"\r
761 " of malformed option for TCB %p\n", Tcb));\r
762\r
763 goto SEND_RESET;\r
764 }\r
765\r
766 //\r
767 // From now on, the segment is headless\r
768 //\r
769 NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD);\r
770 Nbuf->Tcp = NULL;\r
771\r
772 //\r
773 // Process the segment in LISTEN state.\r
774 //\r
775 if (Tcb->State == TCP_LISTEN) {\r
776 //\r
777 // First step: Check RST\r
778 //\r
779 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
780 DEBUG ((EFI_D_WARN, "TcpInput: discard a reset segment "\r
781 "for TCB %p in listening\n", Tcb));\r
782\r
783 goto DISCARD;\r
784 }\r
785\r
786 //\r
787 // Second step: Check ACK.\r
788 // Any ACK sent to TCP in LISTEN is reseted.\r
789 //\r
790 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
791 DEBUG ((EFI_D_WARN, "TcpInput: send reset because of"\r
792 " segment with ACK for TCB %p in listening\n", Tcb));\r
793\r
794 goto SEND_RESET;\r
795 }\r
796\r
797 //\r
798 // Third step: Check SYN\r
799 //\r
800 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
801 //\r
802 // create a child TCB to handle the data\r
803 //\r
804 Parent = Tcb;\r
805\r
806 Tcb = TcpCloneTcb (Parent);\r
807 if (Tcb == NULL) {\r
808 DEBUG ((EFI_D_ERROR, "TcpInput: discard a segment because"\r
809 " failed to clone a child for TCB %p\n", Tcb));\r
810\r
811 goto DISCARD;\r
812 }\r
813\r
814 DEBUG ((EFI_D_NET, "TcpInput: create a child for TCB %p"\r
815 " in listening\n", Tcb));\r
816\r
817 //\r
818 // init the TCB structure\r
819 //\r
820 Tcb->LocalEnd.Ip = Dst;\r
821 Tcb->LocalEnd.Port = Head->DstPort;\r
822 Tcb->RemoteEnd.Ip = Src;\r
823 Tcb->RemoteEnd.Port = Head->SrcPort;\r
824\r
825 TcpInitTcbLocal (Tcb);\r
826 TcpInitTcbPeer (Tcb, Seg, &Option);\r
827\r
828 TcpSetState (Tcb, TCP_SYN_RCVD);\r
829 TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
830 TcpTrimInWnd (Tcb, Nbuf);\r
831\r
832 goto StepSix;\r
833 }\r
834\r
835 goto DISCARD;\r
836\r
837 } else if (Tcb->State == TCP_SYN_SENT) {\r
838 //\r
839 // First step: Check ACK bit\r
840 //\r
841 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) {\r
842\r
843 DEBUG ((EFI_D_WARN, "TcpInput: send reset because of "\r
844 "wrong ACK received for TCB %p in SYN_SENT\n", Tcb));\r
845\r
846 goto SEND_RESET;\r
847 }\r
848\r
849 //\r
850 // Second step: Check RST bit\r
851 //\r
852 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
853\r
854 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
855\r
856 DEBUG ((EFI_D_WARN, "TcpInput: connection reset by"\r
857 " peer for TCB %p in SYN_SENT\n", Tcb));\r
858\r
859 SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
860 goto DROP_CONNECTION;\r
861 } else {\r
862\r
863 DEBUG ((EFI_D_WARN, "TcpInput: discard a reset segment "\r
864 "because of no ACK for TCB %p in SYN_SENT\n", Tcb));\r
865\r
866 goto DISCARD;\r
867 }\r
868 }\r
869\r
870 //\r
871 // Third step: Check security and precedence. Skipped\r
872 //\r
873\r
874 //\r
875 // Fourth step: Check SYN. Pay attention to simultaneous open\r
876 //\r
877 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
878\r
879 TcpInitTcbPeer (Tcb, Seg, &Option);\r
880\r
881 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
882\r
883 Tcb->SndUna = Seg->Ack;\r
884 }\r
885\r
886 TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
887\r
888 if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) {\r
889\r
890 TcpSetState (Tcb, TCP_ESTABLISHED);\r
891\r
892 TcpClearTimer (Tcb, TCP_TIMER_CONNECT);\r
893 TcpDeliverData (Tcb);\r
894\r
895 if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&\r
896 TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
897\r
898 TcpComputeRtt (Tcb, Tcb->RttMeasure);\r
899 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
900 }\r
901\r
902 TcpTrimInWnd (Tcb, Nbuf);\r
903\r
904 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
905\r
906 DEBUG ((EFI_D_NET, "TcpInput: connection established"\r
907 " for TCB %p in SYN_SENT\n", Tcb));\r
908\r
909 goto StepSix;\r
910 } else {\r
911 //\r
912 // Received a SYN segment without ACK, simultaneous open.\r
913 //\r
914 TcpSetState (Tcb, TCP_SYN_RCVD);\r
915\r
916 ASSERT (Tcb->SndNxt == Tcb->Iss + 1);\r
917 TcpAdjustSndQue (Tcb, Tcb->SndNxt);\r
918\r
919 TcpTrimInWnd (Tcb, Nbuf);\r
920\r
921 DEBUG ((EFI_D_WARN, "TcpInput: simultaneous open "\r
922 "for TCB %p in SYN_SENT\n", Tcb));\r
923\r
924 goto StepSix;\r
925 }\r
926 }\r
927\r
928 goto DISCARD;\r
929 }\r
930\r
931 //\r
932 // Process segment in SYN_RCVD or TCP_CONNECTED states\r
933 //\r
934\r
935 //\r
936 // Clear probe timer since the RecvWindow is opened.\r
937 //\r
938 if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) {\r
939 TcpClearTimer (Tcb, TCP_TIMER_PROBE);\r
940 Tcb->ProbeTimerOn = FALSE;\r
941 }\r
942\r
943 //\r
944 // First step: Check whether SEG.SEQ is acceptable\r
945 //\r
946 if (TcpSeqAcceptable (Tcb, Seg) == 0) {\r
947 DEBUG ((EFI_D_WARN, "TcpInput: sequence acceptance"\r
948 " test failed for segment of TCB %p\n", Tcb));\r
949\r
950 if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
951 TcpSendAck (Tcb);\r
952 }\r
953\r
954 goto DISCARD;\r
955 }\r
956\r
957 if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) &&\r
958 (Tcb->RcvWl2 == Seg->End) &&\r
959 !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)) {\r
960\r
961 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
962 }\r
963\r
964 //\r
965 // Second step: Check the RST\r
966 //\r
967 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {\r
968\r
969 DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\n", Tcb));\r
970\r
971 if (Tcb->State == TCP_SYN_RCVD) {\r
972\r
973 SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED);\r
974\r
975 //\r
976 // This TCB comes from either a LISTEN TCB,\r
977 // or active open TCB with simultanous open.\r
978 // Do NOT signal user CONNECTION refused\r
979 // if it comes from a LISTEN TCB.\r
980 //\r
981 } else if ((Tcb->State == TCP_ESTABLISHED) ||\r
982 (Tcb->State == TCP_FIN_WAIT_1) ||\r
983 (Tcb->State == TCP_FIN_WAIT_2) ||\r
984 (Tcb->State == TCP_CLOSE_WAIT)) {\r
985\r
986 SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
987\r
988 } else {\r
989\r
990 }\r
991\r
992 goto DROP_CONNECTION;\r
993 }\r
994\r
995 //\r
996 // Trim the data and flags.\r
997 //\r
998 TcpTrimInWnd (Tcb, Nbuf);\r
999\r
1000 //\r
1001 // Third step: Check security and precedence, Ignored\r
1002 //\r
1003\r
1004 //\r
1005 // Fourth step: Check the SYN bit.\r
1006 //\r
1007 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
1008\r
1009 DEBUG ((EFI_D_WARN, "TcpInput: connection reset "\r
1010 "because received extra SYN for TCB %p\n", Tcb));\r
1011\r
1012 SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);\r
1013 goto RESET_THEN_DROP;\r
1014 }\r
1015\r
1016 //\r
1017 // Fifth step: Check the ACK\r
1018 //\r
1019 if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
1020 DEBUG ((EFI_D_WARN, "TcpInput: segment discard because"\r
1021 " of no ACK for connected TCB %p\n", Tcb));\r
1022\r
1023 goto DISCARD;\r
1024\r
1025 }\r
1026\r
1027 if (Tcb->State == TCP_SYN_RCVD) {\r
1028\r
1029 if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) &&\r
1030 TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) {\r
1031\r
1032 Tcb->SndWnd = Seg->Wnd;\r
1033 Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax);\r
1034 Tcb->SndWl1 = Seg->Seq;\r
1035 Tcb->SndWl2 = Seg->Ack;\r
1036 TcpSetState (Tcb, TCP_ESTABLISHED);\r
1037\r
1038 TcpClearTimer (Tcb, TCP_TIMER_CONNECT);\r
1039 TcpDeliverData (Tcb);\r
1040\r
1041 DEBUG ((EFI_D_NET, "TcpInput: connection established "\r
1042 " for TCB %p in SYN_RCVD\n", Tcb));\r
1043\r
1044 //\r
1045 // Continue the process as ESTABLISHED state\r
1046 //\r
1047 } else {\r
1048 DEBUG ((EFI_D_WARN, "TcpInput: send reset because of"\r
1049 " wrong ACK for TCB %p in SYN_RCVD\n", Tcb));\r
1050\r
1051 goto SEND_RESET;\r
1052 }\r
1053 }\r
1054\r
1055 if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) {\r
1056\r
1057 DEBUG ((EFI_D_WARN, "TcpInput: ignore the out-of-data"\r
1058 " ACK for connected TCB %p\n", Tcb));\r
1059\r
1060 goto StepSix;\r
1061\r
1062 } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) {\r
1063\r
1064 DEBUG ((EFI_D_WARN, "TcpInput: discard segment for "\r
1065 "future ACK for connected TCB %p\n", Tcb));\r
1066\r
1067 TcpSendAck (Tcb);\r
1068 goto DISCARD;\r
1069 }\r
1070\r
1071 //\r
1072 // From now on: SND.UNA <= SEG.ACK <= SND.NXT.\r
1073 //\r
1074 if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) {\r
1075 //\r
1076 // update TsRecent as specified in page 16 RFC1323.\r
1077 // RcvWl2 equals to the variable "LastAckSent"\r
1078 // defined there.\r
1079 //\r
1080 if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) &&\r
1081 TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) {\r
1082\r
1083 Tcb->TsRecent = Option.TSVal;\r
1084 Tcb->TsRecentAge = mTcpTick;\r
1085 }\r
1086\r
1087 TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr));\r
1088\r
1089 } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
1090\r
1091 ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN);\r
1092\r
1093 TcpComputeRtt (Tcb, Tcb->RttMeasure);\r
1094 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
1095 }\r
1096\r
1097 if (Seg->Ack == Tcb->SndNxt) {\r
1098\r
1099 TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
1100 } else {\r
1101\r
1102 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
1103 }\r
1104\r
1105 //\r
1106 // Count duplicate acks.\r
1107 //\r
1108 if ((Seg->Ack == Tcb->SndUna) &&\r
1109 (Tcb->SndUna != Tcb->SndNxt) &&\r
1110 (Seg->Wnd == Tcb->SndWnd) &&\r
1111 (0 == Len)) {\r
1112\r
1113 Tcb->DupAck++;\r
1114 } else {\r
1115\r
1116 Tcb->DupAck = 0;\r
1117 }\r
1118\r
1119 //\r
1120 // Congestion avoidance, fast recovery and fast retransmission.\r
1121 //\r
1122 if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) ||\r
1123 (Tcb->CongestState == TCP_CONGEST_LOSS)) {\r
1124\r
1125 if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
1126\r
1127 if (Tcb->CWnd < Tcb->Ssthresh) {\r
1128\r
1129 Tcb->CWnd += Tcb->SndMss;\r
1130 } else {\r
1131\r
1132 Tcb->CWnd += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1);\r
1133 }\r
1134\r
1135 Tcb->CWnd = MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale);\r
1136 }\r
1137\r
1138 if (Tcb->CongestState == TCP_CONGEST_LOSS) {\r
1139 TcpFastLossRecover (Tcb, Seg);\r
1140 }\r
1141 } else {\r
1142\r
1143 TcpFastRecover (Tcb, Seg);\r
1144 }\r
1145\r
1146 if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {\r
1147\r
1148 TcpAdjustSndQue (Tcb, Seg->Ack);\r
1149 Tcb->SndUna = Seg->Ack;\r
1150\r
1151 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&\r
1152 TCP_SEQ_LT (Tcb->SndUp, Seg->Ack)) {\r
1153\r
1154 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);\r
1155 }\r
1156 }\r
1157\r
1158 //\r
1159 // Update window info\r
1160 //\r
1161 if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) ||\r
1162 ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))) {\r
1163\r
1164 Right = Seg->Ack + Seg->Wnd;\r
1165\r
1166 if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) {\r
1167\r
1168 if ((Tcb->SndWl1 == Seg->Seq) &&\r
1169 (Tcb->SndWl2 == Seg->Ack) &&\r
1170 (Len == 0)) {\r
1171\r
1172 goto NO_UPDATE;\r
1173 }\r
1174\r
1175 DEBUG ((EFI_D_WARN, "TcpInput: peer shrinks the"\r
1176 " window for connected TCB %p\n", Tcb));\r
1177\r
1178 if ((Tcb->CongestState == TCP_CONGEST_RECOVER) &&\r
1179 (TCP_SEQ_LT (Right, Tcb->Recover))) {\r
1180\r
1181 Tcb->Recover = Right;\r
1182 }\r
1183\r
1184 if ((Tcb->CongestState == TCP_CONGEST_LOSS) &&\r
1185 (TCP_SEQ_LT (Right, Tcb->LossRecover))) {\r
1186\r
1187 Tcb->LossRecover = Right;\r
1188 }\r
1189\r
1190 if (TCP_SEQ_LT (Right, Tcb->SndNxt)) {\r
1191 //\r
1192 // Check for Window Retraction in RFC7923 section 2.4.\r
1193 // The lower n bits of the peer's actual receive window is wiped out if TCP\r
1194 // window scale is enabled, it will look like the peer is shrinking the window.\r
1195 // Check whether the SndNxt is out of the advertised receive window by more than\r
1196 // 2^Rcv.Wind.Shift before moving the SndNxt to the left.\r
1197 //\r
1198 DEBUG (\r
1199 (EFI_D_WARN,\r
1200 "TcpInput: peer advise negative useable window for connected TCB %p\n",\r
1201 Tcb)\r
1202 );\r
1203 Usable = TCP_SUB_SEQ (Tcb->SndNxt, Right);\r
1204 if ((Usable >> Tcb->SndWndScale) > 0) {\r
1205 DEBUG (\r
1206 (EFI_D_WARN,\r
1207 "TcpInput: SndNxt is out of window by more than window scale for TCB %p\n",\r
1208 Tcb)\r
1209 );\r
1210 Tcb->SndNxt = Right;\r
1211 }\r
1212 if (Right == Tcb->SndUna) {\r
1213\r
1214 TcpClearTimer (Tcb, TCP_TIMER_REXMIT);\r
1215 TcpSetProbeTimer (Tcb);\r
1216 }\r
1217 }\r
1218 }\r
1219\r
1220 Tcb->SndWnd = Seg->Wnd;\r
1221 Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax);\r
1222 Tcb->SndWl1 = Seg->Seq;\r
1223 Tcb->SndWl2 = Seg->Ack;\r
1224 }\r
1225\r
1226NO_UPDATE:\r
1227\r
1228 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) &&\r
1229 (Tcb->SndUna == Tcb->SndNxt)) {\r
1230\r
1231 DEBUG ((EFI_D_NET, "TcpInput: local FIN is ACKed by"\r
1232 " peer for connected TCB %p\n", Tcb));\r
1233\r
1234 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED);\r
1235 }\r
1236\r
1237 //\r
1238 // Transit the state if proper.\r
1239 //\r
1240 switch (Tcb->State) {\r
1241 case TCP_FIN_WAIT_1:\r
1242\r
1243 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
1244\r
1245 TcpSetState (Tcb, TCP_FIN_WAIT_2);\r
1246\r
1247 TcpClearAllTimer (Tcb);\r
1248 TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout);\r
1249 }\r
1250\r
1251 case TCP_FIN_WAIT_2:\r
1252\r
1253 break;\r
1254\r
1255 case TCP_CLOSE_WAIT:\r
1256 break;\r
1257\r
1258 case TCP_CLOSING:\r
1259\r
1260 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
1261\r
1262 TcpSetState (Tcb, TCP_TIME_WAIT);\r
1263\r
1264 TcpClearAllTimer (Tcb);\r
1265\r
1266 if (Tcb->TimeWaitTimeout != 0) {\r
1267\r
1268 TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
1269 } else {\r
1270\r
1271 DEBUG ((EFI_D_WARN, "Connection closed immediately "\r
1272 "because app disables TIME_WAIT timer for %p\n", Tcb));\r
1273\r
1274 TcpClose (Tcb);\r
1275 }\r
1276 }\r
1277 break;\r
1278\r
1279 case TCP_LAST_ACK:\r
1280\r
1281 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {\r
1282\r
1283 TcpSetState (Tcb, TCP_CLOSED);\r
1284 }\r
1285\r
1286 break;\r
1287\r
1288 case TCP_TIME_WAIT:\r
1289\r
1290 TcpSendAck (Tcb);\r
1291\r
1292 if (Tcb->TimeWaitTimeout != 0) {\r
1293\r
1294 TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);\r
1295 } else {\r
1296\r
1297 DEBUG ((EFI_D_WARN, "Connection closed immediately "\r
1298 "because app disables TIME_WAIT timer for %p\n", Tcb));\r
1299\r
1300 TcpClose (Tcb);\r
1301 }\r
1302 break;\r
1303\r
1304 default:\r
1305 break;\r
1306 }\r
1307\r
1308 //\r
1309 // Sixth step: Check the URG bit.update the Urg point\r
1310 // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact.\r
1311 //\r
1312StepSix:\r
1313\r
1314 Tcb->Idle = 0;\r
1315 TcpSetKeepaliveTimer (Tcb);\r
1316\r
1317 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) &&\r
1318 !TCP_FIN_RCVD (Tcb->State)) \r
1319 {\r
1320\r
1321 DEBUG ((EFI_D_NET, "TcpInput: received urgent data "\r
1322 "from peer for connected TCB %p\n", Tcb));\r
1323\r
1324 Urg = Seg->Seq + Seg->Urg;\r
1325\r
1326 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&\r
1327 TCP_SEQ_GT (Urg, Tcb->RcvUp)) {\r
1328\r
1329 Tcb->RcvUp = Urg;\r
1330 } else {\r
1331\r
1332 Tcb->RcvUp = Urg;\r
1333 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG);\r
1334 }\r
1335 }\r
1336\r
1337 //\r
1338 // Seventh step: Process the segment data\r
1339 //\r
1340 if (Seg->End != Seg->Seq) {\r
1341\r
1342 if (TCP_FIN_RCVD (Tcb->State)) {\r
1343\r
1344 DEBUG ((EFI_D_WARN, "TcpInput: connection reset because"\r
1345 " data is lost for connected TCB %p\n", Tcb));\r
1346\r
1347 goto RESET_THEN_DROP;\r
1348 }\r
1349\r
1350 if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) {\r
1351 DEBUG ((EFI_D_WARN, "TcpInput: connection reset because"\r
1352 " data is lost for connected TCB %p\n", Tcb));\r
1353\r
1354 goto RESET_THEN_DROP;\r
1355 }\r
1356\r
1357 TcpQueueData (Tcb, Nbuf);\r
1358 if (TcpDeliverData (Tcb) == -1) {\r
1359 goto RESET_THEN_DROP;\r
1360 }\r
1361\r
1362 if (!IsListEmpty (&Tcb->RcvQue)) {\r
1363 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
1364 }\r
1365 }\r
1366\r
1367 //\r
1368 // Eighth step: check the FIN.\r
1369 // This step is moved to TcpDeliverData. FIN will be\r
1370 // processed in sequence there. Check the comments in\r
1371 // the beginning of the file header for information.\r
1372 //\r
1373\r
1374 //\r
1375 // Tcb is a new child of the listening Parent,\r
1376 // commit it.\r
1377 //\r
1378 if (Parent != NULL) {\r
1379 Tcb->Parent = Parent;\r
1380 TcpInsertTcb (Tcb);\r
1381 }\r
1382\r
1383 if ((Tcb->State != TCP_CLOSED) &&\r
1384 (TcpToSendData (Tcb, 0) == 0) &&\r
1385 (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0))) {\r
1386\r
1387 TcpToSendAck (Tcb);\r
1388 }\r
1389\r
1390 NetbufFree (Nbuf);\r
1391 return 0;\r
1392\r
1393RESET_THEN_DROP:\r
1394 TcpSendReset (Tcb, Head, Len, Dst, Src);\r
1395\r
1396DROP_CONNECTION:\r
1397 ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL));\r
1398\r
1399 NetbufFree (Nbuf);\r
1400 TcpClose (Tcb);\r
1401\r
1402 return -1;\r
1403\r
1404SEND_RESET:\r
1405\r
1406 TcpSendReset (Tcb, Head, Len, Dst, Src);\r
1407\r
1408DISCARD:\r
1409\r
1410 //\r
1411 // Tcb is a child of Parent, and it doesn't survive\r
1412 //\r
1413 DEBUG ((EFI_D_WARN, "Tcp4Input: Discard a packet\n"));\r
1414 NetbufFree (Nbuf);\r
1415\r
1416 if ((Parent != NULL) && (Tcb != NULL)) {\r
1417\r
1418 ASSERT (Tcb->Sk != NULL);\r
1419 TcpClose (Tcb);\r
1420 }\r
1421\r
1422 return 0;\r
1423}\r
1424\r
1425\r
1426/**\r
1427 Process the received ICMP error messages for TCP.\r
1428\r
1429 @param Nbuf Buffer that contains part of the TCP segment without IP header\r
1430 truncated from the ICMP error packet.\r
1431 @param IcmpErr The ICMP error code interpreted from ICMP error packet.\r
1432 @param Src Source address of the ICMP error message.\r
1433 @param Dst Destination address of the ICMP error message.\r
1434\r
1435**/\r
1436VOID\r
1437TcpIcmpInput (\r
1438 IN NET_BUF *Nbuf,\r
1439 IN UINT8 IcmpErr,\r
1440 IN UINT32 Src,\r
1441 IN UINT32 Dst\r
1442 )\r
1443{\r
1444 TCP_HEAD *Head;\r
1445 TCP_CB *Tcb;\r
1446 TCP_SEQNO Seq;\r
1447 EFI_STATUS IcmpErrStatus;\r
1448 BOOLEAN IcmpErrIsHard;\r
1449 BOOLEAN IcmpErrNotify;\r
1450\r
1451 if (Nbuf->TotalSize < sizeof (TCP_HEAD)) {\r
1452 goto CLEAN_EXIT;\r
1453 }\r
1454\r
1455 Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
1456 ASSERT (Head != NULL);\r
1457 Tcb = TcpLocateTcb (\r
1458 Head->DstPort,\r
1459 Dst,\r
1460 Head->SrcPort,\r
1461 Src,\r
1462 FALSE\r
1463 );\r
1464 if (Tcb == NULL || Tcb->State == TCP_CLOSED) {\r
1465\r
1466 goto CLEAN_EXIT;\r
1467 }\r
1468\r
1469 //\r
1470 // Validate the sequence number.\r
1471 //\r
1472 Seq = NTOHL (Head->Seq);\r
1473 if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) {\r
1474\r
1475 goto CLEAN_EXIT;\r
1476 }\r
1477\r
1478 IcmpErrStatus = IpIoGetIcmpErrStatus (\r
1479 IcmpErr,\r
1480 IP_VERSION_4,\r
1481 &IcmpErrIsHard,\r
1482 &IcmpErrNotify\r
1483 );\r
1484\r
1485 if (IcmpErrNotify) {\r
1486\r
1487 SOCK_ERROR (Tcb->Sk, IcmpErrStatus);\r
1488 }\r
1489\r
1490 if (IcmpErrIsHard) {\r
1491\r
1492 TcpClose (Tcb);\r
1493 }\r
1494\r
1495CLEAN_EXIT:\r
1496 NetbufFree (Nbuf);\r
1497}\r