fdd64ef9f46c0d23f353e8ec659129c8222496b5
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Universal / Network / Tcp4Dxe / Tcp4Misc.c
1 /** @file\r
2 \r
3 Copyright (c) 2005 - 2006, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 Module Name:\r
13 \r
14   Tcp4Misc.c\r
15 \r
16 Abstract:\r
17 \r
18   Misc support routines for tcp.\r
19 \r
20 \r
21 **/\r
22 \r
23 \r
24 #include "Tcp4Main.h"\r
25 \r
26 NET_LIST_ENTRY  mTcpRunQue = {\r
27   &mTcpRunQue,\r
28   &mTcpRunQue\r
29 };\r
30 \r
31 NET_LIST_ENTRY  mTcpListenQue = {\r
32   &mTcpListenQue,\r
33   &mTcpListenQue\r
34 };\r
35 \r
36 TCP_SEQNO       mTcpGlobalIss = 0x4d7e980b;\r
37 \r
38 CHAR16   *mTcpStateName[] = {\r
39   L"TCP_CLOSED",\r
40   L"TCP_LISTEN",\r
41   L"TCP_SYN_SENT",\r
42   L"TCP_SYN_RCVD",\r
43   L"TCP_ESTABLISHED",\r
44   L"TCP_FIN_WAIT_1",\r
45   L"TCP_FIN_WAIT_2",\r
46   L"TCP_CLOSING",\r
47   L"TCP_TIME_WAIT",\r
48   L"TCP_CLOSE_WAIT",\r
49   L"TCP_LAST_ACK"\r
50 };\r
51 \r
52 \r
53 /**\r
54   Initialize the Tcb local related members.\r
55 \r
56   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
57 \r
58   @return None\r
59 \r
60 **/\r
61 VOID\r
62 TcpInitTcbLocal (\r
63   IN TCP_CB *Tcb\r
64   )\r
65 {\r
66   //\r
67   // Compute the checksum of the fixed parts of pseudo header\r
68   //\r
69   Tcb->HeadSum = NetPseudoHeadChecksum (\r
70                   Tcb->LocalEnd.Ip,\r
71                   Tcb->RemoteEnd.Ip,\r
72                   0x06,\r
73                   0\r
74                   );\r
75 \r
76   Tcb->Iss    = TcpGetIss ();\r
77   Tcb->SndUna = Tcb->Iss;\r
78   Tcb->SndNxt = Tcb->Iss;\r
79 \r
80   Tcb->SndWl2 = Tcb->Iss;\r
81   Tcb->SndWnd = 536;\r
82 \r
83   Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
84 \r
85   //\r
86   // Fisrt window size is never scaled\r
87   //\r
88   Tcb->RcvWndScale = 0;\r
89 }\r
90 \r
91 \r
92 /**\r
93   Initialize the peer related members.\r
94 \r
95   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
96   @param  Seg                   Pointer to the segment that contains the peer's\r
97                                 intial info.\r
98   @param  Opt                   Pointer to the options announced by the peer.\r
99 \r
100   @return None\r
101 \r
102 **/\r
103 VOID\r
104 TcpInitTcbPeer (\r
105   IN TCP_CB     *Tcb,\r
106   IN TCP_SEG    *Seg,\r
107   IN TCP_OPTION *Opt\r
108   )\r
109 {\r
110   UINT16  RcvMss;\r
111 \r
112   ASSERT (Tcb && Seg && Opt);\r
113   ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));\r
114 \r
115   Tcb->SndWnd     = Seg->Wnd;\r
116   Tcb->SndWndMax  = Tcb->SndWnd;\r
117   Tcb->SndWl1     = Seg->Seq;\r
118 \r
119   if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {\r
120     Tcb->SndWl2 = Seg->Ack;\r
121   } else {\r
122     Tcb->SndWl2 = Tcb->Iss + 1;\r
123   }\r
124 \r
125   if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {\r
126     Tcb->SndMss = (UINT16) NET_MAX (64, Opt->Mss);\r
127 \r
128     RcvMss = TcpGetRcvMss (Tcb->Sk);\r
129     if (Tcb->SndMss > RcvMss) {\r
130       Tcb->SndMss = RcvMss;\r
131     }\r
132 \r
133   } else {\r
134     //\r
135     // One end doesn't support MSS option, use default.\r
136     //\r
137     Tcb->RcvMss = 536;\r
138   }\r
139 \r
140   Tcb->CWnd   = Tcb->SndMss;\r
141 \r
142   Tcb->Irs    = Seg->Seq;\r
143   Tcb->RcvNxt = Tcb->Irs + 1;\r
144 \r
145   Tcb->RcvWl2 = Tcb->RcvNxt;\r
146 \r
147   if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) &&\r
148       !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {\r
149 \r
150     Tcb->SndWndScale  = Opt->WndScale;\r
151 \r
152     Tcb->RcvWndScale  = TcpComputeScale (Tcb);\r
153     TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);\r
154 \r
155   } else {\r
156     //\r
157     // One end doesn't support window scale option. use zero.\r
158     //\r
159     Tcb->RcvWndScale = 0;\r
160   }\r
161 \r
162   if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) &&\r
163       !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {\r
164 \r
165     TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);\r
166     TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);\r
167 \r
168     //\r
169     // Compute the effective SndMss per RFC1122\r
170     // section 4.2.2.6. If timestamp option is\r
171     // enabled, it will always occupy 12 bytes.\r
172     //\r
173     Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;\r
174   }\r
175 }\r
176 \r
177 \r
178 /**\r
179   Locate a listen TCB that matchs the Local and Remote.\r
180 \r
181   @param  Local                 Pointer to the local (IP, Port).\r
182   @param  Remote                Pointer to the remote (IP, Port).\r
183 \r
184   @return Pointer to the TCP_CB with the least number of wildcard, if NULL no match is found.\r
185 \r
186 **/\r
187 STATIC\r
188 TCP_CB *\r
189 TcpLocateListenTcb (\r
190   IN TCP_PEER *Local,\r
191   IN TCP_PEER *Remote\r
192   )\r
193 {\r
194   NET_LIST_ENTRY  *Entry;\r
195   TCP_CB          *Node;\r
196   TCP_CB          *Match;\r
197   INTN            Last;\r
198   INTN            Cur;\r
199 \r
200   Last  = 4;\r
201   Match = NULL;\r
202 \r
203   NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
204     Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
205 \r
206     if ((Local->Port != Node->LocalEnd.Port) ||\r
207         !TCP_PEER_MATCH (Remote, &Node->RemoteEnd) ||\r
208         !TCP_PEER_MATCH (Local, &Node->LocalEnd)\r
209           ) {\r
210 \r
211       continue;\r
212     }\r
213 \r
214     //\r
215     // Compute the number of wildcard\r
216     //\r
217     Cur = 0;\r
218     if (Node->RemoteEnd.Ip == 0) {\r
219       Cur++;\r
220     }\r
221 \r
222     if (Node->RemoteEnd.Port == 0) {\r
223       Cur++;\r
224     }\r
225 \r
226     if (Node->LocalEnd.Ip == 0) {\r
227       Cur++;\r
228     }\r
229 \r
230     if (Cur < Last) {\r
231       if (Cur == 0) {\r
232         return Node;\r
233       }\r
234 \r
235       Last  = Cur;\r
236       Match = Node;\r
237     }\r
238   }\r
239 \r
240   return Match;\r
241 }\r
242 \r
243 \r
244 /**\r
245   Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.\r
246 \r
247   @param  Addr                  Pointer to the IP address needs to match.\r
248   @param  Port                  The port number needs to match.\r
249 \r
250   @return The Tcb which matches the <Addr Port> paire exists or not.\r
251 \r
252 **/\r
253 BOOLEAN\r
254 TcpFindTcbByPeer (\r
255   IN EFI_IPv4_ADDRESS  *Addr,\r
256   IN TCP_PORTNO        Port\r
257   )\r
258 {\r
259   TCP_PORTNO      LocalPort;\r
260   NET_LIST_ENTRY  *Entry;\r
261   TCP_CB          *Tcb;\r
262 \r
263   ASSERT ((Addr != NULL) && (Port != 0));\r
264 \r
265   LocalPort = HTONS (Port);\r
266 \r
267   NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
268     Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
269 \r
270     if (EFI_IP4_EQUAL (Addr, &Tcb->LocalEnd.Ip) &&\r
271       (LocalPort == Tcb->LocalEnd.Port)) {\r
272 \r
273       return TRUE;\r
274     }\r
275   }\r
276 \r
277   NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
278     Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
279 \r
280     if (EFI_IP4_EQUAL (Addr, &Tcb->LocalEnd.Ip) &&\r
281       (LocalPort == Tcb->LocalEnd.Port)) {\r
282 \r
283       return TRUE;\r
284     }\r
285   }\r
286 \r
287   return FALSE;\r
288 }\r
289 \r
290 \r
291 /**\r
292   Locate the TCP_CB related to the socket pair.\r
293 \r
294   @param  LocalPort             The local port number.\r
295   @param  LocalIp               The local IP address.\r
296   @param  RemotePort            The remote port number.\r
297   @param  RemoteIp              The remote IP address.\r
298   @param  Syn                   Whether to search the listen sockets, if TRUE, the\r
299                                 listen sockets are searched.\r
300 \r
301   @return Pointer to the related TCP_CB, if NULL no match is found.\r
302 \r
303 **/\r
304 TCP_CB *\r
305 TcpLocateTcb (\r
306   IN TCP_PORTNO  LocalPort,\r
307   IN UINT32      LocalIp,\r
308   IN TCP_PORTNO  RemotePort,\r
309   IN UINT32      RemoteIp,\r
310   IN BOOLEAN     Syn\r
311   )\r
312 {\r
313   TCP_PEER        Local;\r
314   TCP_PEER        Remote;\r
315   NET_LIST_ENTRY  *Entry;\r
316   TCP_CB          *Tcb;\r
317 \r
318   Local.Port  = LocalPort;\r
319   Local.Ip    = LocalIp;\r
320 \r
321   Remote.Port = RemotePort;\r
322   Remote.Ip   = RemoteIp;\r
323 \r
324   //\r
325   // First check for exact match.\r
326   //\r
327   NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
328     Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
329 \r
330     if (TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd) &&\r
331         TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd)) {\r
332 \r
333       NetListRemoveEntry (&Tcb->List);\r
334       NetListInsertHead (&mTcpRunQue, &Tcb->List);\r
335 \r
336       return Tcb;\r
337     }\r
338   }\r
339 \r
340   //\r
341   // Only check listen queue when SYN flag is on\r
342   //\r
343   if (Syn) {\r
344     return TcpLocateListenTcb (&Local, &Remote);\r
345   }\r
346 \r
347   return NULL;\r
348 }\r
349 \r
350 \r
351 /**\r
352   Insert a Tcb into the proper queue.\r
353 \r
354   @param  Tcb                   Pointer to the TCP_CB to be inserted.\r
355 \r
356   @retval 0                     The Tcb is inserted successfully.\r
357   @retval -1                    Error condition occurred.\r
358 \r
359 **/\r
360 INTN\r
361 TcpInsertTcb (\r
362   IN TCP_CB *Tcb\r
363   )\r
364 {\r
365   NET_LIST_ENTRY   *Entry;\r
366   NET_LIST_ENTRY   *Head;\r
367   TCP_CB           *Node;\r
368   TCP4_PROTO_DATA  *TcpProto;\r
369 \r
370   ASSERT (\r
371     Tcb &&\r
372     (\r
373     (Tcb->State == TCP_LISTEN) ||\r
374     (Tcb->State == TCP_SYN_SENT) ||\r
375     (Tcb->State == TCP_SYN_RCVD) ||\r
376     (Tcb->State == TCP_CLOSED)\r
377     )\r
378     );\r
379 \r
380   if (Tcb->LocalEnd.Port == 0) {\r
381     return -1;\r
382   }\r
383 \r
384   Head = &mTcpRunQue;\r
385 \r
386   if (Tcb->State == TCP_LISTEN) {\r
387     Head = &mTcpListenQue;\r
388   }\r
389 \r
390   //\r
391   // Check that Tcb isn't already on the list.\r
392   //\r
393   NET_LIST_FOR_EACH (Entry, Head) {\r
394     Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
395 \r
396     if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd) &&\r
397         TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd)) {\r
398 \r
399       return -1;\r
400     }\r
401   }\r
402 \r
403   NetListInsertHead (Head, &Tcb->List);\r
404 \r
405   TcpProto = (TCP4_PROTO_DATA *) Tcb->Sk->ProtoReserved;\r
406   TcpSetVariableData (TcpProto->TcpService);\r
407 \r
408   return 0;\r
409 }\r
410 \r
411 \r
412 /**\r
413   Clone a TCP_CB from Tcb.\r
414 \r
415   @param  Tcb                   Pointer to the TCP_CB to be cloned.\r
416 \r
417   @return Pointer to the new cloned TCP_CB, if NULL error condition occurred.\r
418 \r
419 **/\r
420 TCP_CB *\r
421 TcpCloneTcb (\r
422   IN TCP_CB *Tcb\r
423   )\r
424 {\r
425   TCP_CB               *Clone;\r
426 \r
427   Clone = NetAllocatePool (sizeof (TCP_CB));\r
428 \r
429   if (Clone == NULL) {\r
430     return NULL;\r
431 \r
432   }\r
433 \r
434   NetCopyMem (Clone, Tcb, sizeof (TCP_CB));\r
435 \r
436   //\r
437   // Increate the reference count of the shared IpInfo.\r
438   //\r
439   NET_GET_REF (Tcb->IpInfo);\r
440 \r
441   NetListInit (&Clone->List);\r
442   NetListInit (&Clone->SndQue);\r
443   NetListInit (&Clone->RcvQue);\r
444 \r
445   Clone->Sk = SockClone (Tcb->Sk);\r
446   if (Clone->Sk == NULL) {\r
447     TCP4_DEBUG_ERROR (("TcpCloneTcb: failed to clone a sock\n"));\r
448     NetFreePool (Clone);\r
449     return NULL;\r
450   }\r
451 \r
452   ((TCP4_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;\r
453 \r
454   return Clone;\r
455 }\r
456 \r
457 \r
458 /**\r
459   Compute an ISS to be used by a new connection.\r
460 \r
461   None\r
462 \r
463   @return The result ISS.\r
464 \r
465 **/\r
466 TCP_SEQNO\r
467 TcpGetIss (\r
468   VOID\r
469   )\r
470 {\r
471   mTcpGlobalIss += 2048;\r
472   return mTcpGlobalIss;\r
473 }\r
474 \r
475 \r
476 /**\r
477   Get the local mss.\r
478 \r
479   None\r
480 \r
481   @return The mss size.\r
482 \r
483 **/\r
484 UINT16\r
485 TcpGetRcvMss (\r
486   IN SOCKET  *Sock\r
487   )\r
488 {\r
489   EFI_SIMPLE_NETWORK_MODE SnpMode;\r
490   TCP4_PROTO_DATA         *TcpProto;\r
491   EFI_IP4_PROTOCOL        *Ip;\r
492 \r
493   ASSERT (Sock);\r
494 \r
495   TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;\r
496   Ip       = TcpProto->TcpService->IpIo->Ip;\r
497   ASSERT (Ip);\r
498 \r
499   Ip->GetModeData (Ip, NULL, NULL, &SnpMode);\r
500 \r
501   return (UINT16) (SnpMode.MaxPacketSize - 40);\r
502 }\r
503 \r
504 \r
505 /**\r
506   Set the Tcb's state.\r
507 \r
508   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
509   @param  State                 The state to be set.\r
510 \r
511   @return None\r
512 \r
513 **/\r
514 VOID\r
515 TcpSetState (\r
516   IN TCP_CB *Tcb,\r
517   IN UINT8  State\r
518   )\r
519 {\r
520   TCP4_DEBUG_TRACE (\r
521     ("Tcb (%x) state %s --> %s\n",\r
522     Tcb,\r
523     mTcpStateName[Tcb->State],\r
524     mTcpStateName[State])\r
525     );\r
526 \r
527   Tcb->State = State;\r
528 \r
529   switch (State) {\r
530   case TCP_ESTABLISHED:\r
531 \r
532     SockConnEstablished (Tcb->Sk);\r
533     break;\r
534 \r
535   case TCP_CLOSED:\r
536 \r
537     SockConnClosed (Tcb->Sk);\r
538 \r
539     break;\r
540   }\r
541 }\r
542 \r
543 \r
544 /**\r
545   Compute the TCP segment's checksum.\r
546 \r
547   @param  Nbuf                  Pointer to the buffer that contains the TCP\r
548                                 segment.\r
549   @param  HeadSum               The checksum value of the fixed part of pseudo\r
550                                 header.\r
551 \r
552   @return The checksum value.\r
553 \r
554 **/\r
555 UINT16\r
556 TcpChecksum (\r
557   IN NET_BUF *Nbuf,\r
558   IN UINT16  HeadSum\r
559   )\r
560 {\r
561   UINT16  Checksum;\r
562 \r
563   Checksum  = NetbufChecksum (Nbuf);\r
564   Checksum  = NetAddChecksum (Checksum, HeadSum);\r
565 \r
566   Checksum = NetAddChecksum (\r
567               Checksum,\r
568               HTONS ((UINT16) Nbuf->TotalSize)\r
569               );\r
570 \r
571   return (UINT16) ~Checksum;\r
572 }\r
573 \r
574 \r
575 /**\r
576   Translate the information from the head of the received TCP\r
577   segment Nbuf contains and fill it into a TCP_SEG structure.\r
578 \r
579   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
580   @param  Nbuf                  Pointer to the buffer contains the TCP segment.\r
581 \r
582   @return Pointer to the TCP_SEG that contains the translated TCP head information.\r
583 \r
584 **/\r
585 TCP_SEG *\r
586 TcpFormatNetbuf (\r
587   IN TCP_CB  *Tcb,\r
588   IN NET_BUF *Nbuf\r
589   )\r
590 {\r
591   TCP_SEG   *Seg;\r
592   TCP_HEAD  *Head;\r
593 \r
594   Seg       = TCPSEG_NETBUF (Nbuf);\r
595   Head      = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
596   Nbuf->Tcp = Head;\r
597 \r
598   Seg->Seq  = NTOHL (Head->Seq);\r
599   Seg->Ack  = NTOHL (Head->Ack);\r
600   Seg->End  = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));\r
601 \r
602   Seg->Urg  = NTOHS (Head->Urg);\r
603   Seg->Wnd  = (NTOHS (Head->Wnd) << Tcb->SndWndScale);\r
604   Seg->Flag = Head->Flag;\r
605 \r
606   //\r
607   // SYN and FIN flag occupy one sequence space each.\r
608   //\r
609   if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
610     //\r
611     // RFC requires that initial window not be scaled\r
612     //\r
613     Seg->Wnd = NTOHS (Head->Wnd);\r
614     Seg->End++;\r
615   }\r
616 \r
617   if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
618     Seg->End++;\r
619   }\r
620 \r
621   return Seg;\r
622 }\r
623 \r
624 \r
625 /**\r
626   Reset the connection related with Tcb.\r
627 \r
628   @param  Tcb                   Pointer to the TCP_CB of the connection to be\r
629                                 reset.\r
630 \r
631   @return None\r
632 \r
633 **/\r
634 VOID\r
635 TcpResetConnection (\r
636   IN TCP_CB *Tcb\r
637   )\r
638 {\r
639   NET_BUF   *Nbuf;\r
640   TCP_HEAD  *Nhead;\r
641 \r
642   Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
643 \r
644   if (Nbuf == NULL) {\r
645     return ;\r
646   }\r
647 \r
648   Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
649                         Nbuf,\r
650                         sizeof (TCP_HEAD),\r
651                         NET_BUF_TAIL\r
652                         );\r
653 \r
654   ASSERT (Nhead != NULL);\r
655 \r
656   Nbuf->Tcp       = Nhead;\r
657 \r
658   Nhead->Flag     = TCP_FLG_RST;\r
659   Nhead->Seq      = HTONL (Tcb->SndNxt);\r
660   Nhead->Ack      = HTONL (Tcb->RcvNxt);\r
661   Nhead->SrcPort  = Tcb->LocalEnd.Port;\r
662   Nhead->DstPort  = Tcb->RemoteEnd.Port;\r
663   Nhead->HeadLen  = (sizeof (TCP_HEAD) >> 2);\r
664   Nhead->Res      = 0;\r
665   Nhead->Wnd      = HTONS (0xFFFF);\r
666   Nhead->Checksum = 0;\r
667   Nhead->Urg      = 0;\r
668   Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
669 \r
670   TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip);\r
671 \r
672   NetbufFree (Nbuf);\r
673 }\r
674 \r
675 \r
676 /**\r
677   Initialize an active connection,\r
678 \r
679   @param  Tcb                   Pointer to the TCP_CB that wants to initiate a\r
680                                 connection.\r
681 \r
682   @return None\r
683 \r
684 **/\r
685 VOID\r
686 TcpOnAppConnect (\r
687   IN TCP_CB  *Tcb\r
688   )\r
689 {\r
690   TcpInitTcbLocal (Tcb);\r
691   TcpSetState (Tcb, TCP_SYN_SENT);\r
692 \r
693   TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
694   TcpToSendData (Tcb, 1);\r
695 }\r
696 \r
697 \r
698 /**\r
699   Initiate the connection close procedure, called when\r
700   applications want to close the connection.\r
701 \r
702   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
703 \r
704   @return None.\r
705 \r
706 **/\r
707 VOID\r
708 TcpOnAppClose (\r
709   IN TCP_CB *Tcb\r
710   )\r
711 {\r
712   ASSERT (Tcb);\r
713 \r
714   if (!NetListIsEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk)) {\r
715 \r
716     TCP4_DEBUG_WARN (("TcpOnAppClose: connection reset "\r
717       "because data is lost for TCB %x\n", Tcb));\r
718 \r
719     TcpResetConnection (Tcb);\r
720     TcpClose (Tcb);\r
721     return;\r
722   }\r
723 \r
724   switch (Tcb->State) {\r
725   case TCP_CLOSED:\r
726   case TCP_LISTEN:\r
727   case TCP_SYN_SENT:\r
728     TcpSetState (Tcb, TCP_CLOSED);\r
729     break;\r
730 \r
731   case TCP_SYN_RCVD:\r
732   case TCP_ESTABLISHED:\r
733     TcpSetState (Tcb, TCP_FIN_WAIT_1);\r
734     break;\r
735 \r
736   case TCP_CLOSE_WAIT:\r
737     TcpSetState (Tcb, TCP_LAST_ACK);\r
738     break;\r
739   }\r
740 \r
741   TcpToSendData (Tcb, 1);\r
742 }\r
743 \r
744 \r
745 /**\r
746   Check whether the application's newly delivered data\r
747   can be sent out.\r
748 \r
749   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
750 \r
751   @retval 0                     Whether the data is sent out or is buffered for\r
752                                 further sending.\r
753   @retval -1                    The Tcb is not in a state that data is permitted to\r
754                                 be sent out.\r
755 \r
756 **/\r
757 INTN\r
758 TcpOnAppSend (\r
759   IN TCP_CB *Tcb\r
760   )\r
761 {\r
762 \r
763   switch (Tcb->State) {\r
764   case TCP_CLOSED:\r
765     return -1;\r
766     break;\r
767 \r
768   case TCP_LISTEN:\r
769     return -1;\r
770     break;\r
771 \r
772   case TCP_SYN_SENT:\r
773   case TCP_SYN_RCVD:\r
774     return 0;\r
775     break;\r
776 \r
777   case TCP_ESTABLISHED:\r
778   case TCP_CLOSE_WAIT:\r
779     TcpToSendData (Tcb, 0);\r
780     return 0;\r
781     break;\r
782 \r
783   case TCP_FIN_WAIT_1:\r
784   case TCP_FIN_WAIT_2:\r
785   case TCP_CLOSING:\r
786   case TCP_LAST_ACK:\r
787   case TCP_TIME_WAIT:\r
788     return -1;\r
789     break;\r
790   }\r
791 \r
792   return 0;\r
793 }\r
794 \r
795 \r
796 /**\r
797   Application has consumed some data, check whether\r
798   to send a window updata ack or a delayed ack.\r
799 \r
800   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
801 \r
802 \r
803 **/\r
804 INTN\r
805 TcpOnAppConsume (\r
806   IN TCP_CB *Tcb\r
807   )\r
808 {\r
809   UINT32 TcpOld;\r
810 \r
811   switch (Tcb->State) {\r
812   case TCP_CLOSED:\r
813     return -1;\r
814     break;\r
815 \r
816   case TCP_LISTEN:\r
817     return -1;\r
818     break;\r
819 \r
820   case TCP_SYN_SENT:\r
821   case TCP_SYN_RCVD:\r
822     return 0;\r
823     break;\r
824 \r
825   case TCP_ESTABLISHED:\r
826     TcpOld = TcpRcvWinOld (Tcb);\r
827     if (TcpRcvWinNow (Tcb) > TcpOld) {\r
828 \r
829       if (TcpOld < Tcb->RcvMss) {\r
830 \r
831         TCP4_DEBUG_TRACE (("TcpOnAppConsume: send a window"\r
832           " update for a window closed Tcb(%x)\n", Tcb));\r
833 \r
834         TcpSendAck (Tcb);\r
835       } else if (Tcb->DelayedAck == 0) {\r
836 \r
837         TCP4_DEBUG_TRACE (("TcpOnAppConsume: scheduled a delayed"\r
838           " ACK to update window for Tcb(%x)\n", Tcb));\r
839 \r
840         Tcb->DelayedAck = 1;\r
841       }\r
842     }\r
843 \r
844     break;\r
845 \r
846   case TCP_CLOSE_WAIT:\r
847     return 0;\r
848     break;\r
849 \r
850   case TCP_FIN_WAIT_1:\r
851   case TCP_FIN_WAIT_2:\r
852   case TCP_CLOSING:\r
853   case TCP_LAST_ACK:\r
854   case TCP_TIME_WAIT:\r
855     return -1;\r
856     break;\r
857   }\r
858 \r
859   return -1;\r
860 }\r
861 \r
862 \r
863 /**\r
864   Abort the connection by sending a reset segment, called\r
865   when the application wants to abort the connection.\r
866 \r
867   @param  Tcb                   Pointer to the TCP_CB of the TCP instance.\r
868 \r
869   @return None.\r
870 \r
871 **/\r
872 VOID\r
873 TcpOnAppAbort (\r
874   IN TCP_CB *Tcb\r
875   )\r
876 {\r
877   TCP4_DEBUG_WARN (("TcpOnAppAbort: connection reset "\r
878     "issued by application for TCB %x\n", Tcb));\r
879 \r
880   switch (Tcb->State) {\r
881   case TCP_SYN_RCVD:\r
882   case TCP_ESTABLISHED:\r
883   case TCP_FIN_WAIT_1:\r
884   case TCP_FIN_WAIT_2:\r
885   case TCP_CLOSE_WAIT:\r
886     TcpResetConnection (Tcb);\r
887     break;\r
888   }\r
889 \r
890   TcpSetState (Tcb, TCP_CLOSED);\r
891 }\r
892 \r
893 \r
894 /**\r
895   Set the Tdp4 variable data.\r
896 \r
897   @param  Tcp4Service           Tcp4 service data.\r
898 \r
899   @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.\r
900   @retval other                 Set variable failed.\r
901 \r
902 **/\r
903 EFI_STATUS\r
904 TcpSetVariableData (\r
905   IN TCP4_SERVICE_DATA  *Tcp4Service\r
906   )\r
907 {\r
908   UINT32                  NumConfiguredInstance;\r
909   NET_LIST_ENTRY          *Entry;\r
910   TCP_CB                  *TcpPcb;\r
911   TCP4_PROTO_DATA         *TcpProto;\r
912   UINTN                   VariableDataSize;\r
913   EFI_TCP4_VARIABLE_DATA  *Tcp4VariableData;\r
914   EFI_TCP4_SERVICE_POINT  *Tcp4ServicePoint;\r
915   CHAR16                  *NewMacString;\r
916   EFI_STATUS              Status;\r
917 \r
918   NumConfiguredInstance = 0;\r
919 \r
920   //\r
921   // Go through the running queue to count the instances.\r
922   //\r
923   NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
924     TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
925 \r
926     TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
927 \r
928     if (TcpProto->TcpService == Tcp4Service) {\r
929       //\r
930       // This tcp instance belongs to the Tcp4Service.\r
931       //\r
932       NumConfiguredInstance++;\r
933     }\r
934   }\r
935 \r
936   //\r
937   // Go through the listening queue to count the instances.\r
938   //\r
939   NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
940     TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
941 \r
942     TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
943 \r
944     if (TcpProto->TcpService == Tcp4Service) {\r
945       //\r
946       // This tcp instance belongs to the Tcp4Service.\r
947       //\r
948       NumConfiguredInstance++;\r
949     }\r
950   }\r
951 \r
952   //\r
953   // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child,\r
954   // we should add extra buffer for the service points only if the number of configured\r
955   // children is more than 1.\r
956   //\r
957   VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA);\r
958 \r
959   if (NumConfiguredInstance > 1) {\r
960     VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
961   }\r
962 \r
963   Tcp4VariableData = NetAllocatePool (VariableDataSize);\r
964   if (Tcp4VariableData == NULL) {\r
965     return EFI_OUT_OF_RESOURCES;\r
966   }\r
967 \r
968   Tcp4VariableData->DriverHandle = Tcp4Service->DriverBindingHandle;\r
969   Tcp4VariableData->ServiceCount = NumConfiguredInstance;\r
970 \r
971   Tcp4ServicePoint = &Tcp4VariableData->Services[0];\r
972 \r
973   //\r
974   // Go through the running queue to fill the service points.\r
975   //\r
976   NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
977     TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
978 \r
979     TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
980 \r
981     if (TcpProto->TcpService == Tcp4Service) {\r
982       //\r
983       // This tcp instance belongs to the Tcp4Service.\r
984       //\r
985       Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
986       NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
987       Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
988       NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
989       Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
990 \r
991       Tcp4ServicePoint++;\r
992     }\r
993   }\r
994 \r
995   //\r
996   // Go through the listening queue to fill the service points.\r
997   //\r
998   NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
999     TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
1000 \r
1001     TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
1002 \r
1003     if (TcpProto->TcpService == Tcp4Service) {\r
1004       //\r
1005       // This tcp instance belongs to the Tcp4Service.\r
1006       //\r
1007       Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
1008       NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
1009       Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
1010       NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
1011       Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
1012 \r
1013       Tcp4ServicePoint++;\r
1014     }\r
1015   }\r
1016 \r
1017   //\r
1018   // Get the mac string.\r
1019   //\r
1020   Status = NetLibGetMacString (\r
1021              Tcp4Service->ControllerHandle,\r
1022              Tcp4Service->DriverBindingHandle,\r
1023              &NewMacString\r
1024              );\r
1025   if (EFI_ERROR (Status)) {\r
1026     goto ON_ERROR;\r
1027   }\r
1028 \r
1029   if (Tcp4Service->MacString != NULL) {\r
1030     //\r
1031     // The variable is set already, we're going to update it.\r
1032     //\r
1033     if (StrCmp (Tcp4Service->MacString, NewMacString) != 0) {\r
1034       //\r
1035       // The mac address is changed, delete the previous variable first.\r
1036       //\r
1037       gRT->SetVariable (\r
1038              Tcp4Service->MacString,\r
1039              &gEfiTcp4ServiceBindingProtocolGuid,\r
1040              EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
1041              0,\r
1042              NULL\r
1043              );\r
1044     }\r
1045 \r
1046     NetFreePool (Tcp4Service->MacString);\r
1047   }\r
1048 \r
1049   Tcp4Service->MacString = NewMacString;\r
1050 \r
1051   Status = gRT->SetVariable (\r
1052                   Tcp4Service->MacString,\r
1053                   &gEfiTcp4ServiceBindingProtocolGuid,\r
1054                   EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
1055                   VariableDataSize,\r
1056                   (VOID *) Tcp4VariableData\r
1057                   );\r
1058 \r
1059 ON_ERROR:\r
1060 \r
1061   NetFreePool (Tcp4VariableData);\r
1062 \r
1063   return Status;\r
1064 }\r
1065 \r
1066 \r
1067 /**\r
1068   Clear the variable and free the resource.\r
1069 \r
1070   @param  Tcp4Service           Tcp4 service data.\r
1071 \r
1072   @return None.\r
1073 \r
1074 **/\r
1075 VOID\r
1076 TcpClearVariableData (\r
1077   IN TCP4_SERVICE_DATA  *Tcp4Service\r
1078   )\r
1079 {\r
1080   ASSERT (Tcp4Service->MacString != NULL);\r
1081 \r
1082   gRT->SetVariable (\r
1083          Tcp4Service->MacString,\r
1084          &gEfiTcp4ServiceBindingProtocolGuid,\r
1085          EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
1086          0,\r
1087          NULL\r
1088          );\r
1089 \r
1090   NetFreePool (Tcp4Service->MacString);\r
1091   Tcp4Service->MacString = NULL;\r
1092 }\r
1093 \r