cc869717dd047af7552b8c29579d2180a35838ea
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Library / DxeIpIoLib / DxeIpIoLib.c
1 /** @file\r
2 \r
3 Copyright (c) 2005 - 2007, 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   IpIo.c\r
15 \r
16 Abstract:\r
17 \r
18   The implementation of the IpIo layer.\r
19 \r
20 \r
21 **/\r
22 \r
23 #include <PiDxe.h>\r
24 \r
25 #include <Protocol/Udp4.h>\r
26 \r
27 #include <Library/IpIoLib.h>\r
28 #include <Library/BaseLib.h>\r
29 #include <Library/DebugLib.h>\r
30 #include <Library/UefiBootServicesTableLib.h>\r
31 #include <Library/MemoryAllocationLib.h>\r
32 #include <Library/BaseMemoryLib.h>\r
33 \r
34 \r
35 #define NET_PROTO_HDR(Buf, Type)  ((Type *) ((Buf)->BlockOp[0].Head))\r
36 #define ICMP_ERRLEN(IpHdr) \\r
37   (sizeof(IP4_ICMP_HEAD) + EFI_IP4_HEADER_LEN(IpHdr) + 8)\r
38 \r
39 NET_LIST_ENTRY  mActiveIpIoList = {\r
40   &mActiveIpIoList,\r
41   &mActiveIpIoList\r
42 };\r
43 \r
44 EFI_IP4_CONFIG_DATA  mIpIoDefaultIpConfigData = {\r
45   EFI_IP_PROTO_UDP,\r
46   FALSE,\r
47   TRUE,\r
48   FALSE,\r
49   FALSE,\r
50   FALSE,\r
51   {{0, 0, 0, 0}},\r
52   {{0, 0, 0, 0}},\r
53   0,\r
54   255,\r
55   FALSE,\r
56   FALSE,\r
57   0,\r
58   0\r
59 };\r
60 \r
61 STATIC\r
62 VOID\r
63 EFIAPI\r
64 IpIoTransmitHandler (\r
65   IN EFI_EVENT Event,\r
66   IN VOID      *Context\r
67   );\r
68 \r
69 \r
70 /**\r
71   This function create an ip child ,open the IP protocol, return the opened\r
72   Ip protocol to Interface.\r
73 \r
74   @param  ControllerHandle      The controller handle.\r
75   @param  ImageHandle           The image handle.\r
76   @param  ChildHandle           Pointer to the buffer to save the ip child handle.\r
77   @param  Interface             Pointer used to get the ip protocol interface.\r
78 \r
79   @retval EFI_SUCCESS           The ip child is created and the ip protocol\r
80                                 interface is retrieved.\r
81   @retval other                 The required operation failed.\r
82 \r
83 **/\r
84 STATIC\r
85 EFI_STATUS\r
86 IpIoCreateIpChildOpenProtocol (\r
87   IN  EFI_HANDLE  ControllerHandle,\r
88   IN  EFI_HANDLE  ImageHandle,\r
89   IN  EFI_HANDLE  *ChildHandle,\r
90   OUT VOID        **Interface\r
91   )\r
92 {\r
93   EFI_STATUS  Status;\r
94 \r
95   //\r
96   // Create an ip child.\r
97   //\r
98   Status = NetLibCreateServiceChild (\r
99              ControllerHandle,\r
100              ImageHandle,\r
101              &gEfiIp4ServiceBindingProtocolGuid,\r
102              ChildHandle\r
103              );\r
104   if (EFI_ERROR (Status)) {\r
105     return Status;\r
106   }\r
107 \r
108   //\r
109   // Open the ip protocol installed on the *ChildHandle.\r
110   //\r
111   Status = gBS->OpenProtocol (\r
112                   *ChildHandle,\r
113                   &gEfiIp4ProtocolGuid,\r
114                   Interface,\r
115                   ImageHandle,\r
116                   ControllerHandle,\r
117                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
118                   );\r
119   if (EFI_ERROR (Status)) {\r
120     //\r
121     // On failure, destroy the ip child.\r
122     //\r
123     NetLibDestroyServiceChild (\r
124       ControllerHandle,\r
125       ImageHandle,\r
126       &gEfiIp4ServiceBindingProtocolGuid,\r
127       *ChildHandle\r
128       );\r
129   }\r
130 \r
131   return Status;\r
132 }\r
133 \r
134 \r
135 /**\r
136   This function close the previously openned ip protocol and destroy the ip child.\r
137 \r
138   @param  ControllerHandle      The controller handle.\r
139   @param  ImageHandle           the image handle.\r
140   @param  ChildHandle           The child handle of the ip child.\r
141 \r
142   @retval EFI_SUCCESS           The ip protocol is closed and the relevant ip child\r
143                                 is destroyed.\r
144   @retval other                 The required operation failed.\r
145 \r
146 **/\r
147 STATIC\r
148 EFI_STATUS\r
149 IpIoCloseProtocolDestroyIpChild (\r
150   IN EFI_HANDLE  ControllerHandle,\r
151   IN EFI_HANDLE  ImageHandle,\r
152   IN EFI_HANDLE  ChildHandle\r
153   )\r
154 {\r
155   EFI_STATUS  Status;\r
156 \r
157   //\r
158   // Close the previously openned ip protocol.\r
159   //\r
160   gBS->CloseProtocol (\r
161          ChildHandle,\r
162          &gEfiIp4ProtocolGuid,\r
163          ImageHandle,\r
164          ControllerHandle\r
165          );\r
166 \r
167   //\r
168   // Destroy the ip child.\r
169   //\r
170   Status = NetLibDestroyServiceChild (\r
171              ControllerHandle,\r
172              ImageHandle,\r
173              &gEfiIp4ServiceBindingProtocolGuid,\r
174              ChildHandle\r
175              );\r
176 \r
177   return Status;\r
178 }\r
179 \r
180 \r
181 /**\r
182   Handle ICMP packets.\r
183 \r
184   @param  IpIo                  Pointer to the IP_IO instance.\r
185   @param  Pkt                   Pointer to the ICMP packet.\r
186   @param  Session               Pointer to the net session of this ICMP packet.\r
187 \r
188   @retval EFI_SUCCESS           The ICMP packet is handled successfully.\r
189   @retval EFI_ABORTED           This type of ICMP packet is not supported.\r
190 \r
191 **/\r
192 STATIC\r
193 EFI_STATUS\r
194 IpIoIcmpHandler (\r
195   IN IP_IO                *IpIo,\r
196   IN NET_BUF              *Pkt,\r
197   IN EFI_NET_SESSION_DATA *Session\r
198   )\r
199 {\r
200   IP4_ICMP_ERROR_HEAD  *IcmpHdr;\r
201   EFI_IP4_HEADER       *IpHdr;\r
202   ICMP_ERROR           IcmpErr;\r
203   UINT8                *PayLoadHdr;\r
204   UINT8                Type;\r
205   UINT8                Code;\r
206   UINT32               TrimBytes;\r
207 \r
208   IcmpHdr = NET_PROTO_HDR (Pkt, IP4_ICMP_ERROR_HEAD);\r
209   IpHdr   = (EFI_IP4_HEADER *) (&IcmpHdr->IpHead);\r
210 \r
211   //\r
212   // Check the ICMP packet length.\r
213   //\r
214   if (Pkt->TotalSize < ICMP_ERRLEN (IpHdr)) {\r
215 \r
216     return EFI_ABORTED;\r
217   }\r
218 \r
219   Type = IcmpHdr->Head.Type;\r
220   Code = IcmpHdr->Head.Code;\r
221 \r
222   //\r
223   // Analyze the ICMP Error in this ICMP pkt\r
224   //\r
225   switch (Type) {\r
226   case ICMP_TYPE_UNREACH:\r
227     switch (Code) {\r
228     case ICMP_CODE_UNREACH_NET:\r
229     case ICMP_CODE_UNREACH_HOST:\r
230     case ICMP_CODE_UNREACH_PROTOCOL:\r
231     case ICMP_CODE_UNREACH_PORT:\r
232     case ICMP_CODE_UNREACH_SRCFAIL:\r
233       IcmpErr = (ICMP_ERROR) (ICMP_ERR_UNREACH_NET + Code);\r
234 \r
235       break;\r
236 \r
237     case ICMP_CODE_UNREACH_NEEDFRAG:\r
238       IcmpErr = ICMP_ERR_MSGSIZE;\r
239 \r
240       break;\r
241 \r
242     case ICMP_CODE_UNREACH_NET_UNKNOWN:\r
243     case ICMP_CODE_UNREACH_NET_PROHIB:\r
244     case ICMP_CODE_UNREACH_TOSNET:\r
245       IcmpErr = ICMP_ERR_UNREACH_NET;\r
246 \r
247       break;\r
248 \r
249     case ICMP_CODE_UNREACH_HOST_UNKNOWN:\r
250     case ICMP_CODE_UNREACH_ISOLATED:\r
251     case ICMP_CODE_UNREACH_HOST_PROHIB:\r
252     case ICMP_CODE_UNREACH_TOSHOST:\r
253       IcmpErr = ICMP_ERR_UNREACH_HOST;\r
254 \r
255       break;\r
256 \r
257     default:\r
258       return EFI_ABORTED;\r
259 \r
260       break;\r
261     }\r
262 \r
263     break;\r
264 \r
265   case ICMP_TYPE_TIMXCEED:\r
266     if (Code > 1) {\r
267       return EFI_ABORTED;\r
268     }\r
269 \r
270     IcmpErr = (ICMP_ERROR) (Code + ICMP_ERR_TIMXCEED_INTRANS);\r
271 \r
272     break;\r
273 \r
274   case ICMP_TYPE_PARAMPROB:\r
275     if (Code > 1) {\r
276       return EFI_ABORTED;\r
277     }\r
278 \r
279     IcmpErr = ICMP_ERR_PARAMPROB;\r
280 \r
281     break;\r
282 \r
283   case ICMP_TYPE_SOURCEQUENCH:\r
284     if (Code != 0) {\r
285       return EFI_ABORTED;\r
286     }\r
287 \r
288     IcmpErr = ICMP_ERR_QUENCH;\r
289 \r
290     break;\r
291 \r
292   default:\r
293     return EFI_ABORTED;\r
294 \r
295     break;\r
296   }\r
297 \r
298   //\r
299   // Notify user the ICMP pkt only containing payload except\r
300   // IP and ICMP header\r
301   //\r
302   PayLoadHdr = (UINT8 *) ((UINT8 *) IpHdr + EFI_IP4_HEADER_LEN (IpHdr));\r
303   TrimBytes  = (UINT32) (PayLoadHdr - (UINT8 *) IcmpHdr);\r
304 \r
305   NetbufTrim (Pkt, TrimBytes, TRUE);\r
306 \r
307   IpIo->PktRcvdNotify (EFI_ICMP_ERROR, IcmpErr, Session, Pkt, IpIo->RcvdContext);\r
308 \r
309   return EFI_SUCCESS;\r
310 }\r
311 \r
312 \r
313 /**\r
314   Ext free function for net buffer. This function is\r
315   called when the net buffer is freed. It is used to\r
316   signal the recycle event to notify IP to recycle the\r
317   data buffer.\r
318 \r
319   @param  Event                 The event to be signaled.\r
320 \r
321   @return None.\r
322 \r
323 **/\r
324 STATIC\r
325 VOID\r
326 IpIoExtFree (\r
327   IN VOID  *Event\r
328   )\r
329 {\r
330   gBS->SignalEvent ((EFI_EVENT) Event);\r
331 }\r
332 \r
333 \r
334 /**\r
335   Create a send entry to wrap a packet before sending\r
336   out it through IP.\r
337 \r
338   @param  IpIo                  Pointer to the IP_IO instance.\r
339   @param  Pkt                   Pointer to the packet.\r
340   @param  Sender                Pointer to the IP sender.\r
341   @param  NotifyData            Pointer to the notify data.\r
342   @param  Dest                  Pointer to the destination IP address.\r
343   @param  Override              Pointer to the overriden IP_IO data.\r
344 \r
345   @return Pointer to the data structure created to wrap the packet. If NULL,\r
346   @return resource limit occurred.\r
347 \r
348 **/\r
349 STATIC\r
350 IP_IO_SEND_ENTRY *\r
351 IpIoCreateSndEntry (\r
352   IN IP_IO             *IpIo,\r
353   IN NET_BUF           *Pkt,\r
354   IN EFI_IP4_PROTOCOL  *Sender,\r
355   IN VOID              *Context    OPTIONAL,\r
356   IN VOID              *NotifyData OPTIONAL,\r
357   IN IP4_ADDR          Dest,\r
358   IN IP_IO_OVERRIDE    *Override\r
359   )\r
360 {\r
361   IP_IO_SEND_ENTRY          *SndEntry;\r
362   EFI_IP4_COMPLETION_TOKEN  *SndToken;\r
363   EFI_IP4_TRANSMIT_DATA     *TxData;\r
364   EFI_STATUS                Status;\r
365   EFI_IP4_OVERRIDE_DATA     *OverrideData;\r
366   volatile UINT32           Index;\r
367 \r
368   //\r
369   // Allocate resource for SndEntry\r
370   //\r
371   SndEntry = NetAllocatePool (sizeof (IP_IO_SEND_ENTRY));\r
372   if (NULL == SndEntry) {\r
373     return NULL;\r
374   }\r
375 \r
376   //\r
377   // Allocate resource for SndToken\r
378   //\r
379   SndToken = NetAllocatePool (sizeof (EFI_IP4_COMPLETION_TOKEN));\r
380   if (NULL == SndToken) {\r
381     goto ReleaseSndEntry;\r
382   }\r
383 \r
384   Status = gBS->CreateEvent (\r
385                   EVT_NOTIFY_SIGNAL,\r
386                   NET_TPL_EVENT,\r
387                   IpIoTransmitHandler,\r
388                   SndEntry,\r
389                   &(SndToken->Event)\r
390                   );\r
391   if (EFI_ERROR (Status)) {\r
392     goto ReleaseSndToken;\r
393   }\r
394 \r
395   //\r
396   // Allocate resource for TxData\r
397   //\r
398   TxData = NetAllocatePool (\r
399     sizeof (EFI_IP4_TRANSMIT_DATA) +\r
400     sizeof (EFI_IP4_FRAGMENT_DATA) * (Pkt->BlockOpNum - 1)\r
401     );\r
402 \r
403   if (NULL == TxData) {\r
404     goto ReleaseEvent;\r
405   }\r
406 \r
407   //\r
408   // Allocate resource for OverrideData if needed\r
409   //\r
410   OverrideData = NULL;\r
411   if (NULL != Override) {\r
412 \r
413     OverrideData = NetAllocatePool (sizeof (EFI_IP4_OVERRIDE_DATA));\r
414     if (NULL == OverrideData) {\r
415       goto ReleaseResource;\r
416     }\r
417     //\r
418     // Set the fields of OverrideData\r
419     //\r
420     NetCopyMem (OverrideData, Override, sizeof (*OverrideData));\r
421   }\r
422 \r
423   //\r
424   // Set the fields of TxData\r
425   //\r
426   NetCopyMem (&TxData->DestinationAddress, &Dest, sizeof (EFI_IPv4_ADDRESS));\r
427   TxData->OverrideData                  = OverrideData;\r
428   TxData->OptionsLength                 = 0;\r
429   TxData->OptionsBuffer                 = NULL;\r
430   TxData->TotalDataLength               = Pkt->TotalSize;\r
431   TxData->FragmentCount                 = Pkt->BlockOpNum;\r
432 \r
433 \r
434   for (Index = 0; Index < Pkt->BlockOpNum; Index++) {\r
435     TxData->FragmentTable[Index].FragmentBuffer = Pkt->BlockOp[Index].Head;\r
436     TxData->FragmentTable[Index].FragmentLength = Pkt->BlockOp[Index].Size;\r
437   }\r
438 \r
439   //\r
440   // Set the fields of SndToken\r
441   //\r
442   SndToken->Packet.TxData = TxData;\r
443 \r
444   //\r
445   // Set the fields of SndEntry\r
446   //\r
447   SndEntry->IpIo        = IpIo;\r
448   SndEntry->Ip      = Sender;\r
449   SndEntry->Context     = Context;\r
450   SndEntry->NotifyData  = NotifyData;\r
451 \r
452   SndEntry->Pkt         = Pkt;\r
453   NET_GET_REF (Pkt);\r
454 \r
455   SndEntry->SndToken = SndToken;\r
456 \r
457   NetListInsertTail (&IpIo->PendingSndList, &SndEntry->Entry);\r
458 \r
459   return SndEntry;\r
460 \r
461 ReleaseResource:\r
462   NetFreePool (TxData);\r
463 \r
464 ReleaseEvent:\r
465   gBS->CloseEvent (SndToken->Event);\r
466 \r
467 ReleaseSndToken:\r
468   NetFreePool (SndToken);\r
469 \r
470 ReleaseSndEntry:\r
471   NetFreePool (SndEntry);\r
472 \r
473   return NULL;\r
474 }\r
475 \r
476 \r
477 /**\r
478   Destroy the SndEntry.\r
479 \r
480   @param  SndEntry              Pointer to the send entry to be destroyed.\r
481 \r
482   @return None.\r
483 \r
484 **/\r
485 STATIC\r
486 VOID\r
487 IpIoDestroySndEntry (\r
488   IN IP_IO_SEND_ENTRY  *SndEntry\r
489   )\r
490 {\r
491   EFI_IP4_TRANSMIT_DATA  *TxData;\r
492 \r
493   TxData = SndEntry->SndToken->Packet.TxData;\r
494 \r
495   if (NULL != TxData->OverrideData) {\r
496     NetFreePool (TxData->OverrideData);\r
497   }\r
498 \r
499   NetFreePool (TxData);\r
500   NetbufFree (SndEntry->Pkt);\r
501   gBS->CloseEvent (SndEntry->SndToken->Event);\r
502 \r
503   NetFreePool (SndEntry->SndToken);\r
504   NetListRemoveEntry (&SndEntry->Entry);\r
505 \r
506   NetFreePool (SndEntry);\r
507 }\r
508 \r
509 \r
510 /**\r
511   Notify function for IP transmit token.\r
512 \r
513   @param  Event                 The event signaled.\r
514   @param  Context               The context passed in by the event notifier.\r
515 \r
516   @return None.\r
517 \r
518 **/\r
519 STATIC\r
520 VOID\r
521 EFIAPI\r
522 IpIoTransmitHandler (\r
523   IN EFI_EVENT Event,\r
524   IN VOID      *Context\r
525   )\r
526 {\r
527   IP_IO             *IpIo;\r
528   IP_IO_SEND_ENTRY  *SndEntry;\r
529 \r
530   SndEntry  = (IP_IO_SEND_ENTRY *) Context;\r
531 \r
532   IpIo      = SndEntry->IpIo;\r
533 \r
534   if (IpIo->PktSentNotify && SndEntry->NotifyData) {\r
535     IpIo->PktSentNotify (\r
536             SndEntry->SndToken->Status,\r
537             SndEntry->Context,\r
538             SndEntry->Ip,\r
539             SndEntry->NotifyData\r
540             );\r
541   }\r
542 \r
543   IpIoDestroySndEntry (SndEntry);\r
544 }\r
545 \r
546 \r
547 /**\r
548   The dummy handler for the dummy IP receive token.\r
549 \r
550   @param  Evt                   The event signaled.\r
551   @param  Context               The context passed in by the event notifier.\r
552 \r
553   @return None.\r
554 \r
555 **/\r
556 STATIC\r
557 VOID\r
558 EFIAPI\r
559 IpIoDummyHandler (\r
560   IN EFI_EVENT Event,\r
561   IN VOID      *Context\r
562   )\r
563 {\r
564   IP_IO_IP_INFO             *IpInfo;\r
565   EFI_IP4_COMPLETION_TOKEN  *DummyToken;\r
566 \r
567   ASSERT (Event && Context);\r
568 \r
569   IpInfo      = (IP_IO_IP_INFO *) Context;\r
570   DummyToken  = &(IpInfo->DummyRcvToken);\r
571 \r
572   if (EFI_SUCCESS == DummyToken->Status) {\r
573     ASSERT (DummyToken->Packet.RxData);\r
574 \r
575     gBS->SignalEvent (DummyToken->Packet.RxData->RecycleSignal);\r
576   }\r
577 \r
578   IpInfo->Ip->Receive (IpInfo->Ip, DummyToken);\r
579 }\r
580 \r
581 \r
582 /**\r
583   Notify function for the IP receive token, used to process\r
584   the received IP packets.\r
585 \r
586   @param  Event                 The event signaled.\r
587   @param  Context               The context passed in by the event notifier.\r
588 \r
589   @return None.\r
590 \r
591 **/\r
592 STATIC\r
593 VOID\r
594 EFIAPI\r
595 IpIoListenHandler (\r
596   IN EFI_EVENT Event,\r
597   IN VOID      *Context\r
598   )\r
599 {\r
600   IP_IO                 *IpIo;\r
601   EFI_STATUS            Status;\r
602   EFI_IP4_RECEIVE_DATA  *RxData;\r
603   EFI_IP4_PROTOCOL      *Ip;\r
604   EFI_NET_SESSION_DATA  Session;\r
605   NET_BUF               *Pkt;\r
606 \r
607   IpIo    = (IP_IO *) Context;\r
608 \r
609   Ip      = IpIo->Ip;\r
610   Status  = IpIo->RcvToken.Status;\r
611   RxData  = IpIo->RcvToken.Packet.RxData;\r
612 \r
613   if (((EFI_SUCCESS != Status) && (EFI_ICMP_ERROR != Status)) || (NULL == RxData)) {\r
614     //\r
615     // Only process the normal packets and the icmp error packets, if RxData is NULL\r
616     // with Status == EFI_SUCCESS or EFI_ICMP_ERROR, just resume the receive although\r
617     // this should be a bug of the low layer (IP).\r
618     //\r
619     goto Resume;\r
620   }\r
621 \r
622   if (NULL == IpIo->PktRcvdNotify) {\r
623     goto CleanUp;\r
624   }\r
625 \r
626   if ((EFI_IP4 (RxData->Header->SourceAddress) != 0) &&\r
627     !Ip4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) {\r
628     //\r
629     // The source address is not zero and it's not a unicast IP address, discard it.\r
630     //\r
631     goto CleanUp;\r
632   }\r
633 \r
634   //\r
635   // Create a netbuffer representing packet\r
636   //\r
637   Pkt = NetbufFromExt (\r
638           (NET_FRAGMENT *) RxData->FragmentTable,\r
639           RxData->FragmentCount,\r
640           0,\r
641           0,\r
642           IpIoExtFree,\r
643           RxData->RecycleSignal\r
644           );\r
645   if (NULL == Pkt) {\r
646     goto CleanUp;\r
647   }\r
648 \r
649   //\r
650   // Create a net session\r
651   //\r
652   Session.Source = EFI_IP4 (RxData->Header->SourceAddress);\r
653   Session.Dest   = EFI_IP4 (RxData->Header->DestinationAddress);\r
654   Session.IpHdr  = RxData->Header;\r
655 \r
656   if (EFI_SUCCESS == Status) {\r
657 \r
658     IpIo->PktRcvdNotify (EFI_SUCCESS, (ICMP_ERROR) 0, &Session, Pkt, IpIo->RcvdContext);\r
659   } else {\r
660     //\r
661     // Status is EFI_ICMP_ERROR\r
662     //\r
663     Status = IpIoIcmpHandler (IpIo, Pkt, &Session);\r
664     if (EFI_ERROR (Status)) {\r
665       NetbufFree (Pkt);\r
666     }\r
667   }\r
668 \r
669   goto Resume;\r
670 \r
671 CleanUp:\r
672   gBS->SignalEvent (RxData->RecycleSignal);\r
673 \r
674 Resume:\r
675   Ip->Receive (Ip, &(IpIo->RcvToken));\r
676 }\r
677 \r
678 \r
679 /**\r
680   Create a new IP_IO instance.\r
681 \r
682   @param  Image                 The image handle of an IP_IO consumer protocol.\r
683   @param  Controller            The controller handle of an IP_IO consumer protocol\r
684                                 installed on.\r
685 \r
686   @return Pointer to a newly created IP_IO instance.\r
687 \r
688 **/\r
689 IP_IO *\r
690 IpIoCreate (\r
691   IN EFI_HANDLE Image,\r
692   IN EFI_HANDLE Controller\r
693   )\r
694 {\r
695   EFI_STATUS  Status;\r
696   IP_IO       *IpIo;\r
697 \r
698   IpIo = NetAllocateZeroPool (sizeof (IP_IO));\r
699   if (NULL == IpIo) {\r
700     return NULL;\r
701   }\r
702 \r
703   NetListInit (&(IpIo->PendingSndList));\r
704   NetListInit (&(IpIo->IpList));\r
705   IpIo->Controller  = Controller;\r
706   IpIo->Image       = Image;\r
707 \r
708   Status = gBS->CreateEvent (\r
709                   EVT_NOTIFY_SIGNAL,\r
710                   NET_TPL_EVENT,\r
711                   IpIoListenHandler,\r
712                   IpIo,\r
713                   &(IpIo->RcvToken.Event)\r
714                   );\r
715   if (EFI_ERROR (Status)) {\r
716     goto ReleaseIpIo;\r
717   }\r
718 \r
719   //\r
720   // Create an IP child and open IP protocol\r
721   //\r
722   Status = IpIoCreateIpChildOpenProtocol (\r
723              Controller,\r
724              Image,\r
725              &IpIo->ChildHandle,\r
726              (VOID **)&(IpIo->Ip)\r
727              );\r
728   if (EFI_ERROR (Status)) {\r
729     goto ReleaseIpIo;\r
730   }\r
731 \r
732   return IpIo;\r
733 \r
734 ReleaseIpIo:\r
735 \r
736   if (NULL != IpIo->RcvToken.Event) {\r
737     gBS->CloseEvent (IpIo->RcvToken.Event);\r
738   }\r
739 \r
740   NetFreePool (IpIo);\r
741 \r
742   return NULL;\r
743 }\r
744 \r
745 \r
746 /**\r
747   Open an IP_IO instance for use.\r
748 \r
749   @param  IpIo                  Pointer to an IP_IO instance that needs to open.\r
750   @param  OpenData              The configuration data for the IP_IO instance.\r
751 \r
752   @retval EFI_SUCCESS           The IP_IO instance opened with OpenData\r
753                                 successfully.\r
754   @retval other                 Error condition occurred.\r
755 \r
756 **/\r
757 EFI_STATUS\r
758 IpIoOpen (\r
759   IN IP_IO           *IpIo,\r
760   IN IP_IO_OPEN_DATA *OpenData\r
761   )\r
762 {\r
763   EFI_STATUS        Status;\r
764   EFI_IP4_PROTOCOL  *Ip;\r
765 \r
766   if (IpIo->IsConfigured) {\r
767     return EFI_ACCESS_DENIED;\r
768   }\r
769 \r
770   Ip = IpIo->Ip;\r
771 \r
772   //\r
773   // configure ip\r
774   //\r
775   Status = Ip->Configure (Ip, &OpenData->IpConfigData);\r
776   if (EFI_ERROR (Status)) {\r
777     return Status;\r
778   }\r
779 \r
780   //\r
781   // bugbug: to delete the default route entry in this Ip, if it is:\r
782   // (0.0.0.0, 0.0.0.0, 0.0.0.0). Delete this statement if Ip modified\r
783   // its code\r
784   //\r
785   Status = Ip->Routes (Ip, TRUE, &mZeroIp4Addr, &mZeroIp4Addr, &mZeroIp4Addr);\r
786 \r
787   if (EFI_ERROR (Status) && (EFI_NOT_FOUND != Status)) {\r
788     return Status;\r
789   }\r
790 \r
791   IpIo->PktRcvdNotify = OpenData->PktRcvdNotify;\r
792   IpIo->PktSentNotify = OpenData->PktSentNotify;\r
793 \r
794   IpIo->RcvdContext   = OpenData->RcvdContext;\r
795   IpIo->SndContext    = OpenData->SndContext;\r
796 \r
797   IpIo->Protocol      = OpenData->IpConfigData.DefaultProtocol;\r
798 \r
799   //\r
800   // start to listen incoming packet\r
801   //\r
802   Status = Ip->Receive (Ip, &(IpIo->RcvToken));\r
803   if (EFI_ERROR (Status)) {\r
804     Ip->Configure (Ip, NULL);\r
805     goto ErrorExit;\r
806   }\r
807 \r
808   IpIo->IsConfigured = TRUE;\r
809   NetListInsertTail (&mActiveIpIoList, &IpIo->Entry);\r
810 \r
811 ErrorExit:\r
812 \r
813   return Status;\r
814 }\r
815 \r
816 \r
817 /**\r
818   Stop an IP_IO instance.\r
819 \r
820   @param  IpIo                  Pointer to the IP_IO instance that needs to stop.\r
821 \r
822   @retval EFI_SUCCESS           The IP_IO instance stopped successfully.\r
823   @retval other                 Error condition occurred.\r
824 \r
825 **/\r
826 EFI_STATUS\r
827 IpIoStop (\r
828   IN IP_IO *IpIo\r
829   )\r
830 {\r
831   EFI_STATUS        Status;\r
832   EFI_IP4_PROTOCOL  *Ip;\r
833   IP_IO_IP_INFO     *IpInfo;\r
834 \r
835   if (!IpIo->IsConfigured) {\r
836     return EFI_SUCCESS;\r
837   }\r
838 \r
839   //\r
840   // Remove the IpIo from the active IpIo list.\r
841   //\r
842   NetListRemoveEntry (&IpIo->Entry);\r
843 \r
844   Ip = IpIo->Ip;\r
845 \r
846   //\r
847   // Configure NULL Ip\r
848   //\r
849   Status = Ip->Configure (Ip, NULL);\r
850   if (EFI_ERROR (Status)) {\r
851     return Status;\r
852   }\r
853 \r
854   IpIo->IsConfigured = FALSE;\r
855 \r
856   //\r
857   // Detroy the Ip List used by IpIo\r
858   //\r
859 \r
860   while (!NetListIsEmpty (&(IpIo->IpList))) {\r
861     IpInfo = NET_LIST_HEAD (&(IpIo->IpList), IP_IO_IP_INFO, Entry);\r
862 \r
863     IpIoRemoveIp (IpIo, IpInfo);\r
864   }\r
865 \r
866   //\r
867   // All pending snd tokens should be flushed by reseting the IP instances.\r
868   //\r
869   ASSERT (NetListIsEmpty (&IpIo->PendingSndList));\r
870 \r
871   //\r
872   // Close the receive event.\r
873   //\r
874   gBS->CloseEvent (IpIo->RcvToken.Event);\r
875 \r
876   return EFI_SUCCESS;\r
877 }\r
878 \r
879 \r
880 /**\r
881   Destroy an IP_IO instance.\r
882 \r
883   @param  IpIo                  Pointer to the IP_IO instance that needs to\r
884                                 destroy.\r
885 \r
886   @retval EFI_SUCCESS           The IP_IO instance destroyed successfully.\r
887   @retval other                 Error condition occurred.\r
888 \r
889 **/\r
890 EFI_STATUS\r
891 IpIoDestroy (\r
892   IN IP_IO *IpIo\r
893   )\r
894 {\r
895   //\r
896   // Stop the IpIo.\r
897   //\r
898   IpIoStop (IpIo);\r
899 \r
900   //\r
901   // Close the IP protocol and destroy the child.\r
902   //\r
903   IpIoCloseProtocolDestroyIpChild (IpIo->Controller, IpIo->Image, IpIo->ChildHandle);\r
904 \r
905   NetFreePool (IpIo);\r
906 \r
907   return EFI_SUCCESS;\r
908 }\r
909 \r
910 \r
911 /**\r
912   Send out an IP packet.\r
913 \r
914   @param  IpIo                  Pointer to an IP_IO instance used for sending IP\r
915                                 packet.\r
916   @param  Pkt                   Pointer to the IP packet to be sent.\r
917   @param  Sender                The IP protocol instance used for sending.\r
918   @param  NotifyData\r
919   @param  Dest                  The destination IP address to send this packet to.\r
920   @param  OverrideData          The data to override some configuration of the IP\r
921                                 instance used for sending.\r
922 \r
923   @retval EFI_SUCCESS           The operation is completed successfully.\r
924   @retval EFI_NOT_STARTED       The IpIo is not configured.\r
925   @retval EFI_OUT_OF_RESOURCES  Failed due to resource limit.\r
926 \r
927 **/\r
928 EFI_STATUS\r
929 IpIoSend (\r
930   IN IP_IO           *IpIo,\r
931   IN NET_BUF         *Pkt,\r
932   IN IP_IO_IP_INFO   *Sender,\r
933   IN VOID            *Context    OPTIONAL,\r
934   IN VOID            *NotifyData OPTIONAL,\r
935   IN IP4_ADDR        Dest,\r
936   IN IP_IO_OVERRIDE  *OverrideData\r
937   )\r
938 {\r
939   EFI_STATUS        Status;\r
940   EFI_IP4_PROTOCOL  *Ip;\r
941   IP_IO_SEND_ENTRY  *SndEntry;\r
942 \r
943   if (!IpIo->IsConfigured) {\r
944     return EFI_NOT_STARTED;\r
945   }\r
946 \r
947   Ip = (NULL == Sender) ? IpIo->Ip : Sender->Ip;\r
948 \r
949   //\r
950   // create a new SndEntry\r
951   //\r
952   SndEntry = IpIoCreateSndEntry (IpIo, Pkt, Ip, Context, NotifyData, Dest, OverrideData);\r
953   if (NULL == SndEntry) {\r
954     return EFI_OUT_OF_RESOURCES;\r
955   }\r
956 \r
957   //\r
958   // Send this Packet\r
959   //\r
960   Status = Ip->Transmit (Ip, SndEntry->SndToken);\r
961   if (EFI_ERROR (Status)) {\r
962     IpIoDestroySndEntry (SndEntry);\r
963   }\r
964 \r
965   return Status;\r
966 }\r
967 \r
968 \r
969 /**\r
970   Cancel the IP transmit token which wraps this Packet.\r
971 \r
972   @param  IpIo                  Pointer to the IP_IO instance.\r
973   @param  Packet                Pointer to the packet to cancel.\r
974 \r
975   @return N/A.\r
976 \r
977 **/\r
978 VOID\r
979 IpIoCancelTxToken (\r
980   IN IP_IO  *IpIo,\r
981   IN VOID   *Packet\r
982   )\r
983 {\r
984   NET_LIST_ENTRY    *Node;\r
985   IP_IO_SEND_ENTRY  *SndEntry;\r
986   EFI_IP4_PROTOCOL  *Ip;\r
987 \r
988   ASSERT (IpIo && Packet);\r
989 \r
990   NET_LIST_FOR_EACH (Node, &IpIo->PendingSndList) {\r
991 \r
992     SndEntry = NET_LIST_USER_STRUCT (Node, IP_IO_SEND_ENTRY, Entry);\r
993 \r
994     if (SndEntry->Pkt == Packet) {\r
995 \r
996       Ip = SndEntry->Ip;\r
997       Ip->Cancel (Ip, SndEntry->SndToken);\r
998 \r
999       //\r
1000       // Abort the user token.\r
1001       //\r
1002       SndEntry->SndToken->Status = EFI_ABORTED;\r
1003       IpIoTransmitHandler (NULL, SndEntry);\r
1004 \r
1005       break;\r
1006     }\r
1007   }\r
1008 \r
1009 }\r
1010 \r
1011 \r
1012 /**\r
1013   Add a new IP instance for sending data.\r
1014 \r
1015   @param  IpIo                  Pointer to a IP_IO instance to add a new IP\r
1016                                 instance for sending purpose.\r
1017 \r
1018   @return Pointer to the created IP_IO_IP_INFO structure, NULL is failed.\r
1019 \r
1020 **/\r
1021 IP_IO_IP_INFO *\r
1022 IpIoAddIp (\r
1023   IN IP_IO  *IpIo\r
1024   )\r
1025 {\r
1026   EFI_STATUS     Status;\r
1027   IP_IO_IP_INFO  *IpInfo;\r
1028 \r
1029   ASSERT (IpIo);\r
1030 \r
1031   IpInfo = NetAllocatePool (sizeof (IP_IO_IP_INFO));\r
1032   if (IpInfo == NULL) {\r
1033     return IpInfo;\r
1034   }\r
1035 \r
1036   //\r
1037   // Init this IpInfo, set the Addr and SubnetMask to 0 before we configure the IP\r
1038   // instance.\r
1039   //\r
1040   NetListInit (&IpInfo->Entry);\r
1041   IpInfo->ChildHandle = NULL;\r
1042   IpInfo->Addr        = 0;\r
1043   IpInfo->SubnetMask  = 0;\r
1044   IpInfo->RefCnt      = 1;\r
1045 \r
1046   //\r
1047   // Create the IP instance and open the Ip4 protocol.\r
1048   //\r
1049   Status = IpIoCreateIpChildOpenProtocol (\r
1050              IpIo->Controller,\r
1051              IpIo->Image,\r
1052              &IpInfo->ChildHandle,\r
1053              (VOID **) &IpInfo->Ip\r
1054              );\r
1055   if (EFI_ERROR (Status)) {\r
1056     goto ReleaseIpInfo;\r
1057   }\r
1058 \r
1059   //\r
1060   // Create the event for the DummyRcvToken.\r
1061   //\r
1062   Status = gBS->CreateEvent (\r
1063                   EVT_NOTIFY_SIGNAL,\r
1064                   NET_TPL_EVENT,\r
1065                   IpIoDummyHandler,\r
1066                   IpInfo,\r
1067                   &IpInfo->DummyRcvToken.Event\r
1068                   );\r
1069   if (EFI_ERROR (Status)) {\r
1070     goto ReleaseIpChild;\r
1071   }\r
1072 \r
1073   //\r
1074   // Link this IpInfo into the IpIo.\r
1075   //\r
1076   NetListInsertTail (&IpIo->IpList, &IpInfo->Entry);\r
1077 \r
1078   return IpInfo;\r
1079 \r
1080 ReleaseIpChild:\r
1081 \r
1082   IpIoCloseProtocolDestroyIpChild (\r
1083     IpIo->Controller,\r
1084     IpIo->Image,\r
1085     IpInfo->ChildHandle\r
1086     );\r
1087 \r
1088 ReleaseIpInfo:\r
1089 \r
1090   NetFreePool (IpInfo);\r
1091 \r
1092   return NULL;\r
1093 }\r
1094 \r
1095 \r
1096 /**\r
1097   Configure the IP instance of this IpInfo and start the receiving if Ip4ConfigData\r
1098   is not NULL.\r
1099 \r
1100   @param  IpInfo                Pointer to the IP_IO_IP_INFO instance.\r
1101   @param  Ip4ConfigData         The IP4 configure data used to configure the ip\r
1102                                 instance, if NULL the ip instance is reseted. If\r
1103                                 UseDefaultAddress is set to TRUE, and the configure\r
1104                                 operation succeeds, the default address information\r
1105                                 is written back in this Ip4ConfigData.\r
1106 \r
1107   @retval EFI_STATUS            The status returned by IP4->Configure or\r
1108                                 IP4->Receive.\r
1109 \r
1110 **/\r
1111 EFI_STATUS\r
1112 IpIoConfigIp (\r
1113   IN     IP_IO_IP_INFO        *IpInfo,\r
1114   IN OUT EFI_IP4_CONFIG_DATA  *Ip4ConfigData OPTIONAL\r
1115   )\r
1116 {\r
1117   EFI_STATUS         Status;\r
1118   EFI_IP4_PROTOCOL   *Ip;\r
1119   EFI_IP4_MODE_DATA  Ip4ModeData;\r
1120 \r
1121   ASSERT (IpInfo);\r
1122 \r
1123   if (IpInfo->RefCnt > 1) {\r
1124     //\r
1125     // This IP instance is shared, don't reconfigure it until it has only one\r
1126     // consumer. Currently, only the tcp children cloned from their passive parent\r
1127     // will share the same IP. So this cases only happens while Ip4ConfigData is NULL,\r
1128     // let the last consumer clean the IP instance.\r
1129     //\r
1130     return EFI_SUCCESS;\r
1131   }\r
1132 \r
1133   Ip = IpInfo->Ip;\r
1134 \r
1135   Status = Ip->Configure (Ip, Ip4ConfigData);\r
1136   if (EFI_ERROR (Status)) {\r
1137     goto OnExit;\r
1138   }\r
1139 \r
1140   if (Ip4ConfigData != NULL) {\r
1141 \r
1142     if (Ip4ConfigData->UseDefaultAddress) {\r
1143       Ip->GetModeData (Ip, &Ip4ModeData, NULL, NULL);\r
1144 \r
1145       Ip4ConfigData->StationAddress = Ip4ModeData.ConfigData.StationAddress;\r
1146       Ip4ConfigData->SubnetMask     = Ip4ModeData.ConfigData.SubnetMask;\r
1147     }\r
1148 \r
1149     NetCopyMem (&IpInfo->Addr, &Ip4ConfigData->StationAddress, sizeof (IP4_ADDR));\r
1150     NetCopyMem (&IpInfo->SubnetMask, &Ip4ConfigData->SubnetMask, sizeof (IP4_ADDR));\r
1151 \r
1152     Status = Ip->Receive (Ip, &IpInfo->DummyRcvToken);\r
1153     if (EFI_ERROR (Status)) {\r
1154       Ip->Configure (Ip, NULL);\r
1155     }\r
1156   } else {\r
1157 \r
1158     //\r
1159     // The IP instance is reseted, set the stored Addr and SubnetMask to zero.\r
1160     //\r
1161     IpInfo->Addr       = 0;\r
1162     IpInfo->SubnetMask =0;\r
1163   }\r
1164 \r
1165 OnExit:\r
1166 \r
1167   return Status;\r
1168 }\r
1169 \r
1170 \r
1171 /**\r
1172   Destroy an IP instance maintained in IpIo->IpList for\r
1173   sending purpose.\r
1174 \r
1175   @param  IpIo                  Pointer to the IP_IO instance.\r
1176   @param  IpInfo                Pointer to the IpInfo to be removed.\r
1177 \r
1178   @return None.\r
1179 \r
1180 **/\r
1181 VOID\r
1182 IpIoRemoveIp (\r
1183   IN IP_IO          *IpIo,\r
1184   IN IP_IO_IP_INFO  *IpInfo\r
1185   )\r
1186 {\r
1187   ASSERT (IpInfo->RefCnt > 0);\r
1188 \r
1189   NET_PUT_REF (IpInfo);\r
1190 \r
1191   if (IpInfo->RefCnt > 0) {\r
1192 \r
1193     return;\r
1194   }\r
1195 \r
1196   NetListRemoveEntry (&IpInfo->Entry);\r
1197 \r
1198   IpInfo->Ip->Configure (IpInfo->Ip, NULL);\r
1199 \r
1200   IpIoCloseProtocolDestroyIpChild (IpIo->Controller, IpIo->Image, IpInfo->ChildHandle);\r
1201 \r
1202   gBS->CloseEvent (IpInfo->DummyRcvToken.Event);\r
1203 \r
1204   NetFreePool (IpInfo);\r
1205 }\r
1206 \r
1207 \r
1208 /**\r
1209   Find the first IP protocol maintained in IpIo whose local\r
1210   address is the same with Src.\r
1211 \r
1212   @param  IpIo                  Pointer to the pointer of the IP_IO instance.\r
1213   @param  Src                   The local IP address.\r
1214 \r
1215   @return Pointer to the IP protocol can be used for sending purpose and its local\r
1216   @return address is the same with Src.\r
1217 \r
1218 **/\r
1219 IP_IO_IP_INFO *\r
1220 IpIoFindSender (\r
1221   IN OUT IP_IO     **IpIo,\r
1222   IN     IP4_ADDR  Src\r
1223   )\r
1224 {\r
1225   NET_LIST_ENTRY  *IpIoEntry;\r
1226   IP_IO           *IpIoPtr;\r
1227   NET_LIST_ENTRY  *IpInfoEntry;\r
1228   IP_IO_IP_INFO   *IpInfo;\r
1229 \r
1230   NET_LIST_FOR_EACH (IpIoEntry, &mActiveIpIoList) {\r
1231     IpIoPtr = NET_LIST_USER_STRUCT (IpIoEntry, IP_IO, Entry);\r
1232 \r
1233     if ((*IpIo != NULL) && (*IpIo != IpIoPtr)) {\r
1234       continue;\r
1235     }\r
1236 \r
1237     NET_LIST_FOR_EACH (IpInfoEntry, &IpIoPtr->IpList) {\r
1238       IpInfo = NET_LIST_USER_STRUCT (IpInfoEntry, IP_IO_IP_INFO, Entry);\r
1239 \r
1240       if (IpInfo->Addr == Src) {\r
1241         *IpIo = IpIoPtr;\r
1242         return IpInfo;\r
1243       }\r
1244     }\r
1245   }\r
1246 \r
1247   //\r
1248   // No match.\r
1249   //\r
1250   return NULL;\r
1251 }\r
1252 \r
1253 \r
1254 /**\r
1255   Get the ICMP error map information, the ErrorStatus will be returned.\r
1256   The IsHard and Notify are optional. If they are not NULL, this rouine will\r
1257   fill them.\r
1258   We move IcmpErrMap[] to local variable to enable EBC build.\r
1259 \r
1260   @param  IcmpError             IcmpError Type\r
1261   @param  IsHard                Whether it is a hard error\r
1262   @param  Notify                Whether it need to notify SockError\r
1263 \r
1264   @return ICMP Error Status\r
1265 \r
1266 **/\r
1267 EFI_STATUS\r
1268 IpIoGetIcmpErrStatus (\r
1269   IN  ICMP_ERROR  IcmpError,\r
1270   OUT BOOLEAN     *IsHard, OPTIONAL\r
1271   OUT BOOLEAN     *Notify OPTIONAL\r
1272   )\r
1273 {\r
1274   ICMP_ERROR_INFO  IcmpErrMap[10];\r
1275 \r
1276   IcmpErrMap[0].Error  = EFI_NETWORK_UNREACHABLE;\r
1277   IcmpErrMap[0].IsHard = FALSE;\r
1278   IcmpErrMap[0].Notify = TRUE;\r
1279 \r
1280   IcmpErrMap[1].Error = EFI_HOST_UNREACHABLE;\r
1281   IcmpErrMap[1].IsHard = FALSE;\r
1282   IcmpErrMap[1].Notify = TRUE;\r
1283 \r
1284   IcmpErrMap[2].Error = EFI_PROTOCOL_UNREACHABLE;\r
1285   IcmpErrMap[2].IsHard = TRUE;\r
1286   IcmpErrMap[2].Notify = TRUE;\r
1287 \r
1288   IcmpErrMap[3].Error = EFI_PORT_UNREACHABLE;\r
1289   IcmpErrMap[3].IsHard = TRUE;\r
1290   IcmpErrMap[3].Notify = TRUE;\r
1291 \r
1292   IcmpErrMap[4].Error = EFI_ICMP_ERROR;\r
1293   IcmpErrMap[4].IsHard = TRUE;\r
1294   IcmpErrMap[4].Notify = TRUE;\r
1295 \r
1296   IcmpErrMap[5].Error = EFI_ICMP_ERROR;\r
1297   IcmpErrMap[5].IsHard = FALSE;\r
1298   IcmpErrMap[5].Notify = TRUE;\r
1299 \r
1300   IcmpErrMap[6].Error = EFI_HOST_UNREACHABLE;\r
1301   IcmpErrMap[6].IsHard = FALSE;\r
1302   IcmpErrMap[6].Notify = TRUE;\r
1303 \r
1304   IcmpErrMap[7].Error = EFI_HOST_UNREACHABLE;\r
1305   IcmpErrMap[7].IsHard = FALSE;\r
1306   IcmpErrMap[7].Notify = TRUE;\r
1307 \r
1308   IcmpErrMap[8].Error = EFI_ICMP_ERROR;\r
1309   IcmpErrMap[8].IsHard = FALSE;\r
1310   IcmpErrMap[8].Notify = FALSE;\r
1311 \r
1312   IcmpErrMap[9].Error = EFI_ICMP_ERROR;\r
1313   IcmpErrMap[9].IsHard = FALSE;\r
1314   IcmpErrMap[9].Notify = TRUE;\r
1315 \r
1316   ASSERT ((IcmpError >= ICMP_ERR_UNREACH_NET) && (IcmpError <= ICMP_ERR_PARAMPROB));\r
1317 \r
1318   if (IsHard != NULL) {\r
1319     *IsHard = IcmpErrMap[IcmpError].IsHard;\r
1320   }\r
1321 \r
1322   if (Notify != NULL) {\r
1323     *Notify = IcmpErrMap[IcmpError].Notify;\r
1324   }\r
1325 \r
1326   return IcmpErrMap[IcmpError].Error;\r
1327 }\r
1328 \r