1. Sync the latest network stack. Add NetLibCreateIPv4DPathNode () in netlib library.
[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) NET_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   TCP4_SERVICE_DATA  *TcpService;\r
429 \r
430   Clone = NetAllocatePool (sizeof (TCP_CB));\r
431 \r
432   if (Clone == NULL) {\r
433     return NULL;\r
434 \r
435   }\r
436 \r
437   NetCopyMem (Clone, Tcb, sizeof (TCP_CB));\r
438 \r
439   //\r
440   // Increate the reference count of the shared IpInfo.\r
441   //\r
442   NET_GET_REF (Tcb->IpInfo);\r
443 \r
444   NetListInit (&Clone->List);\r
445   NetListInit (&Clone->SndQue);\r
446   NetListInit (&Clone->RcvQue);\r
447 \r
448   Clone->Sk = SockClone (Tcb->Sk);\r
449   if (Clone->Sk == NULL) {\r
450     TCP4_DEBUG_ERROR (("TcpCloneTcb: failed to clone a sock\n"));\r
451     NetFreePool (Clone);\r
452     return NULL;\r
453   }\r
454 \r
455   ((TCP4_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;\r
456 \r
457   //\r
458   // Open the device path on the handle where service binding resides on.\r
459   //\r
460   TcpService = ((TCP4_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpService;\r
461   gBS->OpenProtocol (\r
462          TcpService->ControllerHandle,\r
463          &gEfiDevicePathProtocolGuid,\r
464          (VOID **) &Clone->Sk->ParentDevicePath,\r
465          TcpService->DriverBindingHandle,\r
466          Clone->Sk->SockHandle,\r
467          EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
468          );\r
469 \r
470   return Clone;\r
471 }\r
472 \r
473 \r
474 /**\r
475   Compute an ISS to be used by a new connection.\r
476 \r
477   None\r
478 \r
479   @return The result ISS.\r
480 \r
481 **/\r
482 TCP_SEQNO\r
483 TcpGetIss (\r
484   VOID\r
485   )\r
486 {\r
487   mTcpGlobalIss += 2048;\r
488   return mTcpGlobalIss;\r
489 }\r
490 \r
491 \r
492 /**\r
493   Get the local mss.\r
494 \r
495   None\r
496 \r
497   @return The mss size.\r
498 \r
499 **/\r
500 UINT16\r
501 TcpGetRcvMss (\r
502   IN SOCKET  *Sock\r
503   )\r
504 {\r
505   EFI_SIMPLE_NETWORK_MODE SnpMode;\r
506   TCP4_PROTO_DATA         *TcpProto;\r
507   EFI_IP4_PROTOCOL        *Ip;\r
508 \r
509   ASSERT (Sock);\r
510 \r
511   TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;\r
512   Ip       = TcpProto->TcpService->IpIo->Ip;\r
513   ASSERT (Ip);\r
514 \r
515   Ip->GetModeData (Ip, NULL, NULL, &SnpMode);\r
516 \r
517   return (UINT16) (SnpMode.MaxPacketSize - 40);\r
518 }\r
519 \r
520 \r
521 /**\r
522   Set the Tcb's state.\r
523 \r
524   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
525   @param  State                 The state to be set.\r
526 \r
527   @return None\r
528 \r
529 **/\r
530 VOID\r
531 TcpSetState (\r
532   IN TCP_CB *Tcb,\r
533   IN UINT8  State\r
534   )\r
535 {\r
536   TCP4_DEBUG_TRACE (\r
537     ("Tcb (%x) state %s --> %s\n",\r
538     Tcb,\r
539     mTcpStateName[Tcb->State],\r
540     mTcpStateName[State])\r
541     );\r
542 \r
543   Tcb->State = State;\r
544 \r
545   switch (State) {\r
546   case TCP_ESTABLISHED:\r
547 \r
548     SockConnEstablished (Tcb->Sk);\r
549 \r
550     if (Tcb->Parent != NULL) {\r
551       //\r
552       // A new connection is accepted by a listening socket, install\r
553       // the device path.\r
554       //\r
555       TcpInstallDevicePath (Tcb->Sk);\r
556     }\r
557 \r
558     break;\r
559 \r
560   case TCP_CLOSED:\r
561 \r
562     SockConnClosed (Tcb->Sk);\r
563 \r
564     break;\r
565   }\r
566 }\r
567 \r
568 \r
569 /**\r
570   Compute the TCP segment's checksum.\r
571 \r
572   @param  Nbuf                  Pointer to the buffer that contains the TCP\r
573                                 segment.\r
574   @param  HeadSum               The checksum value of the fixed part of pseudo\r
575                                 header.\r
576 \r
577   @return The checksum value.\r
578 \r
579 **/\r
580 UINT16\r
581 TcpChecksum (\r
582   IN NET_BUF *Nbuf,\r
583   IN UINT16  HeadSum\r
584   )\r
585 {\r
586   UINT16  Checksum;\r
587 \r
588   Checksum  = NetbufChecksum (Nbuf);\r
589   Checksum  = NetAddChecksum (Checksum, HeadSum);\r
590 \r
591   Checksum = NetAddChecksum (\r
592               Checksum,\r
593               HTONS ((UINT16) Nbuf->TotalSize)\r
594               );\r
595 \r
596   return (UINT16) ~Checksum;\r
597 }\r
598 \r
599 \r
600 /**\r
601   Translate the information from the head of the received TCP\r
602   segment Nbuf contains and fill it into a TCP_SEG structure.\r
603 \r
604   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
605   @param  Nbuf                  Pointer to the buffer contains the TCP segment.\r
606 \r
607   @return Pointer to the TCP_SEG that contains the translated TCP head information.\r
608 \r
609 **/\r
610 TCP_SEG *\r
611 TcpFormatNetbuf (\r
612   IN TCP_CB  *Tcb,\r
613   IN NET_BUF *Nbuf\r
614   )\r
615 {\r
616   TCP_SEG   *Seg;\r
617   TCP_HEAD  *Head;\r
618 \r
619   Seg       = TCPSEG_NETBUF (Nbuf);\r
620   Head      = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);\r
621   Nbuf->Tcp = Head;\r
622 \r
623   Seg->Seq  = NTOHL (Head->Seq);\r
624   Seg->Ack  = NTOHL (Head->Ack);\r
625   Seg->End  = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));\r
626 \r
627   Seg->Urg  = NTOHS (Head->Urg);\r
628   Seg->Wnd  = (NTOHS (Head->Wnd) << Tcb->SndWndScale);\r
629   Seg->Flag = Head->Flag;\r
630 \r
631   //\r
632   // SYN and FIN flag occupy one sequence space each.\r
633   //\r
634   if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
635     //\r
636     // RFC requires that initial window not be scaled\r
637     //\r
638     Seg->Wnd = NTOHS (Head->Wnd);\r
639     Seg->End++;\r
640   }\r
641 \r
642   if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
643     Seg->End++;\r
644   }\r
645 \r
646   return Seg;\r
647 }\r
648 \r
649 \r
650 /**\r
651   Reset the connection related with Tcb.\r
652 \r
653   @param  Tcb                   Pointer to the TCP_CB of the connection to be\r
654                                 reset.\r
655 \r
656   @return None\r
657 \r
658 **/\r
659 VOID\r
660 TcpResetConnection (\r
661   IN TCP_CB *Tcb\r
662   )\r
663 {\r
664   NET_BUF   *Nbuf;\r
665   TCP_HEAD  *Nhead;\r
666 \r
667   Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
668 \r
669   if (Nbuf == NULL) {\r
670     return ;\r
671   }\r
672 \r
673   Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
674                         Nbuf,\r
675                         sizeof (TCP_HEAD),\r
676                         NET_BUF_TAIL\r
677                         );\r
678 \r
679   ASSERT (Nhead != NULL);\r
680 \r
681   Nbuf->Tcp       = Nhead;\r
682 \r
683   Nhead->Flag     = TCP_FLG_RST;\r
684   Nhead->Seq      = HTONL (Tcb->SndNxt);\r
685   Nhead->Ack      = HTONL (Tcb->RcvNxt);\r
686   Nhead->SrcPort  = Tcb->LocalEnd.Port;\r
687   Nhead->DstPort  = Tcb->RemoteEnd.Port;\r
688   Nhead->HeadLen  = (sizeof (TCP_HEAD) >> 2);\r
689   Nhead->Res      = 0;\r
690   Nhead->Wnd      = HTONS (0xFFFF);\r
691   Nhead->Checksum = 0;\r
692   Nhead->Urg      = 0;\r
693   Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
694 \r
695   TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip);\r
696 \r
697   NetbufFree (Nbuf);\r
698 }\r
699 \r
700 \r
701 /**\r
702   Initialize an active connection,\r
703 \r
704   @param  Tcb                   Pointer to the TCP_CB that wants to initiate a\r
705                                 connection.\r
706 \r
707   @return None\r
708 \r
709 **/\r
710 VOID\r
711 TcpOnAppConnect (\r
712   IN TCP_CB  *Tcb\r
713   )\r
714 {\r
715   TcpInitTcbLocal (Tcb);\r
716   TcpSetState (Tcb, TCP_SYN_SENT);\r
717 \r
718   TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);\r
719   TcpToSendData (Tcb, 1);\r
720 }\r
721 \r
722 \r
723 /**\r
724   Initiate the connection close procedure, called when\r
725   applications want to close the connection.\r
726 \r
727   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
728 \r
729   @return None.\r
730 \r
731 **/\r
732 VOID\r
733 TcpOnAppClose (\r
734   IN TCP_CB *Tcb\r
735   )\r
736 {\r
737   ASSERT (Tcb);\r
738 \r
739   if (!NetListIsEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk)) {\r
740 \r
741     TCP4_DEBUG_WARN (("TcpOnAppClose: connection reset "\r
742       "because data is lost for TCB %x\n", Tcb));\r
743 \r
744     TcpResetConnection (Tcb);\r
745     TcpClose (Tcb);\r
746     return;\r
747   }\r
748 \r
749   switch (Tcb->State) {\r
750   case TCP_CLOSED:\r
751   case TCP_LISTEN:\r
752   case TCP_SYN_SENT:\r
753     TcpSetState (Tcb, TCP_CLOSED);\r
754     break;\r
755 \r
756   case TCP_SYN_RCVD:\r
757   case TCP_ESTABLISHED:\r
758     TcpSetState (Tcb, TCP_FIN_WAIT_1);\r
759     break;\r
760 \r
761   case TCP_CLOSE_WAIT:\r
762     TcpSetState (Tcb, TCP_LAST_ACK);\r
763     break;\r
764   }\r
765 \r
766   TcpToSendData (Tcb, 1);\r
767 }\r
768 \r
769 \r
770 /**\r
771   Check whether the application's newly delivered data\r
772   can be sent out.\r
773 \r
774   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
775 \r
776   @retval 0                     Whether the data is sent out or is buffered for\r
777                                 further sending.\r
778   @retval -1                    The Tcb is not in a state that data is permitted to\r
779                                 be sent out.\r
780 \r
781 **/\r
782 INTN\r
783 TcpOnAppSend (\r
784   IN TCP_CB *Tcb\r
785   )\r
786 {\r
787 \r
788   switch (Tcb->State) {\r
789   case TCP_CLOSED:\r
790     return -1;\r
791     break;\r
792 \r
793   case TCP_LISTEN:\r
794     return -1;\r
795     break;\r
796 \r
797   case TCP_SYN_SENT:\r
798   case TCP_SYN_RCVD:\r
799     return 0;\r
800     break;\r
801 \r
802   case TCP_ESTABLISHED:\r
803   case TCP_CLOSE_WAIT:\r
804     TcpToSendData (Tcb, 0);\r
805     return 0;\r
806     break;\r
807 \r
808   case TCP_FIN_WAIT_1:\r
809   case TCP_FIN_WAIT_2:\r
810   case TCP_CLOSING:\r
811   case TCP_LAST_ACK:\r
812   case TCP_TIME_WAIT:\r
813     return -1;\r
814     break;\r
815   }\r
816 \r
817   return 0;\r
818 }\r
819 \r
820 \r
821 /**\r
822   Application has consumed some data, check whether\r
823   to send a window updata ack or a delayed ack.\r
824 \r
825   @param  Tcb                   Pointer to the TCP_CB of this TCP instance.\r
826 \r
827 \r
828 **/\r
829 INTN\r
830 TcpOnAppConsume (\r
831   IN TCP_CB *Tcb\r
832   )\r
833 {\r
834   UINT32 TcpOld;\r
835 \r
836   switch (Tcb->State) {\r
837   case TCP_CLOSED:\r
838     return -1;\r
839     break;\r
840 \r
841   case TCP_LISTEN:\r
842     return -1;\r
843     break;\r
844 \r
845   case TCP_SYN_SENT:\r
846   case TCP_SYN_RCVD:\r
847     return 0;\r
848     break;\r
849 \r
850   case TCP_ESTABLISHED:\r
851     TcpOld = TcpRcvWinOld (Tcb);\r
852     if (TcpRcvWinNow (Tcb) > TcpOld) {\r
853 \r
854       if (TcpOld < Tcb->RcvMss) {\r
855 \r
856         TCP4_DEBUG_TRACE (("TcpOnAppConsume: send a window"\r
857           " update for a window closed Tcb(%x)\n", Tcb));\r
858 \r
859         TcpSendAck (Tcb);\r
860       } else if (Tcb->DelayedAck == 0) {\r
861 \r
862         TCP4_DEBUG_TRACE (("TcpOnAppConsume: scheduled a delayed"\r
863           " ACK to update window for Tcb(%x)\n", Tcb));\r
864 \r
865         Tcb->DelayedAck = 1;\r
866       }\r
867     }\r
868 \r
869     break;\r
870 \r
871   case TCP_CLOSE_WAIT:\r
872     return 0;\r
873     break;\r
874 \r
875   case TCP_FIN_WAIT_1:\r
876   case TCP_FIN_WAIT_2:\r
877   case TCP_CLOSING:\r
878   case TCP_LAST_ACK:\r
879   case TCP_TIME_WAIT:\r
880     return -1;\r
881     break;\r
882   }\r
883 \r
884   return -1;\r
885 }\r
886 \r
887 \r
888 /**\r
889   Abort the connection by sending a reset segment, called\r
890   when the application wants to abort the connection.\r
891 \r
892   @param  Tcb                   Pointer to the TCP_CB of the TCP instance.\r
893 \r
894   @return None.\r
895 \r
896 **/\r
897 VOID\r
898 TcpOnAppAbort (\r
899   IN TCP_CB *Tcb\r
900   )\r
901 {\r
902   TCP4_DEBUG_WARN (("TcpOnAppAbort: connection reset "\r
903     "issued by application for TCB %x\n", Tcb));\r
904 \r
905   switch (Tcb->State) {\r
906   case TCP_SYN_RCVD:\r
907   case TCP_ESTABLISHED:\r
908   case TCP_FIN_WAIT_1:\r
909   case TCP_FIN_WAIT_2:\r
910   case TCP_CLOSE_WAIT:\r
911     TcpResetConnection (Tcb);\r
912     break;\r
913   }\r
914 \r
915   TcpSetState (Tcb, TCP_CLOSED);\r
916 }\r
917 \r
918 \r
919 /**\r
920   Set the Tdp4 variable data.\r
921 \r
922   @param  Tcp4Service           Tcp4 service data.\r
923 \r
924   @retval EFI_OUT_OF_RESOURCES  There are not enough resources to set the variable.\r
925   @retval other                 Set variable failed.\r
926 \r
927 **/\r
928 EFI_STATUS\r
929 TcpSetVariableData (\r
930   IN TCP4_SERVICE_DATA  *Tcp4Service\r
931   )\r
932 {\r
933   UINT32                  NumConfiguredInstance;\r
934   NET_LIST_ENTRY          *Entry;\r
935   TCP_CB                  *TcpPcb;\r
936   TCP4_PROTO_DATA         *TcpProto;\r
937   UINTN                   VariableDataSize;\r
938   EFI_TCP4_VARIABLE_DATA  *Tcp4VariableData;\r
939   EFI_TCP4_SERVICE_POINT  *Tcp4ServicePoint;\r
940   CHAR16                  *NewMacString;\r
941   EFI_STATUS              Status;\r
942 \r
943   NumConfiguredInstance = 0;\r
944 \r
945   //\r
946   // Go through the running queue to count the instances.\r
947   //\r
948   NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
949     TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
950 \r
951     TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
952 \r
953     if (TcpProto->TcpService == Tcp4Service) {\r
954       //\r
955       // This tcp instance belongs to the Tcp4Service.\r
956       //\r
957       NumConfiguredInstance++;\r
958     }\r
959   }\r
960 \r
961   //\r
962   // Go through the listening queue to count the instances.\r
963   //\r
964   NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
965     TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
966 \r
967     TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
968 \r
969     if (TcpProto->TcpService == Tcp4Service) {\r
970       //\r
971       // This tcp instance belongs to the Tcp4Service.\r
972       //\r
973       NumConfiguredInstance++;\r
974     }\r
975   }\r
976 \r
977   //\r
978   // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child,\r
979   // we should add extra buffer for the service points only if the number of configured\r
980   // children is more than 1.\r
981   //\r
982   VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA);\r
983 \r
984   if (NumConfiguredInstance > 1) {\r
985     VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1);\r
986   }\r
987 \r
988   Tcp4VariableData = NetAllocatePool (VariableDataSize);\r
989   if (Tcp4VariableData == NULL) {\r
990     return EFI_OUT_OF_RESOURCES;\r
991   }\r
992 \r
993   Tcp4VariableData->DriverHandle = Tcp4Service->DriverBindingHandle;\r
994   Tcp4VariableData->ServiceCount = NumConfiguredInstance;\r
995 \r
996   Tcp4ServicePoint = &Tcp4VariableData->Services[0];\r
997 \r
998   //\r
999   // Go through the running queue to fill the service points.\r
1000   //\r
1001   NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {\r
1002     TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
1003 \r
1004     TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
1005 \r
1006     if (TcpProto->TcpService == Tcp4Service) {\r
1007       //\r
1008       // This tcp instance belongs to the Tcp4Service.\r
1009       //\r
1010       Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
1011       NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
1012       Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
1013       NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
1014       Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
1015 \r
1016       Tcp4ServicePoint++;\r
1017     }\r
1018   }\r
1019 \r
1020   //\r
1021   // Go through the listening queue to fill the service points.\r
1022   //\r
1023   NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {\r
1024     TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
1025 \r
1026     TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;\r
1027 \r
1028     if (TcpProto->TcpService == Tcp4Service) {\r
1029       //\r
1030       // This tcp instance belongs to the Tcp4Service.\r
1031       //\r
1032       Tcp4ServicePoint->InstanceHandle          = TcpPcb->Sk->SockHandle;\r
1033       NetCopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
1034       Tcp4ServicePoint->LocalPort               = NTOHS (TcpPcb->LocalEnd.Port);\r
1035       NetCopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS));\r
1036       Tcp4ServicePoint->RemotePort              = NTOHS (TcpPcb->RemoteEnd.Port);\r
1037 \r
1038       Tcp4ServicePoint++;\r
1039     }\r
1040   }\r
1041 \r
1042   //\r
1043   // Get the mac string.\r
1044   //\r
1045   Status = NetLibGetMacString (\r
1046              Tcp4Service->ControllerHandle,\r
1047              Tcp4Service->DriverBindingHandle,\r
1048              &NewMacString\r
1049              );\r
1050   if (EFI_ERROR (Status)) {\r
1051     goto ON_ERROR;\r
1052   }\r
1053 \r
1054   if (Tcp4Service->MacString != NULL) {\r
1055     //\r
1056     // The variable is set already, we're going to update it.\r
1057     //\r
1058     if (StrCmp (Tcp4Service->MacString, NewMacString) != 0) {\r
1059       //\r
1060       // The mac address is changed, delete the previous variable first.\r
1061       //\r
1062       gRT->SetVariable (\r
1063              Tcp4Service->MacString,\r
1064              &gEfiTcp4ServiceBindingProtocolGuid,\r
1065              EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
1066              0,\r
1067              NULL\r
1068              );\r
1069     }\r
1070 \r
1071     NetFreePool (Tcp4Service->MacString);\r
1072   }\r
1073 \r
1074   Tcp4Service->MacString = NewMacString;\r
1075 \r
1076   Status = gRT->SetVariable (\r
1077                   Tcp4Service->MacString,\r
1078                   &gEfiTcp4ServiceBindingProtocolGuid,\r
1079                   EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
1080                   VariableDataSize,\r
1081                   (VOID *) Tcp4VariableData\r
1082                   );\r
1083 \r
1084 ON_ERROR:\r
1085 \r
1086   NetFreePool (Tcp4VariableData);\r
1087 \r
1088   return Status;\r
1089 }\r
1090 \r
1091 \r
1092 /**\r
1093   Clear the variable and free the resource.\r
1094 \r
1095   @param  Tcp4Service           Tcp4 service data.\r
1096 \r
1097   @return None.\r
1098 \r
1099 **/\r
1100 VOID\r
1101 TcpClearVariableData (\r
1102   IN TCP4_SERVICE_DATA  *Tcp4Service\r
1103   )\r
1104 {\r
1105   ASSERT (Tcp4Service->MacString != NULL);\r
1106 \r
1107   gRT->SetVariable (\r
1108          Tcp4Service->MacString,\r
1109          &gEfiTcp4ServiceBindingProtocolGuid,\r
1110          EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
1111          0,\r
1112          NULL\r
1113          );\r
1114 \r
1115   NetFreePool (Tcp4Service->MacString);\r
1116   Tcp4Service->MacString = NULL;\r
1117 }\r
1118 \r
1119 EFI_STATUS\r
1120 TcpInstallDevicePath (\r
1121   IN SOCKET *Sock\r
1122   )\r
1123 /*++\r
1124 \r
1125 Routine Description:\r
1126 \r
1127   Install the device path protocol on the TCP instance.\r
1128 \r
1129 Arguments:\r
1130 \r
1131   Sock - Pointer to the socket representing the TCP instance.\r
1132 \r
1133 Returns:\r
1134 \r
1135   EFI_SUCCESS - The device path protocol is installed.\r
1136   other       - Failed to install the device path protocol.\r
1137 \r
1138 --*/\r
1139 {\r
1140   TCP4_PROTO_DATA    *TcpProto;\r
1141   TCP4_SERVICE_DATA  *TcpService;\r
1142   TCP_CB             *Tcb;\r
1143   IPv4_DEVICE_PATH   Ip4DPathNode;\r
1144   EFI_STATUS         Status;\r
1145 \r
1146   TcpProto   = (TCP4_PROTO_DATA *) Sock->ProtoReserved;\r
1147   TcpService = TcpProto->TcpService;\r
1148   Tcb        = TcpProto->TcpPcb;\r
1149 \r
1150   NetLibCreateIPv4DPathNode (\r
1151     &Ip4DPathNode,\r
1152     TcpService->ControllerHandle,\r
1153     Tcb->LocalEnd.Ip,\r
1154     NTOHS (Tcb->LocalEnd.Port),\r
1155     Tcb->RemoteEnd.Ip,\r
1156     NTOHS (Tcb->RemoteEnd.Port),\r
1157     EFI_IP_PROTO_TCP,\r
1158     Tcb->UseDefaultAddr\r
1159     );\r
1160 \r
1161   Sock->DevicePath = AppendDevicePathNode (\r
1162                      Sock->ParentDevicePath,\r
1163                      (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode\r
1164                      );\r
1165   if (Sock->DevicePath == NULL) {\r
1166     return EFI_OUT_OF_RESOURCES;\r
1167   }\r
1168 \r
1169   Status = gBS->InstallProtocolInterface (\r
1170                   &Sock->SockHandle,\r
1171                   &gEfiDevicePathProtocolGuid,\r
1172                   EFI_NATIVE_INTERFACE,\r
1173                   Sock->DevicePath\r
1174                   );\r
1175   if (EFI_ERROR (Status)) {\r
1176     NetFreePool (Sock->DevicePath);\r
1177   }\r
1178 \r
1179   return Status;\r
1180 }\r