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