034031d09f68ecfc0a0ac5bdca23de3de2daead0
[mirror_edk2.git] / MdeModulePkg / Library / DxeNetLib / DxeNetLib.c
1 /** @file\r
2   Network library.\r
3 \r
4 Copyright (c) 2005 - 2010, Intel Corporation.<BR>\r
5 All rights reserved. This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution.  The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9 \r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 **/\r
13 \r
14 #include <Uefi.h>\r
15 \r
16 #include <Protocol/DriverBinding.h>\r
17 #include <Protocol/ServiceBinding.h>\r
18 #include <Protocol/SimpleNetwork.h>\r
19 #include <Protocol/ManagedNetwork.h>\r
20 #include <Protocol/HiiConfigRouting.h>\r
21 #include <Protocol/ComponentName.h>\r
22 #include <Protocol/ComponentName2.h>\r
23 \r
24 #include <Guid/NicIp4ConfigNvData.h>\r
25 \r
26 #include <Library/NetLib.h>\r
27 #include <Library/BaseLib.h>\r
28 #include <Library/DebugLib.h>\r
29 #include <Library/BaseMemoryLib.h>\r
30 #include <Library/UefiBootServicesTableLib.h>\r
31 #include <Library/UefiRuntimeServicesTableLib.h>\r
32 #include <Library/MemoryAllocationLib.h>\r
33 #include <Library/DevicePathLib.h>\r
34 #include <Library/HiiLib.h>\r
35 #include <Library/PrintLib.h>\r
36 \r
37 #define NIC_ITEM_CONFIG_SIZE   sizeof (NIC_IP4_CONFIG_INFO) + sizeof (EFI_IP4_ROUTE_TABLE) * MAX_IP4_CONFIG_IN_VARIABLE\r
38 \r
39 //\r
40 // All the supported IP4 maskes in host byte order.\r
41 //\r
42 GLOBAL_REMOVE_IF_UNREFERENCED IP4_ADDR  gIp4AllMasks[IP4_MASK_NUM] = {\r
43   0x00000000,\r
44   0x80000000,\r
45   0xC0000000,\r
46   0xE0000000,\r
47   0xF0000000,\r
48   0xF8000000,\r
49   0xFC000000,\r
50   0xFE000000,\r
51 \r
52   0xFF000000,\r
53   0xFF800000,\r
54   0xFFC00000,\r
55   0xFFE00000,\r
56   0xFFF00000,\r
57   0xFFF80000,\r
58   0xFFFC0000,\r
59   0xFFFE0000,\r
60 \r
61   0xFFFF0000,\r
62   0xFFFF8000,\r
63   0xFFFFC000,\r
64   0xFFFFE000,\r
65   0xFFFFF000,\r
66   0xFFFFF800,\r
67   0xFFFFFC00,\r
68   0xFFFFFE00,\r
69 \r
70   0xFFFFFF00,\r
71   0xFFFFFF80,\r
72   0xFFFFFFC0,\r
73   0xFFFFFFE0,\r
74   0xFFFFFFF0,\r
75   0xFFFFFFF8,\r
76   0xFFFFFFFC,\r
77   0xFFFFFFFE,\r
78   0xFFFFFFFF,\r
79 };\r
80 \r
81 GLOBAL_REMOVE_IF_UNREFERENCED EFI_IPv4_ADDRESS  mZeroIp4Addr = {{0, 0, 0, 0}};\r
82 \r
83 //\r
84 // Any error level digitally larger than mNetDebugLevelMax\r
85 // will be silently discarded.\r
86 //\r
87 GLOBAL_REMOVE_IF_UNREFERENCED UINTN  mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR;\r
88 GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogPacketSeq  = 0xDEADBEEF;\r
89 \r
90 //\r
91 // You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp\r
92 // here to direct the syslog packets to the syslog deamon. The\r
93 // default is broadcast to both the ethernet and IP.\r
94 //\r
95 GLOBAL_REMOVE_IF_UNREFERENCED UINT8  mSyslogDstMac[NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
96 GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogDstIp                      = 0xffffffff;\r
97 GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogSrcIp                      = 0;\r
98 \r
99 GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 *mMonthName[] = {\r
100   "Jan",\r
101   "Feb",\r
102   "Mar",\r
103   "Apr",\r
104   "May",\r
105   "Jun",\r
106   "Jul",\r
107   "Aug",\r
108   "Sep",\r
109   "Oct",\r
110   "Nov",\r
111   "Dec"\r
112 };\r
113 \r
114 //\r
115 // VLAN device path node template\r
116 //\r
117 GLOBAL_REMOVE_IF_UNREFERENCED VLAN_DEVICE_PATH mNetVlanDevicePathTemplate = {\r
118   {\r
119     MESSAGING_DEVICE_PATH,\r
120     MSG_VLAN_DP,\r
121     {\r
122       (UINT8) (sizeof (VLAN_DEVICE_PATH)),\r
123       (UINT8) ((sizeof (VLAN_DEVICE_PATH)) >> 8)\r
124     }\r
125   },\r
126   0\r
127 };\r
128 \r
129 /**\r
130   Locate the handles that support SNP, then open one of them\r
131   to send the syslog packets. The caller isn't required to close\r
132   the SNP after use because the SNP is opened by HandleProtocol.\r
133 \r
134   @return The point to SNP if one is properly openned. Otherwise NULL\r
135 \r
136 **/\r
137 EFI_SIMPLE_NETWORK_PROTOCOL *\r
138 SyslogLocateSnp (\r
139   VOID\r
140   )\r
141 {\r
142   EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
143   EFI_STATUS                  Status;\r
144   EFI_HANDLE                  *Handles;\r
145   UINTN                       HandleCount;\r
146   UINTN                       Index;\r
147 \r
148   //\r
149   // Locate the handles which has SNP installed.\r
150   //\r
151   Handles = NULL;\r
152   Status  = gBS->LocateHandleBuffer (\r
153                    ByProtocol,\r
154                    &gEfiSimpleNetworkProtocolGuid,\r
155                    NULL,\r
156                    &HandleCount,\r
157                    &Handles\r
158                    );\r
159 \r
160   if (EFI_ERROR (Status) || (HandleCount == 0)) {\r
161     return NULL;\r
162   }\r
163 \r
164   //\r
165   // Try to open one of the ethernet SNP protocol to send packet\r
166   //\r
167   Snp = NULL;\r
168 \r
169   for (Index = 0; Index < HandleCount; Index++) {\r
170     Status = gBS->HandleProtocol (\r
171                     Handles[Index],\r
172                     &gEfiSimpleNetworkProtocolGuid,\r
173                     (VOID **) &Snp\r
174                     );\r
175 \r
176     if ((Status == EFI_SUCCESS) && (Snp != NULL) &&\r
177         (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) &&\r
178         (Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) {\r
179 \r
180       break;\r
181     }\r
182 \r
183     Snp = NULL;\r
184   }\r
185 \r
186   FreePool (Handles);\r
187   return Snp;\r
188 }\r
189 \r
190 /**\r
191   Transmit a syslog packet synchronously through SNP. The Packet\r
192   already has the ethernet header prepended. This function should\r
193   fill in the source MAC because it will try to locate a SNP each\r
194   time it is called to avoid the problem if SNP is unloaded.\r
195   This code snip is copied from MNP.\r
196 \r
197   @param[in] Packet          The Syslog packet\r
198   @param[in] Length          The length of the packet\r
199 \r
200   @retval EFI_DEVICE_ERROR   Failed to locate a usable SNP protocol\r
201   @retval EFI_TIMEOUT        Timeout happened to send the packet.\r
202   @retval EFI_SUCCESS        Packet is sent.\r
203 \r
204 **/\r
205 EFI_STATUS\r
206 SyslogSendPacket (\r
207   IN CHAR8                    *Packet,\r
208   IN UINT32                   Length\r
209   )\r
210 {\r
211   EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
212   ETHER_HEAD                  *Ether;\r
213   EFI_STATUS                  Status;\r
214   EFI_EVENT                   TimeoutEvent;\r
215   UINT8                       *TxBuf;\r
216 \r
217   Snp = SyslogLocateSnp ();\r
218 \r
219   if (Snp == NULL) {\r
220     return EFI_DEVICE_ERROR;\r
221   }\r
222 \r
223   Ether = (ETHER_HEAD *) Packet;\r
224   CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN);\r
225 \r
226   //\r
227   // Start the timeout event.\r
228   //\r
229   Status = gBS->CreateEvent (\r
230                   EVT_TIMER,\r
231                   TPL_NOTIFY,\r
232                   NULL,\r
233                   NULL,\r
234                   &TimeoutEvent\r
235                   );\r
236 \r
237   if (EFI_ERROR (Status)) {\r
238     return Status;\r
239   }\r
240 \r
241   Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);\r
242 \r
243   if (EFI_ERROR (Status)) {\r
244     goto ON_EXIT;\r
245   }\r
246 \r
247   for (;;) {\r
248     //\r
249     // Transmit the packet through SNP.\r
250     //\r
251     Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL);\r
252 \r
253     if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {\r
254       Status = EFI_DEVICE_ERROR;\r
255       break;\r
256     }\r
257 \r
258     //\r
259     // If Status is EFI_SUCCESS, the packet is put in the transmit queue.\r
260     // if Status is EFI_NOT_READY, the transmit engine of the network\r
261     // interface is busy. Both need to sync SNP.\r
262     //\r
263     TxBuf = NULL;\r
264 \r
265     do {\r
266       //\r
267       // Get the recycled transmit buffer status.\r
268       //\r
269       Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf);\r
270 \r
271       if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
272         Status = EFI_TIMEOUT;\r
273         break;\r
274       }\r
275 \r
276     } while (TxBuf == NULL);\r
277 \r
278     if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {\r
279       break;\r
280     }\r
281 \r
282     //\r
283     // Status is EFI_NOT_READY. Restart the timer event and\r
284     // call Snp->Transmit again.\r
285     //\r
286     gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);\r
287   }\r
288 \r
289   gBS->SetTimer (TimeoutEvent, TimerCancel, 0);\r
290 \r
291 ON_EXIT:\r
292   gBS->CloseEvent (TimeoutEvent);\r
293   return Status;\r
294 }\r
295 \r
296 /**\r
297   Build a syslog packet, including the Ethernet/Ip/Udp headers\r
298   and user's message.\r
299 \r
300   @param[in]  Level     Syslog servity level\r
301   @param[in]  Module    The module that generates the log\r
302   @param[in]  File      The file that contains the current log\r
303   @param[in]  Line      The line of code in the File that contains the current log\r
304   @param[in]  Message   The log message\r
305   @param[in]  BufLen    The lenght of the Buf\r
306   @param[out] Buf       The buffer to put the packet data\r
307 \r
308   @return The length of the syslog packet built.\r
309 \r
310 **/\r
311 UINT32\r
312 SyslogBuildPacket (\r
313   IN  UINT32                Level,\r
314   IN  UINT8                 *Module,\r
315   IN  UINT8                 *File,\r
316   IN  UINT32                Line,\r
317   IN  UINT8                 *Message,\r
318   IN  UINT32                BufLen,\r
319   OUT CHAR8                 *Buf\r
320   )\r
321 {\r
322   ETHER_HEAD                *Ether;\r
323   IP4_HEAD                  *Ip4;\r
324   EFI_UDP_HEADER            *Udp4;\r
325   EFI_TIME                  Time;\r
326   UINT32                    Pri;\r
327   UINT32                    Len;\r
328 \r
329   //\r
330   // Fill in the Ethernet header. Leave alone the source MAC.\r
331   // SyslogSendPacket will fill in the address for us.\r
332   //\r
333   Ether = (ETHER_HEAD *) Buf;\r
334   CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN);\r
335   ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN);\r
336 \r
337   Ether->EtherType = HTONS (0x0800);    // IPv4 protocol\r
338 \r
339   Buf             += sizeof (ETHER_HEAD);\r
340   BufLen          -= sizeof (ETHER_HEAD);\r
341 \r
342   //\r
343   // Fill in the IP header\r
344   //\r
345   Ip4              = (IP4_HEAD *) Buf;\r
346   Ip4->HeadLen     = 5;\r
347   Ip4->Ver         = 4;\r
348   Ip4->Tos         = 0;\r
349   Ip4->TotalLen    = 0;\r
350   Ip4->Id          = (UINT16) mSyslogPacketSeq;\r
351   Ip4->Fragment    = 0;\r
352   Ip4->Ttl         = 16;\r
353   Ip4->Protocol    = 0x11;\r
354   Ip4->Checksum    = 0;\r
355   Ip4->Src         = mSyslogSrcIp;\r
356   Ip4->Dst         = mSyslogDstIp;\r
357 \r
358   Buf             += sizeof (IP4_HEAD);\r
359   BufLen          -= sizeof (IP4_HEAD);\r
360 \r
361   //\r
362   // Fill in the UDP header, Udp checksum is optional. Leave it zero.\r
363   //\r
364   Udp4             = (EFI_UDP_HEADER *) Buf;\r
365   Udp4->SrcPort    = HTONS (514);\r
366   Udp4->DstPort    = HTONS (514);\r
367   Udp4->Length     = 0;\r
368   Udp4->Checksum   = 0;\r
369 \r
370   Buf             += sizeof (EFI_UDP_HEADER);\r
371   BufLen          -= sizeof (EFI_UDP_HEADER);\r
372 \r
373   //\r
374   // Build the syslog message body with <PRI> Timestamp  machine module Message\r
375   //\r
376   Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);\r
377   gRT->GetTime (&Time, NULL);\r
378   ASSERT ((Time.Month <= 12) && (Time.Month >= 1));\r
379 \r
380   //\r
381   // Use %a to format the ASCII strings, %s to format UNICODE strings\r
382   //\r
383   Len  = 0;\r
384   Len += (UINT32) AsciiSPrint (\r
385                     Buf,\r
386                     BufLen,\r
387                     "<%d> %a %d %d:%d:%d ",\r
388                     Pri,\r
389                     mMonthName [Time.Month-1],\r
390                     Time.Day,\r
391                     Time.Hour,\r
392                     Time.Minute,\r
393                     Time.Second\r
394                     );\r
395   Len--;\r
396 \r
397   Len += (UINT32) AsciiSPrint (\r
398                     Buf + Len,\r
399                     BufLen - Len,\r
400                     "Tiano %a: %a (Line: %d File: %a)",\r
401                     Module,\r
402                     Message,\r
403                     Line,\r
404                     File\r
405                     );\r
406   Len--;\r
407 \r
408   //\r
409   // OK, patch the IP length/checksum and UDP length fields.\r
410   //\r
411   Len           += sizeof (EFI_UDP_HEADER);\r
412   Udp4->Length   = HTONS ((UINT16) Len);\r
413 \r
414   Len           += sizeof (IP4_HEAD);\r
415   Ip4->TotalLen  = HTONS ((UINT16) Len);\r
416   Ip4->Checksum  = (UINT16) (~NetblockChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD)));\r
417 \r
418   return Len + sizeof (ETHER_HEAD);\r
419 }\r
420 \r
421 /**\r
422   Allocate a buffer, then format the message to it. This is a\r
423   help function for the NET_DEBUG_XXX macros. The PrintArg of\r
424   these macros treats the variable length print parameters as a\r
425   single parameter, and pass it to the NetDebugASPrint. For\r
426   example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name))\r
427   if extracted to:\r
428 \r
429          NetDebugOutput (\r
430            NETDEBUG_LEVEL_TRACE,\r
431            "Tcp",\r
432            __FILE__,\r
433            __LINE__,\r
434            NetDebugASPrint ("State transit to %a\n", Name)\r
435          )\r
436 \r
437   @param Format  The ASCII format string.\r
438   @param ...     The variable length parameter whose format is determined\r
439                  by the Format string.\r
440 \r
441   @return        The buffer containing the formatted message,\r
442                  or NULL if failed to allocate memory.\r
443 \r
444 **/\r
445 CHAR8 *\r
446 NetDebugASPrint (\r
447   IN CHAR8                  *Format,\r
448   ...\r
449   )\r
450 {\r
451   VA_LIST                   Marker;\r
452   CHAR8                     *Buf;\r
453 \r
454   Buf = (CHAR8 *) AllocatePool (NET_DEBUG_MSG_LEN);\r
455 \r
456   if (Buf == NULL) {\r
457     return NULL;\r
458   }\r
459 \r
460   VA_START (Marker, Format);\r
461   AsciiVSPrint (Buf, NET_DEBUG_MSG_LEN, Format, Marker);\r
462   VA_END (Marker);\r
463 \r
464   return Buf;\r
465 }\r
466 \r
467 /**\r
468   Builds an UDP4 syslog packet and send it using SNP.\r
469 \r
470   This function will locate a instance of SNP then send the message through it.\r
471   Because it isn't open the SNP BY_DRIVER, apply caution when using it.\r
472 \r
473   @param Level    The servity level of the message.\r
474   @param Module   The Moudle that generates the log.\r
475   @param File     The file that contains the log.\r
476   @param Line     The exact line that contains the log.\r
477   @param Message  The user message to log.\r
478 \r
479   @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
480   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the packet\r
481   @retval EFI_SUCCESS           The log is discard because that it is more verbose\r
482                                 than the mNetDebugLevelMax. Or, it has been sent out.\r
483 **/\r
484 EFI_STATUS\r
485 NetDebugOutput (\r
486   IN UINT32                    Level,\r
487   IN UINT8                     *Module,\r
488   IN UINT8                     *File,\r
489   IN UINT32                    Line,\r
490   IN UINT8                     *Message\r
491   )\r
492 {\r
493   CHAR8                        *Packet;\r
494   UINT32                       Len;\r
495   EFI_STATUS                   Status;\r
496 \r
497   //\r
498   // Check whether the message should be sent out\r
499   //\r
500   if (Message == NULL) {\r
501     return EFI_INVALID_PARAMETER;\r
502   }\r
503 \r
504   if (Level > mNetDebugLevelMax) {\r
505     Status = EFI_SUCCESS;\r
506     goto ON_EXIT;\r
507   }\r
508 \r
509   //\r
510   // Allocate a maxium of 1024 bytes, the caller should ensure\r
511   // that the message plus the ethernet/ip/udp header is shorter\r
512   // than this\r
513   //\r
514   Packet = (CHAR8 *) AllocatePool (NET_SYSLOG_PACKET_LEN);\r
515 \r
516   if (Packet == NULL) {\r
517     Status = EFI_OUT_OF_RESOURCES;\r
518     goto ON_EXIT;\r
519   }\r
520 \r
521   //\r
522   // Build the message: Ethernet header + IP header + Udp Header + user data\r
523   //\r
524   Len = SyslogBuildPacket (\r
525           Level,\r
526           Module,\r
527           File,\r
528           Line,\r
529           Message,\r
530           NET_SYSLOG_PACKET_LEN,\r
531           Packet\r
532           );\r
533 \r
534   mSyslogPacketSeq++;\r
535   Status = SyslogSendPacket (Packet, Len);\r
536   FreePool (Packet);\r
537 \r
538 ON_EXIT:\r
539   FreePool (Message);\r
540   return Status;\r
541 }\r
542 /**\r
543   Return the length of the mask.\r
544 \r
545   Return the length of the mask, the correct value is from 0 to 32.\r
546   If the mask is invalid, return the invalid length 33, which is IP4_MASK_NUM.\r
547   NetMask is in the host byte order.\r
548 \r
549   @param[in]  NetMask              The netmask to get the length from.\r
550 \r
551   @return The length of the netmask, IP4_MASK_NUM if the mask is invalid.\r
552 \r
553 **/\r
554 INTN\r
555 EFIAPI\r
556 NetGetMaskLength (\r
557   IN IP4_ADDR               NetMask\r
558   )\r
559 {\r
560   INTN                      Index;\r
561 \r
562   for (Index = 0; Index < IP4_MASK_NUM; Index++) {\r
563     if (NetMask == gIp4AllMasks[Index]) {\r
564       break;\r
565     }\r
566   }\r
567 \r
568   return Index;\r
569 }\r
570 \r
571 \r
572 \r
573 /**\r
574   Return the class of the IP address, such as class A, B, C.\r
575   Addr is in host byte order.\r
576 \r
577   The address of class A  starts with 0.\r
578   If the address belong to class A, return IP4_ADDR_CLASSA.\r
579   The address of class B  starts with 10.\r
580   If the address belong to class B, return IP4_ADDR_CLASSB.\r
581   The address of class C  starts with 110.\r
582   If the address belong to class C, return IP4_ADDR_CLASSC.\r
583   The address of class D  starts with 1110.\r
584   If the address belong to class D, return IP4_ADDR_CLASSD.\r
585   The address of class E  starts with 1111.\r
586   If the address belong to class E, return IP4_ADDR_CLASSE.\r
587 \r
588 \r
589   @param[in]   Addr                  The address to get the class from.\r
590 \r
591   @return IP address class, such as IP4_ADDR_CLASSA.\r
592 \r
593 **/\r
594 INTN\r
595 EFIAPI\r
596 NetGetIpClass (\r
597   IN IP4_ADDR               Addr\r
598   )\r
599 {\r
600   UINT8                     ByteOne;\r
601 \r
602   ByteOne = (UINT8) (Addr >> 24);\r
603 \r
604   if ((ByteOne & 0x80) == 0) {\r
605     return IP4_ADDR_CLASSA;\r
606 \r
607   } else if ((ByteOne & 0xC0) == 0x80) {\r
608     return IP4_ADDR_CLASSB;\r
609 \r
610   } else if ((ByteOne & 0xE0) == 0xC0) {\r
611     return IP4_ADDR_CLASSC;\r
612 \r
613   } else if ((ByteOne & 0xF0) == 0xE0) {\r
614     return IP4_ADDR_CLASSD;\r
615 \r
616   } else {\r
617     return IP4_ADDR_CLASSE;\r
618 \r
619   }\r
620 }\r
621 \r
622 \r
623 /**\r
624   Check whether the IP is a valid unicast address according to\r
625   the netmask. If NetMask is zero, use the IP address's class to get the default mask.\r
626 \r
627   If Ip is 0, IP is not a valid unicast address.\r
628   Class D address is used for multicasting and class E address is reserved for future. If Ip\r
629   belongs to class D or class E, IP is not a valid unicast address.\r
630   If all bits of the host address of IP are 0 or 1, IP is also not a valid unicast address.\r
631 \r
632   @param[in]  Ip                    The IP to check against.\r
633   @param[in]  NetMask               The mask of the IP.\r
634 \r
635   @return TRUE if IP is a valid unicast address on the network, otherwise FALSE.\r
636 \r
637 **/\r
638 BOOLEAN\r
639 EFIAPI\r
640 NetIp4IsUnicast (\r
641   IN IP4_ADDR               Ip,\r
642   IN IP4_ADDR               NetMask\r
643   )\r
644 {\r
645   INTN                      Class;\r
646 \r
647   Class = NetGetIpClass (Ip);\r
648 \r
649   if ((Ip == 0) || (Class >= IP4_ADDR_CLASSD)) {\r
650     return FALSE;\r
651   }\r
652 \r
653   if (NetMask == 0) {\r
654     NetMask = gIp4AllMasks[Class << 3];\r
655   }\r
656 \r
657   if (((Ip &~NetMask) == ~NetMask) || ((Ip &~NetMask) == 0)) {\r
658     return FALSE;\r
659   }\r
660 \r
661   return TRUE;\r
662 }\r
663 \r
664 /**\r
665   Check whether the incoming IPv6 address is a valid unicast address.\r
666 \r
667   If the address is a multicast address has binary 0xFF at the start, it is not\r
668   a valid unicast address. If the address is unspecified ::, it is not a valid\r
669   unicast address to be assigned to any node. If the address is loopback address\r
670   ::1, it is also not a valid unicast address to be assigned to any physical\r
671   interface.\r
672 \r
673   @param[in]  Ip6                   The IPv6 address to check against.\r
674 \r
675   @return TRUE if Ip6 is a valid unicast address on the network, otherwise FALSE.\r
676 \r
677 **/\r
678 BOOLEAN\r
679 NetIp6IsValidUnicast (\r
680   IN EFI_IPv6_ADDRESS       *Ip6\r
681   )\r
682 {\r
683   UINT8 Byte;\r
684   UINT8 Index;\r
685 \r
686   if (Ip6->Addr[0] == 0xFF) {\r
687     return FALSE;\r
688   }\r
689 \r
690   for (Index = 0; Index < 15; Index++) {\r
691     if (Ip6->Addr[Index] != 0) {\r
692       return TRUE;\r
693     }\r
694   }\r
695 \r
696   Byte = Ip6->Addr[Index];\r
697 \r
698   if (Byte == 0x0 || Byte == 0x1) {\r
699     return FALSE;\r
700   }\r
701 \r
702   return TRUE;\r
703 }\r
704 \r
705 /**\r
706   Check whether the incoming Ipv6 address is the unspecified address or not.\r
707 \r
708   @param[in] Ip6   - Ip6 address, in network order.\r
709 \r
710   @retval TRUE     - Yes, unspecified\r
711   @retval FALSE    - No\r
712 \r
713 **/\r
714 BOOLEAN\r
715 NetIp6IsUnspecifiedAddr (\r
716   IN EFI_IPv6_ADDRESS       *Ip6\r
717   )\r
718 {\r
719   UINT8 Index;\r
720 \r
721   for (Index = 0; Index < 16; Index++) {\r
722     if (Ip6->Addr[Index] != 0) {\r
723       return FALSE;\r
724     }\r
725   }\r
726 \r
727   return TRUE;\r
728 }\r
729 \r
730 /**\r
731   Check whether the incoming Ipv6 address is a link-local address.\r
732 \r
733   @param[in] Ip6   - Ip6 address, in network order.\r
734 \r
735   @retval TRUE  - Yes, link-local address\r
736   @retval FALSE - No\r
737 \r
738 **/\r
739 BOOLEAN\r
740 NetIp6IsLinkLocalAddr (\r
741   IN EFI_IPv6_ADDRESS *Ip6\r
742   )\r
743 {\r
744   UINT8 Index;\r
745 \r
746   ASSERT (Ip6 != NULL);\r
747 \r
748   if (Ip6->Addr[0] != 0xFE) {\r
749     return FALSE;\r
750   }\r
751 \r
752   if (Ip6->Addr[1] != 0x80) {\r
753     return FALSE;\r
754   }\r
755 \r
756   for (Index = 2; Index < 8; Index++) {\r
757     if (Ip6->Addr[Index] != 0) {\r
758       return FALSE;\r
759     }\r
760   }\r
761 \r
762   return TRUE;\r
763 }\r
764 \r
765 /**\r
766   Check whether the Ipv6 address1 and address2 are on the connected network.\r
767 \r
768   @param[in] Ip1          - Ip6 address1, in network order.\r
769   @param[in] Ip2          - Ip6 address2, in network order.\r
770   @param[in] PrefixLength - The prefix length of the checking net.\r
771 \r
772   @retval TRUE            - Yes, connected.\r
773   @retval FALSE           - No.\r
774 \r
775 **/\r
776 BOOLEAN\r
777 NetIp6IsNetEqual (\r
778   EFI_IPv6_ADDRESS *Ip1,\r
779   EFI_IPv6_ADDRESS *Ip2,\r
780   UINT8            PrefixLength\r
781   )\r
782 {\r
783   UINT8 Byte;\r
784   UINT8 Bit;\r
785   UINT8 Mask;\r
786 \r
787   ASSERT ((Ip1 != NULL) && (Ip2 != NULL) && (PrefixLength < IP6_PREFIX_NUM));\r
788 \r
789   if (PrefixLength == 0) {\r
790     return TRUE;\r
791   }\r
792 \r
793   Byte = (UINT8) (PrefixLength / 8);\r
794   Bit  = (UINT8) (PrefixLength % 8);\r
795 \r
796   if (CompareMem (Ip1, Ip2, Byte) != 0) {\r
797     return FALSE;\r
798   }\r
799 \r
800   if (Bit > 0) {\r
801     Mask = (UINT8) (0xFF << (8 - Bit));\r
802 \r
803     ASSERT (Byte < 16);\r
804     if ((Ip1->Addr[Byte] & Mask) != (Ip2->Addr[Byte] & Mask)) {\r
805       return FALSE;\r
806     }\r
807   }\r
808 \r
809   return TRUE;\r
810 }\r
811 \r
812 \r
813 /**\r
814   Switches the endianess of an IPv6 address\r
815 \r
816   This function swaps the bytes in a 128-bit IPv6 address to switch the value\r
817   from little endian to big endian or vice versa. The byte swapped value is\r
818   returned.\r
819 \r
820   @param  Ip6 Points to an IPv6 address\r
821 \r
822   @return The byte swapped IPv6 address.\r
823 \r
824 **/\r
825 EFI_IPv6_ADDRESS *\r
826 Ip6Swap128 (\r
827   EFI_IPv6_ADDRESS *Ip6\r
828   )\r
829 {\r
830   UINT64 High;\r
831   UINT64 Low;\r
832 \r
833   CopyMem (&High, Ip6, sizeof (UINT64));\r
834   CopyMem (&Low, &Ip6->Addr[8], sizeof (UINT64));\r
835 \r
836   High = SwapBytes64 (High);\r
837   Low  = SwapBytes64 (Low);\r
838 \r
839   CopyMem (Ip6, &Low, sizeof (UINT64));\r
840   CopyMem (&Ip6->Addr[8], &High, sizeof (UINT64));\r
841 \r
842   return Ip6;\r
843 }\r
844 \r
845 /**\r
846   Initialize a random seed using current time.\r
847 \r
848   Get current time first. Then initialize a random seed based on some basic\r
849   mathematics operation on the hour, day, minute, second, nanosecond and year\r
850   of the current time.\r
851 \r
852   @return The random seed initialized with current time.\r
853 \r
854 **/\r
855 UINT32\r
856 EFIAPI\r
857 NetRandomInitSeed (\r
858   VOID\r
859   )\r
860 {\r
861   EFI_TIME                  Time;\r
862   UINT32                    Seed;\r
863 \r
864   gRT->GetTime (&Time, NULL);\r
865   Seed = (~Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second);\r
866   Seed ^= Time.Nanosecond;\r
867   Seed ^= Time.Year << 7;\r
868 \r
869   return Seed;\r
870 }\r
871 \r
872 \r
873 /**\r
874   Extract a UINT32 from a byte stream.\r
875 \r
876   Copy a UINT32 from a byte stream, then converts it from Network\r
877   byte order to host byte order. Use this function to avoid alignment error.\r
878 \r
879   @param[in]  Buf                 The buffer to extract the UINT32.\r
880 \r
881   @return The UINT32 extracted.\r
882 \r
883 **/\r
884 UINT32\r
885 EFIAPI\r
886 NetGetUint32 (\r
887   IN UINT8                  *Buf\r
888   )\r
889 {\r
890   UINT32                    Value;\r
891 \r
892   CopyMem (&Value, Buf, sizeof (UINT32));\r
893   return NTOHL (Value);\r
894 }\r
895 \r
896 \r
897 /**\r
898   Put a UINT32 to the byte stream in network byte order.\r
899 \r
900   Converts a UINT32 from host byte order to network byte order. Then copy it to the\r
901   byte stream.\r
902 \r
903   @param[in, out]  Buf          The buffer to put the UINT32.\r
904   @param[in]       Data         The data to be converted and put into the byte stream.\r
905 \r
906 **/\r
907 VOID\r
908 EFIAPI\r
909 NetPutUint32 (\r
910   IN OUT UINT8                 *Buf,\r
911   IN     UINT32                Data\r
912   )\r
913 {\r
914   Data = HTONL (Data);\r
915   CopyMem (Buf, &Data, sizeof (UINT32));\r
916 }\r
917 \r
918 \r
919 /**\r
920   Remove the first node entry on the list, and return the removed node entry.\r
921 \r
922   Removes the first node Entry from a doubly linked list. It is up to the caller of\r
923   this function to release the memory used by the first node if that is required. On\r
924   exit, the removed node is returned.\r
925 \r
926   If Head is NULL, then ASSERT().\r
927   If Head was not initialized, then ASSERT().\r
928   If PcdMaximumLinkedListLength is not zero, and the number of nodes in the\r
929   linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,\r
930   then ASSERT().\r
931 \r
932   @param[in, out]  Head                  The list header.\r
933 \r
934   @return The first node entry that is removed from the list, NULL if the list is empty.\r
935 \r
936 **/\r
937 LIST_ENTRY *\r
938 EFIAPI\r
939 NetListRemoveHead (\r
940   IN OUT LIST_ENTRY            *Head\r
941   )\r
942 {\r
943   LIST_ENTRY            *First;\r
944 \r
945   ASSERT (Head != NULL);\r
946 \r
947   if (IsListEmpty (Head)) {\r
948     return NULL;\r
949   }\r
950 \r
951   First                         = Head->ForwardLink;\r
952   Head->ForwardLink             = First->ForwardLink;\r
953   First->ForwardLink->BackLink  = Head;\r
954 \r
955   DEBUG_CODE (\r
956     First->ForwardLink  = (LIST_ENTRY *) NULL;\r
957     First->BackLink     = (LIST_ENTRY *) NULL;\r
958   );\r
959 \r
960   return First;\r
961 }\r
962 \r
963 \r
964 /**\r
965   Remove the last node entry on the list and and return the removed node entry.\r
966 \r
967   Removes the last node entry from a doubly linked list. It is up to the caller of\r
968   this function to release the memory used by the first node if that is required. On\r
969   exit, the removed node is returned.\r
970 \r
971   If Head is NULL, then ASSERT().\r
972   If Head was not initialized, then ASSERT().\r
973   If PcdMaximumLinkedListLength is not zero, and the number of nodes in the\r
974   linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,\r
975   then ASSERT().\r
976 \r
977   @param[in, out]  Head                  The list head.\r
978 \r
979   @return The last node entry that is removed from the list, NULL if the list is empty.\r
980 \r
981 **/\r
982 LIST_ENTRY *\r
983 EFIAPI\r
984 NetListRemoveTail (\r
985   IN OUT LIST_ENTRY            *Head\r
986   )\r
987 {\r
988   LIST_ENTRY            *Last;\r
989 \r
990   ASSERT (Head != NULL);\r
991 \r
992   if (IsListEmpty (Head)) {\r
993     return NULL;\r
994   }\r
995 \r
996   Last                        = Head->BackLink;\r
997   Head->BackLink              = Last->BackLink;\r
998   Last->BackLink->ForwardLink = Head;\r
999 \r
1000   DEBUG_CODE (\r
1001     Last->ForwardLink = (LIST_ENTRY *) NULL;\r
1002     Last->BackLink    = (LIST_ENTRY *) NULL;\r
1003   );\r
1004 \r
1005   return Last;\r
1006 }\r
1007 \r
1008 \r
1009 /**\r
1010   Insert a new node entry after a designated node entry of a doubly linked list.\r
1011 \r
1012   Inserts a new node entry donated by NewEntry after the node entry donated by PrevEntry\r
1013   of the doubly linked list.\r
1014 \r
1015   @param[in, out]  PrevEntry             The previous entry to insert after.\r
1016   @param[in, out]  NewEntry              The new entry to insert.\r
1017 \r
1018 **/\r
1019 VOID\r
1020 EFIAPI\r
1021 NetListInsertAfter (\r
1022   IN OUT LIST_ENTRY         *PrevEntry,\r
1023   IN OUT LIST_ENTRY         *NewEntry\r
1024   )\r
1025 {\r
1026   NewEntry->BackLink                = PrevEntry;\r
1027   NewEntry->ForwardLink             = PrevEntry->ForwardLink;\r
1028   PrevEntry->ForwardLink->BackLink  = NewEntry;\r
1029   PrevEntry->ForwardLink            = NewEntry;\r
1030 }\r
1031 \r
1032 \r
1033 /**\r
1034   Insert a new node entry before a designated node entry of a doubly linked list.\r
1035 \r
1036   Inserts a new node entry donated by NewEntry after the node entry donated by PostEntry\r
1037   of the doubly linked list.\r
1038 \r
1039   @param[in, out]  PostEntry             The entry to insert before.\r
1040   @param[in, out]  NewEntry              The new entry to insert.\r
1041 \r
1042 **/\r
1043 VOID\r
1044 EFIAPI\r
1045 NetListInsertBefore (\r
1046   IN OUT LIST_ENTRY     *PostEntry,\r
1047   IN OUT LIST_ENTRY     *NewEntry\r
1048   )\r
1049 {\r
1050   NewEntry->ForwardLink             = PostEntry;\r
1051   NewEntry->BackLink                = PostEntry->BackLink;\r
1052   PostEntry->BackLink->ForwardLink  = NewEntry;\r
1053   PostEntry->BackLink               = NewEntry;\r
1054 }\r
1055 \r
1056 \r
1057 /**\r
1058   Initialize the netmap. Netmap is a reposity to keep the <Key, Value> pairs.\r
1059 \r
1060   Initialize the forward and backward links of two head nodes donated by Map->Used\r
1061   and Map->Recycled of two doubly linked lists.\r
1062   Initializes the count of the <Key, Value> pairs in the netmap to zero.\r
1063 \r
1064   If Map is NULL, then ASSERT().\r
1065   If the address of Map->Used is NULL, then ASSERT().\r
1066   If the address of Map->Recycled is NULl, then ASSERT().\r
1067 \r
1068   @param[in, out]  Map                   The netmap to initialize.\r
1069 \r
1070 **/\r
1071 VOID\r
1072 EFIAPI\r
1073 NetMapInit (\r
1074   IN OUT NET_MAP                *Map\r
1075   )\r
1076 {\r
1077   ASSERT (Map != NULL);\r
1078 \r
1079   InitializeListHead (&Map->Used);\r
1080   InitializeListHead (&Map->Recycled);\r
1081   Map->Count = 0;\r
1082 }\r
1083 \r
1084 \r
1085 /**\r
1086   To clean up the netmap, that is, release allocated memories.\r
1087 \r
1088   Removes all nodes of the Used doubly linked list and free memory of all related netmap items.\r
1089   Removes all nodes of the Recycled doubly linked list and free memory of all related netmap items.\r
1090   The number of the <Key, Value> pairs in the netmap is set to be zero.\r
1091 \r
1092   If Map is NULL, then ASSERT().\r
1093 \r
1094   @param[in, out]  Map                   The netmap to clean up.\r
1095 \r
1096 **/\r
1097 VOID\r
1098 EFIAPI\r
1099 NetMapClean (\r
1100   IN OUT NET_MAP            *Map\r
1101   )\r
1102 {\r
1103   NET_MAP_ITEM              *Item;\r
1104   LIST_ENTRY                *Entry;\r
1105   LIST_ENTRY                *Next;\r
1106 \r
1107   ASSERT (Map != NULL);\r
1108 \r
1109   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Used) {\r
1110     Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
1111 \r
1112     RemoveEntryList (&Item->Link);\r
1113     Map->Count--;\r
1114 \r
1115     gBS->FreePool (Item);\r
1116   }\r
1117 \r
1118   ASSERT ((Map->Count == 0) && IsListEmpty (&Map->Used));\r
1119 \r
1120   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Recycled) {\r
1121     Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
1122 \r
1123     RemoveEntryList (&Item->Link);\r
1124     gBS->FreePool (Item);\r
1125   }\r
1126 \r
1127   ASSERT (IsListEmpty (&Map->Recycled));\r
1128 }\r
1129 \r
1130 \r
1131 /**\r
1132   Test whether the netmap is empty and return true if it is.\r
1133 \r
1134   If the number of the <Key, Value> pairs in the netmap is zero, return TRUE.\r
1135 \r
1136   If Map is NULL, then ASSERT().\r
1137 \r
1138 \r
1139   @param[in]  Map                   The net map to test.\r
1140 \r
1141   @return TRUE if the netmap is empty, otherwise FALSE.\r
1142 \r
1143 **/\r
1144 BOOLEAN\r
1145 EFIAPI\r
1146 NetMapIsEmpty (\r
1147   IN NET_MAP                *Map\r
1148   )\r
1149 {\r
1150   ASSERT (Map != NULL);\r
1151   return (BOOLEAN) (Map->Count == 0);\r
1152 }\r
1153 \r
1154 \r
1155 /**\r
1156   Return the number of the <Key, Value> pairs in the netmap.\r
1157 \r
1158   @param[in]  Map                   The netmap to get the entry number.\r
1159 \r
1160   @return The entry number in the netmap.\r
1161 \r
1162 **/\r
1163 UINTN\r
1164 EFIAPI\r
1165 NetMapGetCount (\r
1166   IN NET_MAP                *Map\r
1167   )\r
1168 {\r
1169   return Map->Count;\r
1170 }\r
1171 \r
1172 \r
1173 /**\r
1174   Return one allocated item.\r
1175 \r
1176   If the Recycled doubly linked list of the netmap is empty, it will try to allocate\r
1177   a batch of items if there are enough resources and add corresponding nodes to the begining\r
1178   of the Recycled doubly linked list of the netmap. Otherwise, it will directly remove\r
1179   the fist node entry of the Recycled doubly linked list and return the corresponding item.\r
1180 \r
1181   If Map is NULL, then ASSERT().\r
1182 \r
1183   @param[in, out]  Map          The netmap to allocate item for.\r
1184 \r
1185   @return                       The allocated item. If NULL, the\r
1186                                 allocation failed due to resource limit.\r
1187 \r
1188 **/\r
1189 NET_MAP_ITEM *\r
1190 NetMapAllocItem (\r
1191   IN OUT NET_MAP            *Map\r
1192   )\r
1193 {\r
1194   NET_MAP_ITEM              *Item;\r
1195   LIST_ENTRY                *Head;\r
1196   UINTN                     Index;\r
1197 \r
1198   ASSERT (Map != NULL);\r
1199 \r
1200   Head = &Map->Recycled;\r
1201 \r
1202   if (IsListEmpty (Head)) {\r
1203     for (Index = 0; Index < NET_MAP_INCREAMENT; Index++) {\r
1204       Item = AllocatePool (sizeof (NET_MAP_ITEM));\r
1205 \r
1206       if (Item == NULL) {\r
1207         if (Index == 0) {\r
1208           return NULL;\r
1209         }\r
1210 \r
1211         break;\r
1212       }\r
1213 \r
1214       InsertHeadList (Head, &Item->Link);\r
1215     }\r
1216   }\r
1217 \r
1218   Item = NET_LIST_HEAD (Head, NET_MAP_ITEM, Link);\r
1219   NetListRemoveHead (Head);\r
1220 \r
1221   return Item;\r
1222 }\r
1223 \r
1224 \r
1225 /**\r
1226   Allocate an item to save the <Key, Value> pair to the head of the netmap.\r
1227 \r
1228   Allocate an item to save the <Key, Value> pair and add corresponding node entry\r
1229   to the beginning of the Used doubly linked list. The number of the <Key, Value>\r
1230   pairs in the netmap increase by 1.\r
1231 \r
1232   If Map is NULL, then ASSERT().\r
1233 \r
1234   @param[in, out]  Map                   The netmap to insert into.\r
1235   @param[in]       Key                   The user's key.\r
1236   @param[in]       Value                 The user's value for the key.\r
1237 \r
1238   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the item.\r
1239   @retval EFI_SUCCESS           The item is inserted to the head.\r
1240 \r
1241 **/\r
1242 EFI_STATUS\r
1243 EFIAPI\r
1244 NetMapInsertHead (\r
1245   IN OUT NET_MAP            *Map,\r
1246   IN VOID                   *Key,\r
1247   IN VOID                   *Value    OPTIONAL\r
1248   )\r
1249 {\r
1250   NET_MAP_ITEM              *Item;\r
1251 \r
1252   ASSERT (Map != NULL);\r
1253 \r
1254   Item = NetMapAllocItem (Map);\r
1255 \r
1256   if (Item == NULL) {\r
1257     return EFI_OUT_OF_RESOURCES;\r
1258   }\r
1259 \r
1260   Item->Key   = Key;\r
1261   Item->Value = Value;\r
1262   InsertHeadList (&Map->Used, &Item->Link);\r
1263 \r
1264   Map->Count++;\r
1265   return EFI_SUCCESS;\r
1266 }\r
1267 \r
1268 \r
1269 /**\r
1270   Allocate an item to save the <Key, Value> pair to the tail of the netmap.\r
1271 \r
1272   Allocate an item to save the <Key, Value> pair and add corresponding node entry\r
1273   to the tail of the Used doubly linked list. The number of the <Key, Value>\r
1274   pairs in the netmap increase by 1.\r
1275 \r
1276   If Map is NULL, then ASSERT().\r
1277 \r
1278   @param[in, out]  Map                   The netmap to insert into.\r
1279   @param[in]       Key                   The user's key.\r
1280   @param[in]       Value                 The user's value for the key.\r
1281 \r
1282   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the item.\r
1283   @retval EFI_SUCCESS           The item is inserted to the tail.\r
1284 \r
1285 **/\r
1286 EFI_STATUS\r
1287 EFIAPI\r
1288 NetMapInsertTail (\r
1289   IN OUT NET_MAP            *Map,\r
1290   IN VOID                   *Key,\r
1291   IN VOID                   *Value    OPTIONAL\r
1292   )\r
1293 {\r
1294   NET_MAP_ITEM              *Item;\r
1295 \r
1296   ASSERT (Map != NULL);\r
1297 \r
1298   Item = NetMapAllocItem (Map);\r
1299 \r
1300   if (Item == NULL) {\r
1301     return EFI_OUT_OF_RESOURCES;\r
1302   }\r
1303 \r
1304   Item->Key   = Key;\r
1305   Item->Value = Value;\r
1306   InsertTailList (&Map->Used, &Item->Link);\r
1307 \r
1308   Map->Count++;\r
1309 \r
1310   return EFI_SUCCESS;\r
1311 }\r
1312 \r
1313 \r
1314 /**\r
1315   Check whether the item is in the Map and return TRUE if it is.\r
1316 \r
1317   @param[in]  Map                   The netmap to search within.\r
1318   @param[in]  Item                  The item to search.\r
1319 \r
1320   @return TRUE if the item is in the netmap, otherwise FALSE.\r
1321 \r
1322 **/\r
1323 BOOLEAN\r
1324 NetItemInMap (\r
1325   IN NET_MAP                *Map,\r
1326   IN NET_MAP_ITEM           *Item\r
1327   )\r
1328 {\r
1329   LIST_ENTRY            *ListEntry;\r
1330 \r
1331   NET_LIST_FOR_EACH (ListEntry, &Map->Used) {\r
1332     if (ListEntry == &Item->Link) {\r
1333       return TRUE;\r
1334     }\r
1335   }\r
1336 \r
1337   return FALSE;\r
1338 }\r
1339 \r
1340 \r
1341 /**\r
1342   Find the key in the netmap and returns the point to the item contains the Key.\r
1343 \r
1344   Iterate the Used doubly linked list of the netmap to get every item. Compare the key of every\r
1345   item with the key to search. It returns the point to the item contains the Key if found.\r
1346 \r
1347   If Map is NULL, then ASSERT().\r
1348 \r
1349   @param[in]  Map                   The netmap to search within.\r
1350   @param[in]  Key                   The key to search.\r
1351 \r
1352   @return The point to the item contains the Key, or NULL if Key isn't in the map.\r
1353 \r
1354 **/\r
1355 NET_MAP_ITEM *\r
1356 EFIAPI\r
1357 NetMapFindKey (\r
1358   IN  NET_MAP               *Map,\r
1359   IN  VOID                  *Key\r
1360   )\r
1361 {\r
1362   LIST_ENTRY              *Entry;\r
1363   NET_MAP_ITEM            *Item;\r
1364 \r
1365   ASSERT (Map != NULL);\r
1366 \r
1367   NET_LIST_FOR_EACH (Entry, &Map->Used) {\r
1368     Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
1369 \r
1370     if (Item->Key == Key) {\r
1371       return Item;\r
1372     }\r
1373   }\r
1374 \r
1375   return NULL;\r
1376 }\r
1377 \r
1378 \r
1379 /**\r
1380   Remove the node entry of the item from the netmap and return the key of the removed item.\r
1381 \r
1382   Remove the node entry of the item from the Used doubly linked list of the netmap.\r
1383   The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node\r
1384   entry of the item to the Recycled doubly linked list of the netmap. If Value is not NULL,\r
1385   Value will point to the value of the item. It returns the key of the removed item.\r
1386 \r
1387   If Map is NULL, then ASSERT().\r
1388   If Item is NULL, then ASSERT().\r
1389   if item in not in the netmap, then ASSERT().\r
1390 \r
1391   @param[in, out]  Map                   The netmap to remove the item from.\r
1392   @param[in, out]  Item                  The item to remove.\r
1393   @param[out]      Value                 The variable to receive the value if not NULL.\r
1394 \r
1395   @return                                The key of the removed item.\r
1396 \r
1397 **/\r
1398 VOID *\r
1399 EFIAPI\r
1400 NetMapRemoveItem (\r
1401   IN  OUT NET_MAP             *Map,\r
1402   IN  OUT NET_MAP_ITEM        *Item,\r
1403   OUT VOID                    **Value           OPTIONAL\r
1404   )\r
1405 {\r
1406   ASSERT ((Map != NULL) && (Item != NULL));\r
1407   ASSERT (NetItemInMap (Map, Item));\r
1408 \r
1409   RemoveEntryList (&Item->Link);\r
1410   Map->Count--;\r
1411   InsertHeadList (&Map->Recycled, &Item->Link);\r
1412 \r
1413   if (Value != NULL) {\r
1414     *Value = Item->Value;\r
1415   }\r
1416 \r
1417   return Item->Key;\r
1418 }\r
1419 \r
1420 \r
1421 /**\r
1422   Remove the first node entry on the netmap and return the key of the removed item.\r
1423 \r
1424   Remove the first node entry from the Used doubly linked list of the netmap.\r
1425   The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node\r
1426   entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,\r
1427   parameter Value will point to the value of the item. It returns the key of the removed item.\r
1428 \r
1429   If Map is NULL, then ASSERT().\r
1430   If the Used doubly linked list is empty, then ASSERT().\r
1431 \r
1432   @param[in, out]  Map                   The netmap to remove the head from.\r
1433   @param[out]      Value                 The variable to receive the value if not NULL.\r
1434 \r
1435   @return                                The key of the item removed.\r
1436 \r
1437 **/\r
1438 VOID *\r
1439 EFIAPI\r
1440 NetMapRemoveHead (\r
1441   IN OUT NET_MAP            *Map,\r
1442   OUT VOID                  **Value         OPTIONAL\r
1443   )\r
1444 {\r
1445   NET_MAP_ITEM  *Item;\r
1446 \r
1447   //\r
1448   // Often, it indicates a programming error to remove\r
1449   // the first entry in an empty list\r
1450   //\r
1451   ASSERT (Map && !IsListEmpty (&Map->Used));\r
1452 \r
1453   Item = NET_LIST_HEAD (&Map->Used, NET_MAP_ITEM, Link);\r
1454   RemoveEntryList (&Item->Link);\r
1455   Map->Count--;\r
1456   InsertHeadList (&Map->Recycled, &Item->Link);\r
1457 \r
1458   if (Value != NULL) {\r
1459     *Value = Item->Value;\r
1460   }\r
1461 \r
1462   return Item->Key;\r
1463 }\r
1464 \r
1465 \r
1466 /**\r
1467   Remove the last node entry on the netmap and return the key of the removed item.\r
1468 \r
1469   Remove the last node entry from the Used doubly linked list of the netmap.\r
1470   The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node\r
1471   entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,\r
1472   parameter Value will point to the value of the item. It returns the key of the removed item.\r
1473 \r
1474   If Map is NULL, then ASSERT().\r
1475   If the Used doubly linked list is empty, then ASSERT().\r
1476 \r
1477   @param[in, out]  Map                   The netmap to remove the tail from.\r
1478   @param[out]      Value                 The variable to receive the value if not NULL.\r
1479 \r
1480   @return                                The key of the item removed.\r
1481 \r
1482 **/\r
1483 VOID *\r
1484 EFIAPI\r
1485 NetMapRemoveTail (\r
1486   IN OUT NET_MAP            *Map,\r
1487   OUT VOID                  **Value       OPTIONAL\r
1488   )\r
1489 {\r
1490   NET_MAP_ITEM              *Item;\r
1491 \r
1492   //\r
1493   // Often, it indicates a programming error to remove\r
1494   // the last entry in an empty list\r
1495   //\r
1496   ASSERT (Map && !IsListEmpty (&Map->Used));\r
1497 \r
1498   Item = NET_LIST_TAIL (&Map->Used, NET_MAP_ITEM, Link);\r
1499   RemoveEntryList (&Item->Link);\r
1500   Map->Count--;\r
1501   InsertHeadList (&Map->Recycled, &Item->Link);\r
1502 \r
1503   if (Value != NULL) {\r
1504     *Value = Item->Value;\r
1505   }\r
1506 \r
1507   return Item->Key;\r
1508 }\r
1509 \r
1510 \r
1511 /**\r
1512   Iterate through the netmap and call CallBack for each item.\r
1513 \r
1514   It will contiue the traverse if CallBack returns EFI_SUCCESS, otherwise, break\r
1515   from the loop. It returns the CallBack's last return value. This function is\r
1516   delete safe for the current item.\r
1517 \r
1518   If Map is NULL, then ASSERT().\r
1519   If CallBack is NULL, then ASSERT().\r
1520 \r
1521   @param[in]  Map                   The Map to iterate through.\r
1522   @param[in]  CallBack              The callback function to call for each item.\r
1523   @param[in]  Arg                   The opaque parameter to the callback.\r
1524 \r
1525   @retval EFI_SUCCESS            There is no item in the netmap or CallBack for each item\r
1526                                  return EFI_SUCCESS.\r
1527   @retval Others                 It returns the CallBack's last return value.\r
1528 \r
1529 **/\r
1530 EFI_STATUS\r
1531 EFIAPI\r
1532 NetMapIterate (\r
1533   IN NET_MAP                *Map,\r
1534   IN NET_MAP_CALLBACK       CallBack,\r
1535   IN VOID                   *Arg      OPTIONAL\r
1536   )\r
1537 {\r
1538 \r
1539   LIST_ENTRY            *Entry;\r
1540   LIST_ENTRY            *Next;\r
1541   LIST_ENTRY            *Head;\r
1542   NET_MAP_ITEM          *Item;\r
1543   EFI_STATUS            Result;\r
1544 \r
1545   ASSERT ((Map != NULL) && (CallBack != NULL));\r
1546 \r
1547   Head = &Map->Used;\r
1548 \r
1549   if (IsListEmpty (Head)) {\r
1550     return EFI_SUCCESS;\r
1551   }\r
1552 \r
1553   NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {\r
1554     Item   = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
1555     Result = CallBack (Map, Item, Arg);\r
1556 \r
1557     if (EFI_ERROR (Result)) {\r
1558       return Result;\r
1559     }\r
1560   }\r
1561 \r
1562   return EFI_SUCCESS;\r
1563 }\r
1564 \r
1565 \r
1566 /**\r
1567   This is the default unload handle for all the network drivers.\r
1568 \r
1569   Disconnect the driver specified by ImageHandle from all the devices in the handle database.\r
1570   Uninstall all the protocols installed in the driver entry point.\r
1571 \r
1572   @param[in]  ImageHandle       The drivers' driver image.\r
1573 \r
1574   @retval EFI_SUCCESS           The image is unloaded.\r
1575   @retval Others                Failed to unload the image.\r
1576 \r
1577 **/\r
1578 EFI_STATUS\r
1579 EFIAPI\r
1580 NetLibDefaultUnload (\r
1581   IN EFI_HANDLE             ImageHandle\r
1582   )\r
1583 {\r
1584   EFI_STATUS                        Status;\r
1585   EFI_HANDLE                        *DeviceHandleBuffer;\r
1586   UINTN                             DeviceHandleCount;\r
1587   UINTN                             Index;\r
1588   EFI_DRIVER_BINDING_PROTOCOL       *DriverBinding;\r
1589   EFI_COMPONENT_NAME_PROTOCOL       *ComponentName;\r
1590   EFI_COMPONENT_NAME2_PROTOCOL      *ComponentName2;\r
1591 \r
1592   //\r
1593   // Get the list of all the handles in the handle database.\r
1594   // If there is an error getting the list, then the unload\r
1595   // operation fails.\r
1596   //\r
1597   Status = gBS->LocateHandleBuffer (\r
1598                   AllHandles,\r
1599                   NULL,\r
1600                   NULL,\r
1601                   &DeviceHandleCount,\r
1602                   &DeviceHandleBuffer\r
1603                   );\r
1604 \r
1605   if (EFI_ERROR (Status)) {\r
1606     return Status;\r
1607   }\r
1608 \r
1609   //\r
1610   // Disconnect the driver specified by ImageHandle from all\r
1611   // the devices in the handle database.\r
1612   //\r
1613   for (Index = 0; Index < DeviceHandleCount; Index++) {\r
1614     Status = gBS->DisconnectController (\r
1615                     DeviceHandleBuffer[Index],\r
1616                     ImageHandle,\r
1617                     NULL\r
1618                     );\r
1619   }\r
1620 \r
1621   //\r
1622   // Uninstall all the protocols installed in the driver entry point\r
1623   //\r
1624   for (Index = 0; Index < DeviceHandleCount; Index++) {\r
1625     Status = gBS->HandleProtocol (\r
1626                     DeviceHandleBuffer[Index],\r
1627                     &gEfiDriverBindingProtocolGuid,\r
1628                     (VOID **) &DriverBinding\r
1629                     );\r
1630 \r
1631     if (EFI_ERROR (Status)) {\r
1632       continue;\r
1633     }\r
1634 \r
1635     if (DriverBinding->ImageHandle != ImageHandle) {\r
1636       continue;\r
1637     }\r
1638 \r
1639     gBS->UninstallProtocolInterface (\r
1640           ImageHandle,\r
1641           &gEfiDriverBindingProtocolGuid,\r
1642           DriverBinding\r
1643           );\r
1644     Status = gBS->HandleProtocol (\r
1645                     DeviceHandleBuffer[Index],\r
1646                     &gEfiComponentNameProtocolGuid,\r
1647                     (VOID **) &ComponentName\r
1648                     );\r
1649     if (!EFI_ERROR (Status)) {\r
1650       gBS->UninstallProtocolInterface (\r
1651              ImageHandle,\r
1652              &gEfiComponentNameProtocolGuid,\r
1653              ComponentName\r
1654              );\r
1655     }\r
1656 \r
1657     Status = gBS->HandleProtocol (\r
1658                     DeviceHandleBuffer[Index],\r
1659                     &gEfiComponentName2ProtocolGuid,\r
1660                     (VOID **) &ComponentName2\r
1661                     );\r
1662     if (!EFI_ERROR (Status)) {\r
1663       gBS->UninstallProtocolInterface (\r
1664              ImageHandle,\r
1665              &gEfiComponentName2ProtocolGuid,\r
1666              ComponentName2\r
1667              );\r
1668     }\r
1669   }\r
1670 \r
1671   //\r
1672   // Free the buffer containing the list of handles from the handle database\r
1673   //\r
1674   if (DeviceHandleBuffer != NULL) {\r
1675     gBS->FreePool (DeviceHandleBuffer);\r
1676   }\r
1677 \r
1678   return EFI_SUCCESS;\r
1679 }\r
1680 \r
1681 \r
1682 \r
1683 /**\r
1684   Create a child of the service that is identified by ServiceBindingGuid.\r
1685 \r
1686   Get the ServiceBinding Protocol first, then use it to create a child.\r
1687 \r
1688   If ServiceBindingGuid is NULL, then ASSERT().\r
1689   If ChildHandle is NULL, then ASSERT().\r
1690 \r
1691   @param[in]       Controller            The controller which has the service installed.\r
1692   @param[in]       Image                 The image handle used to open service.\r
1693   @param[in]       ServiceBindingGuid    The service's Guid.\r
1694   @param[in, out]  ChildHandle           The handle to receive the create child.\r
1695 \r
1696   @retval EFI_SUCCESS           The child is successfully created.\r
1697   @retval Others                Failed to create the child.\r
1698 \r
1699 **/\r
1700 EFI_STATUS\r
1701 EFIAPI\r
1702 NetLibCreateServiceChild (\r
1703   IN  EFI_HANDLE            Controller,\r
1704   IN  EFI_HANDLE            Image,\r
1705   IN  EFI_GUID              *ServiceBindingGuid,\r
1706   IN  OUT EFI_HANDLE        *ChildHandle\r
1707   )\r
1708 {\r
1709   EFI_STATUS                    Status;\r
1710   EFI_SERVICE_BINDING_PROTOCOL  *Service;\r
1711 \r
1712 \r
1713   ASSERT ((ServiceBindingGuid != NULL) && (ChildHandle != NULL));\r
1714 \r
1715   //\r
1716   // Get the ServiceBinding Protocol\r
1717   //\r
1718   Status = gBS->OpenProtocol (\r
1719                   Controller,\r
1720                   ServiceBindingGuid,\r
1721                   (VOID **) &Service,\r
1722                   Image,\r
1723                   Controller,\r
1724                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1725                   );\r
1726 \r
1727   if (EFI_ERROR (Status)) {\r
1728     return Status;\r
1729   }\r
1730 \r
1731   //\r
1732   // Create a child\r
1733   //\r
1734   Status = Service->CreateChild (Service, ChildHandle);\r
1735   return Status;\r
1736 }\r
1737 \r
1738 \r
1739 /**\r
1740   Destory a child of the service that is identified by ServiceBindingGuid.\r
1741 \r
1742   Get the ServiceBinding Protocol first, then use it to destroy a child.\r
1743 \r
1744   If ServiceBindingGuid is NULL, then ASSERT().\r
1745 \r
1746   @param[in]   Controller            The controller which has the service installed.\r
1747   @param[in]   Image                 The image handle used to open service.\r
1748   @param[in]   ServiceBindingGuid    The service's Guid.\r
1749   @param[in]   ChildHandle           The child to destory.\r
1750 \r
1751   @retval EFI_SUCCESS           The child is successfully destoried.\r
1752   @retval Others                Failed to destory the child.\r
1753 \r
1754 **/\r
1755 EFI_STATUS\r
1756 EFIAPI\r
1757 NetLibDestroyServiceChild (\r
1758   IN  EFI_HANDLE            Controller,\r
1759   IN  EFI_HANDLE            Image,\r
1760   IN  EFI_GUID              *ServiceBindingGuid,\r
1761   IN  EFI_HANDLE            ChildHandle\r
1762   )\r
1763 {\r
1764   EFI_STATUS                    Status;\r
1765   EFI_SERVICE_BINDING_PROTOCOL  *Service;\r
1766 \r
1767   ASSERT (ServiceBindingGuid != NULL);\r
1768 \r
1769   //\r
1770   // Get the ServiceBinding Protocol\r
1771   //\r
1772   Status = gBS->OpenProtocol (\r
1773                   Controller,\r
1774                   ServiceBindingGuid,\r
1775                   (VOID **) &Service,\r
1776                   Image,\r
1777                   Controller,\r
1778                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1779                   );\r
1780 \r
1781   if (EFI_ERROR (Status)) {\r
1782     return Status;\r
1783   }\r
1784 \r
1785   //\r
1786   // destory the child\r
1787   //\r
1788   Status = Service->DestroyChild (Service, ChildHandle);\r
1789   return Status;\r
1790 }\r
1791 \r
1792 /**\r
1793   Get handle with Simple Network Protocol installed on it.\r
1794 \r
1795   There should be MNP Service Binding Protocol installed on the input ServiceHandle.\r
1796   If Simple Network Protocol is already installed on the ServiceHandle, the\r
1797   ServiceHandle will be returned. If SNP is not installed on the ServiceHandle,\r
1798   try to find its parent handle with SNP installed.\r
1799 \r
1800   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
1801                                 installed on.\r
1802   @param[out]  Snp              The pointer to store the address of the SNP instance.\r
1803                                 This is an optional parameter that may be NULL.\r
1804 \r
1805   @return The SNP handle, or NULL if not found.\r
1806 \r
1807 **/\r
1808 EFI_HANDLE\r
1809 EFIAPI\r
1810 NetLibGetSnpHandle (\r
1811   IN   EFI_HANDLE                  ServiceHandle,\r
1812   OUT  EFI_SIMPLE_NETWORK_PROTOCOL **Snp  OPTIONAL\r
1813   )\r
1814 {\r
1815   EFI_STATUS                   Status;\r
1816   EFI_SIMPLE_NETWORK_PROTOCOL  *SnpInstance;\r
1817   EFI_DEVICE_PATH_PROTOCOL     *DevicePath;\r
1818   EFI_HANDLE                   SnpHandle;\r
1819 \r
1820   //\r
1821   // Try to open SNP from ServiceHandle\r
1822   //\r
1823   SnpInstance = NULL;\r
1824   Status = gBS->HandleProtocol (ServiceHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance);\r
1825   if (!EFI_ERROR (Status)) {\r
1826     if (Snp != NULL) {\r
1827       *Snp = SnpInstance;\r
1828     }\r
1829     return ServiceHandle;\r
1830   }\r
1831 \r
1832   //\r
1833   // Failed to open SNP, try to get SNP handle by LocateDevicePath()\r
1834   //\r
1835   DevicePath = DevicePathFromHandle (ServiceHandle);\r
1836   if (DevicePath == NULL) {\r
1837     return NULL;\r
1838   }\r
1839 \r
1840   SnpHandle = NULL;\r
1841   Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &DevicePath, &SnpHandle);\r
1842   if (EFI_ERROR (Status)) {\r
1843     //\r
1844     // Failed to find SNP handle\r
1845     //\r
1846     return NULL;\r
1847   }\r
1848 \r
1849   Status = gBS->HandleProtocol (SnpHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance);\r
1850   if (!EFI_ERROR (Status)) {\r
1851     if (Snp != NULL) {\r
1852       *Snp = SnpInstance;\r
1853     }\r
1854     return SnpHandle;\r
1855   }\r
1856 \r
1857   return NULL;\r
1858 }\r
1859 \r
1860 /**\r
1861   Retrieve VLAN ID of a VLAN device handle.\r
1862 \r
1863   Search VLAN device path node in Device Path of specified ServiceHandle and\r
1864   return its VLAN ID. If no VLAN device path node found, then this ServiceHandle\r
1865   is not a VLAN device handle, and 0 will be returned.\r
1866 \r
1867   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
1868                                 installed on.\r
1869 \r
1870   @return VLAN ID of the device handle, or 0 if not a VLAN device.\r
1871 \r
1872 **/\r
1873 UINT16\r
1874 EFIAPI\r
1875 NetLibGetVlanId (\r
1876   IN EFI_HANDLE             ServiceHandle\r
1877   )\r
1878 {\r
1879   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
1880   EFI_DEVICE_PATH_PROTOCOL  *Node;\r
1881 \r
1882   DevicePath = DevicePathFromHandle (ServiceHandle);\r
1883   if (DevicePath == NULL) {\r
1884     return 0;\r
1885   }\r
1886 \r
1887   Node = DevicePath;\r
1888   while (!IsDevicePathEnd (Node)) {\r
1889     if (Node->Type == MESSAGING_DEVICE_PATH && Node->SubType == MSG_VLAN_DP) {\r
1890       return ((VLAN_DEVICE_PATH *) Node)->VlanId;\r
1891     }\r
1892     Node = NextDevicePathNode (Node);\r
1893   }\r
1894 \r
1895   return 0;\r
1896 }\r
1897 \r
1898 /**\r
1899   Find VLAN device handle with specified VLAN ID.\r
1900 \r
1901   The VLAN child device handle is created by VLAN Config Protocol on ControllerHandle.\r
1902   This function will append VLAN device path node to the parent device path,\r
1903   and then use LocateDevicePath() to find the correct VLAN device handle.\r
1904 \r
1905   @param[in]   ControllerHandle The handle where network service binding protocols are\r
1906                                 installed on.\r
1907   @param[in]   VlanId           The configured VLAN ID for the VLAN device.\r
1908 \r
1909   @return The VLAN device handle, or NULL if not found.\r
1910 \r
1911 **/\r
1912 EFI_HANDLE\r
1913 EFIAPI\r
1914 NetLibGetVlanHandle (\r
1915   IN EFI_HANDLE             ControllerHandle,\r
1916   IN UINT16                 VlanId\r
1917   )\r
1918 {\r
1919   EFI_DEVICE_PATH_PROTOCOL  *ParentDevicePath;\r
1920   EFI_DEVICE_PATH_PROTOCOL  *VlanDevicePath;\r
1921   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
1922   VLAN_DEVICE_PATH          VlanNode;\r
1923   EFI_HANDLE                Handle;\r
1924 \r
1925   ParentDevicePath = DevicePathFromHandle (ControllerHandle);\r
1926   if (ParentDevicePath == NULL) {\r
1927     return NULL;\r
1928   }\r
1929 \r
1930   //\r
1931   // Construct VLAN device path\r
1932   //\r
1933   CopyMem (&VlanNode, &mNetVlanDevicePathTemplate, sizeof (VLAN_DEVICE_PATH));\r
1934   VlanNode.VlanId = VlanId;\r
1935   VlanDevicePath = AppendDevicePathNode (\r
1936                      ParentDevicePath,\r
1937                      (EFI_DEVICE_PATH_PROTOCOL *) &VlanNode\r
1938                      );\r
1939   if (VlanDevicePath == NULL) {\r
1940     return NULL;\r
1941   }\r
1942 \r
1943   //\r
1944   // Find VLAN device handle\r
1945   //\r
1946   Handle = NULL;\r
1947   DevicePath = VlanDevicePath;\r
1948   gBS->LocateDevicePath (\r
1949          &gEfiDevicePathProtocolGuid,\r
1950          &DevicePath,\r
1951          &Handle\r
1952          );\r
1953   if (!IsDevicePathEnd (DevicePath)) {\r
1954     //\r
1955     // Device path is not exactly match\r
1956     //\r
1957     Handle = NULL;\r
1958   }\r
1959 \r
1960   FreePool (VlanDevicePath);\r
1961   return Handle;\r
1962 }\r
1963 \r
1964 /**\r
1965   Get MAC address associated with the network service handle.\r
1966 \r
1967   There should be MNP Service Binding Protocol installed on the input ServiceHandle.\r
1968   If SNP is installed on the ServiceHandle or its parent handle, MAC address will\r
1969   be retrieved from SNP. If no SNP found, try to get SNP mode data use MNP.\r
1970 \r
1971   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
1972                                 installed on.\r
1973   @param[out]  MacAddress       The pointer to store the returned MAC address.\r
1974   @param[out]  AddressSize      The length of returned MAC address.\r
1975 \r
1976   @retval EFI_SUCCESS           MAC address is returned successfully.\r
1977   @retval Others                Failed to get SNP mode data.\r
1978 \r
1979 **/\r
1980 EFI_STATUS\r
1981 EFIAPI\r
1982 NetLibGetMacAddress (\r
1983   IN  EFI_HANDLE            ServiceHandle,\r
1984   OUT EFI_MAC_ADDRESS       *MacAddress,\r
1985   OUT UINTN                 *AddressSize\r
1986   )\r
1987 {\r
1988   EFI_STATUS                   Status;\r
1989   EFI_SIMPLE_NETWORK_PROTOCOL  *Snp;\r
1990   EFI_SIMPLE_NETWORK_MODE      *SnpMode;\r
1991   EFI_SIMPLE_NETWORK_MODE      SnpModeData;\r
1992   EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
1993   EFI_SERVICE_BINDING_PROTOCOL *MnpSb;\r
1994   EFI_HANDLE                   *SnpHandle;\r
1995   EFI_HANDLE                   MnpChildHandle;\r
1996 \r
1997   ASSERT (MacAddress != NULL);\r
1998   ASSERT (AddressSize != NULL);\r
1999 \r
2000   //\r
2001   // Try to get SNP handle\r
2002   //\r
2003   Snp = NULL;\r
2004   SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);\r
2005   if (SnpHandle != NULL) {\r
2006     //\r
2007     // SNP found, use it directly\r
2008     //\r
2009     SnpMode = Snp->Mode;\r
2010   } else {\r
2011     //\r
2012     // Failed to get SNP handle, try to get MAC address from MNP\r
2013     //\r
2014     MnpChildHandle = NULL;\r
2015     Status = gBS->HandleProtocol (\r
2016                     ServiceHandle,\r
2017                     &gEfiManagedNetworkServiceBindingProtocolGuid,\r
2018                     (VOID **) &MnpSb\r
2019                     );\r
2020     if (EFI_ERROR (Status)) {\r
2021       return Status;\r
2022     }\r
2023 \r
2024     //\r
2025     // Create a MNP child\r
2026     //\r
2027     Status = MnpSb->CreateChild (MnpSb, &MnpChildHandle);\r
2028     if (EFI_ERROR (Status)) {\r
2029       return Status;\r
2030     }\r
2031 \r
2032     //\r
2033     // Open MNP protocol\r
2034     //\r
2035     Status = gBS->HandleProtocol (\r
2036                     MnpChildHandle,\r
2037                     &gEfiManagedNetworkProtocolGuid,\r
2038                     (VOID **) &Mnp\r
2039                     );\r
2040     if (EFI_ERROR (Status)) {\r
2041       return Status;\r
2042     }\r
2043 \r
2044     //\r
2045     // Try to get SNP mode from MNP\r
2046     //\r
2047     Status = Mnp->GetModeData (Mnp, NULL, &SnpModeData);\r
2048     if (EFI_ERROR (Status)) {\r
2049       return Status;\r
2050     }\r
2051     SnpMode = &SnpModeData;\r
2052 \r
2053     //\r
2054     // Destroy the MNP child\r
2055     //\r
2056     MnpSb->DestroyChild (MnpSb, MnpChildHandle);\r
2057   }\r
2058 \r
2059   *AddressSize = SnpMode->HwAddressSize;\r
2060   CopyMem (MacAddress->Addr, SnpMode->CurrentAddress.Addr, SnpMode->HwAddressSize);\r
2061 \r
2062   return EFI_SUCCESS;\r
2063 }\r
2064 \r
2065 /**\r
2066   Convert MAC address of the NIC associated with specified Service Binding Handle\r
2067   to a unicode string. Callers are responsible for freeing the string storage.\r
2068 \r
2069   Locate simple network protocol associated with the Service Binding Handle and\r
2070   get the mac address from SNP. Then convert the mac address into a unicode\r
2071   string. It takes 2 unicode characters to represent a 1 byte binary buffer.\r
2072   Plus one unicode character for the null-terminator.\r
2073 \r
2074   @param[in]   ServiceHandle         The handle where network service binding protocol is\r
2075                                      installed on.\r
2076   @param[in]   ImageHandle           The image handle used to act as the agent handle to\r
2077                                      get the simple network protocol.\r
2078   @param[out]  MacString             The pointer to store the address of the string\r
2079                                      representation of  the mac address.\r
2080 \r
2081   @retval EFI_SUCCESS           Convert the mac address a unicode string successfully.\r
2082   @retval EFI_OUT_OF_RESOURCES  There are not enough memory resource.\r
2083   @retval Others                Failed to open the simple network protocol.\r
2084 \r
2085 **/\r
2086 EFI_STATUS\r
2087 EFIAPI\r
2088 NetLibGetMacString (\r
2089   IN  EFI_HANDLE            ServiceHandle,\r
2090   IN  EFI_HANDLE            ImageHandle,\r
2091   OUT CHAR16                **MacString\r
2092   )\r
2093 {\r
2094   EFI_STATUS                   Status;\r
2095   EFI_MAC_ADDRESS              MacAddress;\r
2096   UINT8                        *HwAddress;\r
2097   UINTN                        HwAddressSize;\r
2098   UINT16                       VlanId;\r
2099   CHAR16                       *String;\r
2100   UINTN                        Index;\r
2101 \r
2102   ASSERT (MacString != NULL);\r
2103 \r
2104   //\r
2105   // Get MAC address of the network device\r
2106   //\r
2107   Status = NetLibGetMacAddress (ServiceHandle, &MacAddress, &HwAddressSize);\r
2108   if (EFI_ERROR (Status)) {\r
2109     return Status;\r
2110   }\r
2111 \r
2112   //\r
2113   // It takes 2 unicode characters to represent a 1 byte binary buffer.\r
2114   // If VLAN is configured, it will need extra 5 characters like "\0005".\r
2115   // Plus one unicode character for the null-terminator.\r
2116   //\r
2117   String = AllocateZeroPool ((2 * HwAddressSize + 5 + 1) * sizeof (CHAR16));\r
2118   if (String == NULL) {\r
2119     return EFI_OUT_OF_RESOURCES;\r
2120   }\r
2121   *MacString = String;\r
2122 \r
2123   //\r
2124   // Convert the MAC address into a unicode string.\r
2125   //\r
2126   HwAddress = &MacAddress.Addr[0];\r
2127   for (Index = 0; Index < HwAddressSize; Index++) {\r
2128     String += UnicodeValueToString (String, PREFIX_ZERO | RADIX_HEX, *(HwAddress++), 2);\r
2129   }\r
2130 \r
2131   //\r
2132   // Append VLAN ID if any\r
2133   //\r
2134   VlanId = NetLibGetVlanId (ServiceHandle);\r
2135   if (VlanId != 0) {\r
2136     *String++ = L'\\';\r
2137     String += UnicodeValueToString (String, PREFIX_ZERO | RADIX_HEX, VlanId, 4);\r
2138   }\r
2139 \r
2140   //\r
2141   // Null terminate the Unicode string\r
2142   //\r
2143   *String = L'\0';\r
2144 \r
2145   return EFI_SUCCESS;\r
2146 }\r
2147 \r
2148 /**\r
2149   Detect media status for specified network device.\r
2150 \r
2151   The underlying UNDI driver may or may not support reporting media status from\r
2152   GET_STATUS command (PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED). This routine\r
2153   will try to invoke Snp->GetStatus() to get the media status: if media already\r
2154   present, it return directly; if media not present, it will stop SNP and then\r
2155   restart SNP to get the latest media status, this give chance to get the correct\r
2156   media status for old UNDI driver which doesn't support reporting media status\r
2157   from GET_STATUS command.\r
2158   Note: there will be two limitations for current algorithm:\r
2159   1) for UNDI with this capability, in case of cable is not attached, there will\r
2160      be an redundant Stop/Start() process;\r
2161   2) for UNDI without this capability, in case that network cable is attached when\r
2162      Snp->Initialize() is invoked while network cable is unattached later,\r
2163      NetLibDetectMedia() will report MediaPresent as TRUE, causing upper layer\r
2164      apps to wait for timeout time.\r
2165 \r
2166   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
2167                                 installed on.\r
2168   @param[out]  MediaPresent     The pointer to store the media status.\r
2169 \r
2170   @retval EFI_SUCCESS           Media detection success.\r
2171   @retval EFI_INVALID_PARAMETER ServiceHandle is not valid network device handle.\r
2172   @retval EFI_UNSUPPORTED       Network device does not support media detection.\r
2173   @retval EFI_DEVICE_ERROR      SNP is in unknown state.\r
2174 \r
2175 **/\r
2176 EFI_STATUS\r
2177 EFIAPI\r
2178 NetLibDetectMedia (\r
2179   IN  EFI_HANDLE            ServiceHandle,\r
2180   OUT BOOLEAN               *MediaPresent\r
2181   )\r
2182 {\r
2183   EFI_STATUS                   Status;\r
2184   EFI_HANDLE                   SnpHandle;\r
2185   EFI_SIMPLE_NETWORK_PROTOCOL  *Snp;\r
2186   UINT32                       InterruptStatus;\r
2187   UINT32                       OldState;\r
2188   EFI_MAC_ADDRESS              *MCastFilter;\r
2189   UINT32                       MCastFilterCount;\r
2190   UINT32                       EnableFilterBits;\r
2191   UINT32                       DisableFilterBits;\r
2192   BOOLEAN                      ResetMCastFilters;\r
2193 \r
2194   ASSERT (MediaPresent != NULL);\r
2195 \r
2196   //\r
2197   // Get SNP handle\r
2198   //\r
2199   Snp = NULL;\r
2200   SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);\r
2201   if (SnpHandle == NULL) {\r
2202     return EFI_INVALID_PARAMETER;\r
2203   }\r
2204 \r
2205   //\r
2206   // Check whether SNP support media detection\r
2207   //\r
2208   if (!Snp->Mode->MediaPresentSupported) {\r
2209     return EFI_UNSUPPORTED;\r
2210   }\r
2211 \r
2212   //\r
2213   // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data\r
2214   //\r
2215   Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);\r
2216   if (EFI_ERROR (Status)) {\r
2217     return Status;\r
2218   }\r
2219 \r
2220   if (Snp->Mode->MediaPresent) {\r
2221     //\r
2222     // Media is present, return directly\r
2223     //\r
2224     *MediaPresent = TRUE;\r
2225     return EFI_SUCCESS;\r
2226   }\r
2227 \r
2228   //\r
2229   // Till now, GetStatus() report no media; while, in case UNDI not support\r
2230   // reporting media status from GetStatus(), this media status may be incorrect.\r
2231   // So, we will stop SNP and then restart it to get the correct media status.\r
2232   //\r
2233   OldState = Snp->Mode->State;\r
2234   if (OldState >= EfiSimpleNetworkMaxState) {\r
2235     return EFI_DEVICE_ERROR;\r
2236   }\r
2237 \r
2238   MCastFilter = NULL;\r
2239 \r
2240   if (OldState == EfiSimpleNetworkInitialized) {\r
2241     //\r
2242     // SNP is already in use, need Shutdown/Stop and then Start/Initialize\r
2243     //\r
2244 \r
2245     //\r
2246     // Backup current SNP receive filter settings\r
2247     //\r
2248     EnableFilterBits  = Snp->Mode->ReceiveFilterSetting;\r
2249     DisableFilterBits = Snp->Mode->ReceiveFilterMask ^ EnableFilterBits;\r
2250 \r
2251     ResetMCastFilters = TRUE;\r
2252     MCastFilterCount  = Snp->Mode->MCastFilterCount;\r
2253     if (MCastFilterCount != 0) {\r
2254       MCastFilter = AllocateCopyPool (\r
2255                       MCastFilterCount * sizeof (EFI_MAC_ADDRESS),\r
2256                       Snp->Mode->MCastFilter\r
2257                       );\r
2258       ASSERT (MCastFilter != NULL);\r
2259 \r
2260       ResetMCastFilters = FALSE;\r
2261     }\r
2262 \r
2263     //\r
2264     // Shutdown/Stop the simple network\r
2265     //\r
2266     Status = Snp->Shutdown (Snp);\r
2267     if (!EFI_ERROR (Status)) {\r
2268       Status = Snp->Stop (Snp);\r
2269     }\r
2270     if (EFI_ERROR (Status)) {\r
2271       goto Exit;\r
2272     }\r
2273 \r
2274     //\r
2275     // Start/Initialize the simple network\r
2276     //\r
2277     Status = Snp->Start (Snp);\r
2278     if (!EFI_ERROR (Status)) {\r
2279       Status = Snp->Initialize (Snp, 0, 0);\r
2280     }\r
2281     if (EFI_ERROR (Status)) {\r
2282       goto Exit;\r
2283     }\r
2284 \r
2285     //\r
2286     // Here we get the correct media status\r
2287     //\r
2288     *MediaPresent = Snp->Mode->MediaPresent;\r
2289 \r
2290     //\r
2291     // Restore SNP receive filter settings\r
2292     //\r
2293     Status = Snp->ReceiveFilters (\r
2294                     Snp,\r
2295                     EnableFilterBits,\r
2296                     DisableFilterBits,\r
2297                     ResetMCastFilters,\r
2298                     MCastFilterCount,\r
2299                     MCastFilter\r
2300                     );\r
2301 \r
2302     if (MCastFilter != NULL) {\r
2303       FreePool (MCastFilter);\r
2304     }\r
2305 \r
2306     return Status;\r
2307   }\r
2308 \r
2309   //\r
2310   // SNP is not in use, it's in state of EfiSimpleNetworkStopped or EfiSimpleNetworkStarted\r
2311   //\r
2312   if (OldState == EfiSimpleNetworkStopped) {\r
2313     //\r
2314     // SNP not start yet, start it\r
2315     //\r
2316     Status = Snp->Start (Snp);\r
2317     if (EFI_ERROR (Status)) {\r
2318       goto Exit;\r
2319     }\r
2320   }\r
2321 \r
2322   //\r
2323   // Initialize the simple network\r
2324   //\r
2325   Status = Snp->Initialize (Snp, 0, 0);\r
2326   if (EFI_ERROR (Status)) {\r
2327     Status = EFI_DEVICE_ERROR;\r
2328     goto Exit;\r
2329   }\r
2330 \r
2331   //\r
2332   // Here we get the correct media status\r
2333   //\r
2334   *MediaPresent = Snp->Mode->MediaPresent;\r
2335 \r
2336   //\r
2337   // Shut down the simple network\r
2338   //\r
2339   Snp->Shutdown (Snp);\r
2340 \r
2341 Exit:\r
2342   if (OldState == EfiSimpleNetworkStopped) {\r
2343     //\r
2344     // Original SNP sate is Stopped, restore to original state\r
2345     //\r
2346     Snp->Stop (Snp);\r
2347   }\r
2348 \r
2349   if (MCastFilter != NULL) {\r
2350     FreePool (MCastFilter);\r
2351   }\r
2352 \r
2353   return Status;\r
2354 }\r
2355 \r
2356 /**\r
2357   Check the default address used by the IPv4 driver is static or dynamic (acquired\r
2358   from DHCP).\r
2359 \r
2360   If the controller handle does not have the NIC Ip4 Config Protocol installed, the\r
2361   default address is static. If the EFI variable to save the configuration is not found,\r
2362   the default address is static. Otherwise, get the result from the EFI variable which\r
2363   saving the configuration.\r
2364 \r
2365   @param[in]   Controller     The controller handle which has the NIC Ip4 Config Protocol\r
2366                               relative with the default address to judge.\r
2367 \r
2368   @retval TRUE           If the default address is static.\r
2369   @retval FALSE          If the default address is acquired from DHCP.\r
2370 \r
2371 **/\r
2372 BOOLEAN\r
2373 NetLibDefaultAddressIsStatic (\r
2374   IN EFI_HANDLE  Controller\r
2375   )\r
2376 {\r
2377   EFI_STATUS                       Status;\r
2378   EFI_HII_CONFIG_ROUTING_PROTOCOL  *HiiConfigRouting;\r
2379   UINTN                            Len;\r
2380   NIC_IP4_CONFIG_INFO              *ConfigInfo;\r
2381   BOOLEAN                          IsStatic;\r
2382   EFI_STRING                       ConfigHdr;\r
2383   EFI_STRING                       ConfigResp;\r
2384   EFI_STRING                       AccessProgress;\r
2385   EFI_STRING                       AccessResults;\r
2386   EFI_STRING                       String;\r
2387 \r
2388   ConfigInfo       = NULL;\r
2389   ConfigHdr        = NULL;\r
2390   ConfigResp       = NULL;\r
2391   AccessProgress   = NULL;\r
2392   AccessResults    = NULL;\r
2393   IsStatic         = TRUE;\r
2394 \r
2395   Status = gBS->LocateProtocol (\r
2396                   &gEfiHiiConfigRoutingProtocolGuid,\r
2397                   NULL,\r
2398                   (VOID **) &HiiConfigRouting\r
2399                   );\r
2400   if (EFI_ERROR (Status)) {\r
2401     return TRUE;\r
2402   }\r
2403 \r
2404   //\r
2405   // Construct config request string header\r
2406   //\r
2407   ConfigHdr = HiiConstructConfigHdr (&gEfiNicIp4ConfigVariableGuid, EFI_NIC_IP4_CONFIG_VARIABLE, Controller);\r
2408   if (ConfigHdr == NULL) {\r
2409     return TRUE;\r
2410   }\r
2411 \r
2412   Len = StrLen (ConfigHdr);\r
2413   ConfigResp = AllocateZeroPool ((Len + NIC_ITEM_CONFIG_SIZE * 2 + 100) * sizeof (CHAR16));\r
2414   if (ConfigResp == NULL) {\r
2415     goto ON_EXIT;\r
2416   }\r
2417   StrCpy (ConfigResp, ConfigHdr);\r
2418 \r
2419   String = ConfigResp + Len;\r
2420   UnicodeSPrint (\r
2421     String,\r
2422     (8 + 4 + 7 + 4 + 1) * sizeof (CHAR16),\r
2423     L"&OFFSET=%04X&WIDTH=%04X",\r
2424     OFFSET_OF (NIC_IP4_CONFIG_INFO, Source),\r
2425     sizeof (UINT32)\r
2426     );\r
2427 \r
2428   Status = HiiConfigRouting->ExtractConfig (\r
2429                                HiiConfigRouting,\r
2430                                ConfigResp,\r
2431                                &AccessProgress,\r
2432                                &AccessResults\r
2433                                );\r
2434   if (EFI_ERROR (Status)) {\r
2435     goto ON_EXIT;\r
2436   }\r
2437 \r
2438   ConfigInfo = AllocateZeroPool (sizeof (NIC_ITEM_CONFIG_SIZE));\r
2439   if (ConfigInfo == NULL) {\r
2440     goto ON_EXIT;\r
2441   }\r
2442 \r
2443   ConfigInfo->Source = IP4_CONFIG_SOURCE_STATIC;\r
2444   Len = NIC_ITEM_CONFIG_SIZE;\r
2445   Status = HiiConfigRouting->ConfigToBlock (\r
2446                                HiiConfigRouting,\r
2447                                AccessResults,\r
2448                                (UINT8 *) ConfigInfo,\r
2449                                &Len,\r
2450                                &AccessProgress\r
2451                                );\r
2452   if (EFI_ERROR (Status)) {\r
2453     goto ON_EXIT;\r
2454   }\r
2455 \r
2456   IsStatic = (BOOLEAN) (ConfigInfo->Source == IP4_CONFIG_SOURCE_STATIC);\r
2457 \r
2458 ON_EXIT:\r
2459 \r
2460   if (AccessResults != NULL) {\r
2461     FreePool (AccessResults);\r
2462   }\r
2463   if (ConfigInfo != NULL) {\r
2464     FreePool (ConfigInfo);\r
2465   }\r
2466   if (ConfigResp != NULL) {\r
2467     FreePool (ConfigResp);\r
2468   }\r
2469   if (ConfigHdr != NULL) {\r
2470     FreePool (ConfigHdr);\r
2471   }\r
2472 \r
2473   return IsStatic;\r
2474 }\r
2475 \r
2476 /**\r
2477   Create an IPv4 device path node.\r
2478 \r
2479   The header type of IPv4 device path node is MESSAGING_DEVICE_PATH.\r
2480   The header subtype of IPv4 device path node is MSG_IPv4_DP.\r
2481   The length of the IPv4 device path node in bytes is 19.\r
2482   Get other info from parameters to make up the whole IPv4 device path node.\r
2483 \r
2484   @param[in, out]  Node                  Pointer to the IPv4 device path node.\r
2485   @param[in]       Controller            The controller handle.\r
2486   @param[in]       LocalIp               The local IPv4 address.\r
2487   @param[in]       LocalPort             The local port.\r
2488   @param[in]       RemoteIp              The remote IPv4 address.\r
2489   @param[in]       RemotePort            The remote port.\r
2490   @param[in]       Protocol              The protocol type in the IP header.\r
2491   @param[in]       UseDefaultAddress     Whether this instance is using default address or not.\r
2492 \r
2493 **/\r
2494 VOID\r
2495 EFIAPI\r
2496 NetLibCreateIPv4DPathNode (\r
2497   IN OUT IPv4_DEVICE_PATH  *Node,\r
2498   IN EFI_HANDLE            Controller,\r
2499   IN IP4_ADDR              LocalIp,\r
2500   IN UINT16                LocalPort,\r
2501   IN IP4_ADDR              RemoteIp,\r
2502   IN UINT16                RemotePort,\r
2503   IN UINT16                Protocol,\r
2504   IN BOOLEAN               UseDefaultAddress\r
2505   )\r
2506 {\r
2507   Node->Header.Type    = MESSAGING_DEVICE_PATH;\r
2508   Node->Header.SubType = MSG_IPv4_DP;\r
2509   SetDevicePathNodeLength (&Node->Header, 19);\r
2510 \r
2511   CopyMem (&Node->LocalIpAddress, &LocalIp, sizeof (EFI_IPv4_ADDRESS));\r
2512   CopyMem (&Node->RemoteIpAddress, &RemoteIp, sizeof (EFI_IPv4_ADDRESS));\r
2513 \r
2514   Node->LocalPort  = LocalPort;\r
2515   Node->RemotePort = RemotePort;\r
2516 \r
2517   Node->Protocol = Protocol;\r
2518 \r
2519   if (!UseDefaultAddress) {\r
2520     Node->StaticIpAddress = TRUE;\r
2521   } else {\r
2522     Node->StaticIpAddress = NetLibDefaultAddressIsStatic (Controller);\r
2523   }\r
2524 }\r
2525 \r
2526 /**\r
2527   Create an IPv6 device path node.\r
2528 \r
2529   The header type of IPv6 device path node is MESSAGING_DEVICE_PATH.\r
2530   The header subtype of IPv6 device path node is MSG_IPv6_DP.\r
2531   Get other info from parameters to make up the whole IPv6 device path node.\r
2532 \r
2533   @param[in, out]  Node                  Pointer to the IPv6 device path node.\r
2534   @param[in]       Controller            The controller handle.\r
2535   @param[in]       LocalIp               The local IPv6 address.\r
2536   @param[in]       LocalPort             The local port.\r
2537   @param[in]       RemoteIp              The remote IPv6 address.\r
2538   @param[in]       RemotePort            The remote port.\r
2539   @param[in]       Protocol              The protocol type in the IP header.\r
2540 \r
2541 **/\r
2542 VOID\r
2543 EFIAPI\r
2544 NetLibCreateIPv6DPathNode (\r
2545   IN OUT IPv6_DEVICE_PATH  *Node,\r
2546   IN EFI_HANDLE            Controller,\r
2547   IN EFI_IPv6_ADDRESS      *LocalIp,\r
2548   IN UINT16                LocalPort,\r
2549   IN EFI_IPv6_ADDRESS      *RemoteIp,\r
2550   IN UINT16                RemotePort,\r
2551   IN UINT16                Protocol\r
2552   )\r
2553 {\r
2554   Node->Header.Type    = MESSAGING_DEVICE_PATH;\r
2555   Node->Header.SubType = MSG_IPv6_DP;\r
2556   SetDevicePathNodeLength (&Node->Header, sizeof (IPv6_DEVICE_PATH));\r
2557 \r
2558   CopyMem (&Node->LocalIpAddress, LocalIp, sizeof (EFI_IPv6_ADDRESS));\r
2559   CopyMem (&Node->RemoteIpAddress, RemoteIp, sizeof (EFI_IPv6_ADDRESS));\r
2560 \r
2561   Node->LocalPort  = LocalPort;\r
2562   Node->RemotePort = RemotePort;\r
2563 \r
2564   Node->Protocol        = Protocol;\r
2565   Node->StaticIpAddress = FALSE;\r
2566 }\r
2567 \r
2568 /**\r
2569   Find the UNDI/SNP handle from controller and protocol GUID.\r
2570 \r
2571   For example, IP will open a MNP child to transmit/receive\r
2572   packets, when MNP is stopped, IP should also be stopped. IP\r
2573   needs to find its own private data which is related the IP's\r
2574   service binding instance that is install on UNDI/SNP handle.\r
2575   Now, the controller is either a MNP or ARP child handle. But\r
2576   IP opens these handle BY_DRIVER, use that info, we can get the\r
2577   UNDI/SNP handle.\r
2578 \r
2579   @param[in]  Controller            Then protocol handle to check.\r
2580   @param[in]  ProtocolGuid          The protocol that is related with the handle.\r
2581 \r
2582   @return The UNDI/SNP handle or NULL for errors.\r
2583 \r
2584 **/\r
2585 EFI_HANDLE\r
2586 EFIAPI\r
2587 NetLibGetNicHandle (\r
2588   IN EFI_HANDLE             Controller,\r
2589   IN EFI_GUID               *ProtocolGuid\r
2590   )\r
2591 {\r
2592   EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenBuffer;\r
2593   EFI_HANDLE                          Handle;\r
2594   EFI_STATUS                          Status;\r
2595   UINTN                               OpenCount;\r
2596   UINTN                               Index;\r
2597 \r
2598   Status = gBS->OpenProtocolInformation (\r
2599                   Controller,\r
2600                   ProtocolGuid,\r
2601                   &OpenBuffer,\r
2602                   &OpenCount\r
2603                   );\r
2604 \r
2605   if (EFI_ERROR (Status)) {\r
2606     return NULL;\r
2607   }\r
2608 \r
2609   Handle = NULL;\r
2610 \r
2611   for (Index = 0; Index < OpenCount; Index++) {\r
2612     if ((OpenBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) {\r
2613       Handle = OpenBuffer[Index].ControllerHandle;\r
2614       break;\r
2615     }\r
2616   }\r
2617 \r
2618   gBS->FreePool (OpenBuffer);\r
2619   return Handle;\r
2620 }\r
2621 \r
2622 /**\r
2623   Convert one Null-terminated ASCII string (decimal dotted) to EFI_IPv4_ADDRESS.\r
2624 \r
2625   @param[in]      String         The pointer to the Ascii string.\r
2626   @param[out]     Ip4Address     The pointer to the converted IPv4 address.\r
2627 \r
2628   @retval EFI_SUCCESS            Convert to IPv4 address successfully.\r
2629   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip4Address is NULL.\r
2630 \r
2631 **/\r
2632 EFI_STATUS\r
2633 NetLibAsciiStrToIp4 (\r
2634   IN CONST CHAR8                 *String,\r
2635   OUT      EFI_IPv4_ADDRESS      *Ip4Address\r
2636   )\r
2637 {\r
2638   UINT8                          Index;\r
2639   CHAR8                          *Ip4Str;\r
2640   CHAR8                          *TempStr;\r
2641   UINTN                          NodeVal;\r
2642 \r
2643   if ((String == NULL) || (Ip4Address == NULL)) {\r
2644     return EFI_INVALID_PARAMETER;\r
2645   }\r
2646 \r
2647   Ip4Str = (CHAR8 *) String;\r
2648 \r
2649   for (Index = 0; Index < 4; Index++) {\r
2650     TempStr = Ip4Str;\r
2651 \r
2652     while ((*Ip4Str != '\0') && (*Ip4Str != '.')) {\r
2653       Ip4Str++;\r
2654     }\r
2655 \r
2656     //\r
2657     // The IPv4 address is X.X.X.X\r
2658     //\r
2659     if (*Ip4Str == '.') {\r
2660       if (Index == 3) {\r
2661         return EFI_INVALID_PARAMETER;\r
2662       }\r
2663     } else {\r
2664       if (Index != 3) {\r
2665         return EFI_INVALID_PARAMETER;\r
2666       }\r
2667     }\r
2668 \r
2669     //\r
2670     // Convert the string to IPv4 address. AsciiStrDecimalToUintn stops at the\r
2671     // first character that is not a valid decimal character, '.' or '\0' here.\r
2672     //\r
2673     NodeVal = AsciiStrDecimalToUintn (TempStr);\r
2674     if (NodeVal > 0xFF) {\r
2675       return EFI_INVALID_PARAMETER;\r
2676     }\r
2677 \r
2678     Ip4Address->Addr[Index] = (UINT8) NodeVal;\r
2679 \r
2680     Ip4Str++;\r
2681   }\r
2682 \r
2683   return EFI_SUCCESS;\r
2684 }\r
2685 \r
2686 \r
2687 /**\r
2688   Convert one Null-terminated ASCII string to EFI_IPv6_ADDRESS. The format of the\r
2689   string is defined in RFC 4291 - Text Pepresentation of Addresses.\r
2690 \r
2691   @param[in]      String         The pointer to the Ascii string.\r
2692   @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
2693 \r
2694   @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
2695   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
2696 \r
2697 **/\r
2698 EFI_STATUS\r
2699 NetLibAsciiStrToIp6 (\r
2700   IN CONST CHAR8                 *String,\r
2701   OUT      EFI_IPv6_ADDRESS      *Ip6Address\r
2702   )\r
2703 {\r
2704   UINT8                          Index;\r
2705   CHAR8                          *Ip6Str;\r
2706   CHAR8                          *TempStr;\r
2707   CHAR8                          *TempStr2;\r
2708   UINT8                          NodeCnt;\r
2709   UINT8                          TailNodeCnt;\r
2710   UINT8                          AllowedCnt;\r
2711   UINTN                          NodeVal;\r
2712   BOOLEAN                        Short;\r
2713   BOOLEAN                        Update;\r
2714 \r
2715   if ((String == NULL) || (Ip6Address == NULL)) {\r
2716     return EFI_INVALID_PARAMETER;\r
2717   }\r
2718 \r
2719   Ip6Str     = (CHAR8 *) String;\r
2720   AllowedCnt = 6;\r
2721 \r
2722   //\r
2723   // An IPv6 address leading with : looks strange.\r
2724   //\r
2725   if (*Ip6Str == ':') {\r
2726     if (*(Ip6Str + 1) != ':') {\r
2727       return EFI_INVALID_PARAMETER;\r
2728     } else {\r
2729       AllowedCnt = 7;\r
2730     }\r
2731   }\r
2732 \r
2733   ZeroMem (Ip6Address, sizeof (EFI_IPv6_ADDRESS));\r
2734 \r
2735   NodeCnt     = 0;\r
2736   TailNodeCnt = 0;\r
2737   Short       = FALSE;\r
2738   Update      = FALSE;\r
2739 \r
2740   for (Index = 0; Index < 15; Index = (UINT8) (Index + 2)) {\r
2741     TempStr = Ip6Str;\r
2742 \r
2743     while ((*Ip6Str != '\0') && (*Ip6Str != ':')) {\r
2744       Ip6Str++;\r
2745     }\r
2746 \r
2747     if ((*Ip6Str == '\0') && (Index != 14)) {\r
2748       return EFI_INVALID_PARAMETER;\r
2749     }\r
2750 \r
2751     if (*Ip6Str == ':') {\r
2752       if (*(Ip6Str + 1) == ':') {\r
2753         if ((*(Ip6Str + 2) == '0') || (NodeCnt > 6)) {\r
2754           //\r
2755           // ::0 looks strange. report error to user.\r
2756           //\r
2757           return EFI_INVALID_PARAMETER;\r
2758         }\r
2759 \r
2760         //\r
2761         // Skip the abbreviation part of IPv6 address.\r
2762         //\r
2763         TempStr2 = Ip6Str + 2;\r
2764         while ((*TempStr2 != '\0')) {\r
2765           if (*TempStr2 == ':') {\r
2766             if (*(TempStr2 + 1) == ':') {\r
2767               //\r
2768               // :: can only appear once in IPv6 address.\r
2769               //\r
2770               return EFI_INVALID_PARAMETER;\r
2771             }\r
2772 \r
2773             TailNodeCnt++;\r
2774             if (TailNodeCnt >= (AllowedCnt - NodeCnt)) {\r
2775               //\r
2776               // :: indicates one or more groups of 16 bits of zeros.\r
2777               //\r
2778               return EFI_INVALID_PARAMETER;\r
2779             }\r
2780           }\r
2781 \r
2782           TempStr2++;\r
2783         }\r
2784 \r
2785         Short  = TRUE;\r
2786         Update = TRUE;\r
2787 \r
2788         Ip6Str = Ip6Str + 2;\r
2789       } else {\r
2790         Ip6Str++;\r
2791         NodeCnt++;\r
2792         if ((Short && (NodeCnt > 6)) || (!Short && (NodeCnt > 7))) {\r
2793           //\r
2794           // There are more than 8 groups of 16 bits of zeros.\r
2795           //\r
2796           return EFI_INVALID_PARAMETER;\r
2797         }\r
2798       }\r
2799     }\r
2800 \r
2801     //\r
2802     // Convert the string to IPv6 address. AsciiStrHexToUintn stops at the first\r
2803     // character that is not a valid hexadecimal character, ':' or '\0' here.\r
2804     //\r
2805     NodeVal = AsciiStrHexToUintn (TempStr);\r
2806     if ((NodeVal > 0xFFFF) || (Index > 14)) {\r
2807       return EFI_INVALID_PARAMETER;\r
2808     }\r
2809 \r
2810     Ip6Address->Addr[Index] = (UINT8) (NodeVal >> 8);\r
2811     Ip6Address->Addr[Index + 1] = (UINT8) (NodeVal & 0xFF);\r
2812 \r
2813     //\r
2814     // Skip the groups of zeros by ::\r
2815     //\r
2816     if (Short && Update) {\r
2817       Index  = (UINT8) (16 - (TailNodeCnt + 2) * 2);\r
2818       Update = FALSE;\r
2819     }\r
2820   }\r
2821 \r
2822   if ((!Short && Index != 16) || (*Ip6Str != '\0')) {\r
2823     return EFI_INVALID_PARAMETER;\r
2824   }\r
2825 \r
2826   return EFI_SUCCESS;\r
2827 }\r
2828 \r
2829 \r
2830 /**\r
2831   Convert one Null-terminated Unicode string (decimal dotted) to EFI_IPv4_ADDRESS.\r
2832 \r
2833   @param[in]      String         The pointer to the Ascii string.\r
2834   @param[out]     Ip4Address     The pointer to the converted IPv4 address.\r
2835 \r
2836   @retval EFI_SUCCESS            Convert to IPv4 address successfully.\r
2837   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip4Address is NULL.\r
2838   @retval EFI_OUT_OF_RESOURCES   Fail to perform the operation due to lack of resource.\r
2839 \r
2840 **/\r
2841 EFI_STATUS\r
2842 NetLibStrToIp4 (\r
2843   IN CONST CHAR16                *String,\r
2844   OUT      EFI_IPv4_ADDRESS      *Ip4Address\r
2845   )\r
2846 {\r
2847   CHAR8                          *Ip4Str;\r
2848   EFI_STATUS                     Status;\r
2849 \r
2850   if ((String == NULL) || (Ip4Address == NULL)) {\r
2851     return EFI_INVALID_PARAMETER;\r
2852   }\r
2853 \r
2854   Ip4Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));\r
2855   if (Ip4Str == NULL) {\r
2856     return EFI_OUT_OF_RESOURCES;\r
2857   }\r
2858 \r
2859   UnicodeStrToAsciiStr (String, Ip4Str);\r
2860 \r
2861   Status = NetLibAsciiStrToIp4 (Ip4Str, Ip4Address);\r
2862 \r
2863   FreePool (Ip4Str);\r
2864 \r
2865   return Status;\r
2866 }\r
2867 \r
2868 \r
2869 /**\r
2870   Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS.  The format of\r
2871   the string is defined in RFC 4291 - Text Pepresentation of Addresses.\r
2872 \r
2873   @param[in]      String         The pointer to the Ascii string.\r
2874   @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
2875 \r
2876   @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
2877   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
2878   @retval EFI_OUT_OF_RESOURCES   Fail to perform the operation due to lack of resource.\r
2879 \r
2880 **/\r
2881 EFI_STATUS\r
2882 NetLibStrToIp6 (\r
2883   IN CONST CHAR16                *String,\r
2884   OUT      EFI_IPv6_ADDRESS      *Ip6Address\r
2885   )\r
2886 {\r
2887   CHAR8                          *Ip6Str;\r
2888   EFI_STATUS                     Status;\r
2889 \r
2890   if ((String == NULL) || (Ip6Address == NULL)) {\r
2891     return EFI_INVALID_PARAMETER;\r
2892   }\r
2893 \r
2894   Ip6Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));\r
2895   if (Ip6Str == NULL) {\r
2896     return EFI_OUT_OF_RESOURCES;\r
2897   }\r
2898 \r
2899   UnicodeStrToAsciiStr (String, Ip6Str);\r
2900 \r
2901   Status = NetLibAsciiStrToIp6 (Ip6Str, Ip6Address);\r
2902 \r
2903   FreePool (Ip6Str);\r
2904 \r
2905   return Status;\r
2906 }\r
2907 \r
2908 /**\r
2909   Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS and prefix length.\r
2910   The format of the string is defined in RFC 4291 - Text Pepresentation of Addresses\r
2911   Prefixes: ipv6-address/prefix-length.\r
2912 \r
2913   @param[in]      String         The pointer to the Ascii string.\r
2914   @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
2915   @param[out]     PrefixLength   The pointer to the converted prefix length.\r
2916 \r
2917   @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
2918   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
2919   @retval EFI_OUT_OF_RESOURCES   Fail to perform the operation due to lack of resource.\r
2920 \r
2921 **/\r
2922 EFI_STATUS\r
2923 NetLibStrToIp6andPrefix (\r
2924   IN CONST CHAR16                *String,\r
2925   OUT      EFI_IPv6_ADDRESS      *Ip6Address,\r
2926   OUT      UINT8                 *PrefixLength\r
2927   )\r
2928 {\r
2929   CHAR8                          *Ip6Str;\r
2930   CHAR8                          *PrefixStr;\r
2931   CHAR8                          *TempStr;\r
2932   EFI_STATUS                     Status;\r
2933   UINT8                          Length;\r
2934 \r
2935   if ((String == NULL) || (Ip6Address == NULL) || (PrefixLength == NULL)) {\r
2936     return EFI_INVALID_PARAMETER;\r
2937   }\r
2938 \r
2939   Ip6Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));\r
2940   if (Ip6Str == NULL) {\r
2941     return EFI_OUT_OF_RESOURCES;\r
2942   }\r
2943 \r
2944   UnicodeStrToAsciiStr (String, Ip6Str);\r
2945 \r
2946   //\r
2947   // Get the sub string describing prefix length.\r
2948   //\r
2949   TempStr = Ip6Str;\r
2950   while (*TempStr != '\0' && (*TempStr != '/')) {\r
2951     TempStr++;\r
2952   }\r
2953 \r
2954   if (*TempStr == '/') {\r
2955     PrefixStr = TempStr + 1;\r
2956   } else {\r
2957     PrefixStr = NULL;\r
2958   }\r
2959 \r
2960   //\r
2961   // Get the sub string describing IPv6 address and convert it.\r
2962   //\r
2963   *TempStr = '\0';\r
2964 \r
2965   Status = NetLibAsciiStrToIp6 (Ip6Str, Ip6Address);\r
2966   if (EFI_ERROR (Status)) {\r
2967     goto Exit;\r
2968   }\r
2969 \r
2970   //\r
2971   // If input string doesn't indicate the prefix length, return 0xff.\r
2972   //\r
2973   Length = 0xFF;\r
2974   \r
2975   //\r
2976   // Convert the string to prefix length\r
2977   //\r
2978   if (PrefixStr != NULL) {\r
2979 \r
2980     Status = EFI_INVALID_PARAMETER;\r
2981     Length = 0;\r
2982     while (*PrefixStr != '\0') {\r
2983       if (NET_IS_DIGIT (*PrefixStr)) {\r
2984         Length = (UINT8) (Length * 10 + (*PrefixStr - '0'));\r
2985         if (Length >= IP6_PREFIX_NUM) {\r
2986           goto Exit;\r
2987         }\r
2988       } else {\r
2989         goto Exit;\r
2990       }\r
2991 \r
2992       PrefixStr++;\r
2993     }\r
2994   }\r
2995 \r
2996   *PrefixLength = Length;\r
2997   Status        = EFI_SUCCESS;\r
2998 \r
2999 Exit:\r
3000 \r
3001   FreePool (Ip6Str);\r
3002   return Status;\r
3003 }\r
3004 \r