• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Functions implementation related with DHCPv6 for UefiPxeBc Driver.
3 
4   (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
6 
7   This program and the accompanying materials
8   are licensed and made available under the terms and conditions of the BSD License
9   which accompanies this distribution.  The full text of the license may be found at
10   http://opensource.org/licenses/bsd-license.php.
11 
12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "PxeBcImpl.h"
18 
19 //
20 // Well-known multi-cast address defined in section-24.1 of rfc-3315
21 //
22 //   ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
23 //
24 EFI_IPv6_ADDRESS   mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
25 
26 /**
27   Parse out a DHCPv6 option by OptTag, and find the position in buffer.
28 
29   @param[in]  Buffer        The pointer to the option buffer.
30   @param[in]  Length        Length of the option buffer.
31   @param[in]  OptTag        The required option tag.
32 
33   @retval     NULL          Failed to parse the required option.
34   @retval     Others        The postion of the required option in buffer.
35 
36 **/
37 EFI_DHCP6_PACKET_OPTION *
PxeBcParseDhcp6Options(IN UINT8 * Buffer,IN UINT32 Length,IN UINT16 OptTag)38 PxeBcParseDhcp6Options (
39   IN UINT8                       *Buffer,
40   IN UINT32                      Length,
41   IN UINT16                      OptTag
42   )
43 {
44   EFI_DHCP6_PACKET_OPTION        *Option;
45   UINT32                         Offset;
46 
47   Option  = (EFI_DHCP6_PACKET_OPTION *) Buffer;
48   Offset  = 0;
49 
50   //
51   // OpLen and OpCode here are both stored in network order.
52   //
53   while (Offset < Length) {
54 
55     if (NTOHS (Option->OpCode) == OptTag) {
56 
57       return Option;
58     }
59 
60     Offset += (NTOHS(Option->OpLen) + 4);
61     Option  = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);
62   }
63 
64   return NULL;
65 }
66 
67 
68 /**
69   Build the options buffer for the DHCPv6 request packet.
70 
71   @param[in]  Private             The pointer to PxeBc private data.
72   @param[out] OptList             The pointer to the option pointer array.
73   @param[in]  Buffer              The pointer to the buffer to contain the option list.
74 
75   @return     Index               The count of the built-in options.
76 
77 **/
78 UINT32
PxeBcBuildDhcp6Options(IN PXEBC_PRIVATE_DATA * Private,OUT EFI_DHCP6_PACKET_OPTION ** OptList,IN UINT8 * Buffer)79 PxeBcBuildDhcp6Options (
80   IN  PXEBC_PRIVATE_DATA           *Private,
81   OUT EFI_DHCP6_PACKET_OPTION      **OptList,
82   IN  UINT8                        *Buffer
83   )
84 {
85   PXEBC_DHCP6_OPTION_ENTRY         OptEnt;
86   UINT32                           Index;
87   UINT16                           Value;
88 
89   Index       = 0;
90   OptList[0]  = (EFI_DHCP6_PACKET_OPTION *) Buffer;
91 
92   //
93   // Append client option request option
94   //
95   OptList[Index]->OpCode     = HTONS (DHCP6_OPT_ORO);
96   OptList[Index]->OpLen      = HTONS (6);
97   OptEnt.Oro                 = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;
98   OptEnt.Oro->OpCode[0]      = HTONS(DHCP6_OPT_BOOT_FILE_URL);
99   OptEnt.Oro->OpCode[1]      = HTONS(DHCP6_OPT_BOOT_FILE_PARAM);
100   OptEnt.Oro->OpCode[2]      = HTONS(DHCP6_OPT_DNS_SERVERS);
101   Index++;
102   OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
103 
104   //
105   // Append client network device interface option
106   //
107   OptList[Index]->OpCode     = HTONS (DHCP6_OPT_UNDI);
108   OptList[Index]->OpLen      = HTONS ((UINT16)3);
109   OptEnt.Undi                = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;
110 
111   if (Private->Nii != NULL) {
112     OptEnt.Undi->Type        = Private->Nii->Type;
113     OptEnt.Undi->MajorVer    = Private->Nii->MajorVer;
114     OptEnt.Undi->MinorVer    = Private->Nii->MinorVer;
115   } else {
116     OptEnt.Undi->Type        = DEFAULT_UNDI_TYPE;
117     OptEnt.Undi->MajorVer    = DEFAULT_UNDI_MAJOR;
118     OptEnt.Undi->MinorVer    = DEFAULT_UNDI_MINOR;
119   }
120 
121   Index++;
122   OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
123 
124   //
125   // Append client system architecture option
126   //
127   OptList[Index]->OpCode     = HTONS (DHCP6_OPT_ARCH);
128   OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));
129   OptEnt.Arch                = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;
130   Value                      = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
131   CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
132   Index++;
133   OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
134 
135   //
136   // Append vendor class option to store the PXE class identifier.
137   //
138   OptList[Index]->OpCode       = HTONS (DHCP6_OPT_VENDOR_CLASS);
139   OptList[Index]->OpLen        = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));
140   OptEnt.VendorClass           = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;
141   OptEnt.VendorClass->Vendor   = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);
142   OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));
143   CopyMem (
144     &OptEnt.VendorClass->ClassId,
145     DEFAULT_CLASS_ID_DATA,
146     sizeof (PXEBC_CLASS_ID)
147     );
148   PxeBcUintnToAscDecWithFormat (
149     EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,
150     OptEnt.VendorClass->ClassId.ArchitectureType,
151     sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)
152     );
153 
154   if (Private->Nii != NULL) {
155     CopyMem (
156       OptEnt.VendorClass->ClassId.InterfaceName,
157       Private->Nii->StringId,
158       sizeof (OptEnt.VendorClass->ClassId.InterfaceName)
159       );
160     PxeBcUintnToAscDecWithFormat (
161       Private->Nii->MajorVer,
162       OptEnt.VendorClass->ClassId.UndiMajor,
163       sizeof (OptEnt.VendorClass->ClassId.UndiMajor)
164       );
165     PxeBcUintnToAscDecWithFormat (
166       Private->Nii->MinorVer,
167       OptEnt.VendorClass->ClassId.UndiMinor,
168       sizeof (OptEnt.VendorClass->ClassId.UndiMinor)
169       );
170   }
171 
172   Index++;
173 
174   return Index;
175 }
176 
177 
178 /**
179   Cache the DHCPv6 packet.
180 
181   @param[in]  Dst          The pointer to the cache buffer for DHCPv6 packet.
182   @param[in]  Src          The pointer to the DHCPv6 packet to be cached.
183 
184   @retval     EFI_SUCCESS                Packet is copied.
185   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
186 
187 **/
188 EFI_STATUS
PxeBcCacheDhcp6Packet(IN EFI_DHCP6_PACKET * Dst,IN EFI_DHCP6_PACKET * Src)189 PxeBcCacheDhcp6Packet (
190   IN EFI_DHCP6_PACKET          *Dst,
191   IN EFI_DHCP6_PACKET          *Src
192   )
193 {
194   if (Dst->Size < Src->Length) {
195     return EFI_BUFFER_TOO_SMALL;
196   }
197 
198   CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
199   Dst->Length = Src->Length;
200 
201   return EFI_SUCCESS;
202 }
203 
204 
205 /**
206   Free all the nodes in the list for boot file.
207 
208   @param[in]  Head            The pointer to the head of list.
209 
210 **/
211 VOID
PxeBcFreeBootFileOption(IN LIST_ENTRY * Head)212 PxeBcFreeBootFileOption (
213   IN LIST_ENTRY               *Head
214   )
215 {
216   LIST_ENTRY                  *Entry;
217   LIST_ENTRY                  *NextEntry;
218   PXEBC_DHCP6_OPTION_NODE     *Node;
219 
220   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {
221     Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);
222     RemoveEntryList (Entry);
223     FreePool (Node);
224   }
225 }
226 
227 /**
228   Retrieve the boot server address using the EFI_DNS6_PROTOCOL.
229 
230   @param[in]  Private             Pointer to PxeBc private data.
231   @param[in]  HostName            Pointer to buffer containing hostname.
232   @param[out] IpAddress           On output, pointer to buffer containing IPv6 address.
233 
234   @retval EFI_SUCCESS             Operation succeeded.
235   @retval EFI_OUT_OF_RESOURCES    Failed to allocate needed resources.
236   @retval EFI_DEVICE_ERROR        An unexpected network error occurred.
237   @retval Others                  Other errors as indicated.
238 
239 **/
240 EFI_STATUS
PxeBcDns6(IN PXEBC_PRIVATE_DATA * Private,IN CHAR16 * HostName,OUT EFI_IPv6_ADDRESS * IpAddress)241 PxeBcDns6 (
242   IN PXEBC_PRIVATE_DATA           *Private,
243   IN     CHAR16                   *HostName,
244      OUT EFI_IPv6_ADDRESS         *IpAddress
245   )
246 {
247   EFI_STATUS                      Status;
248   EFI_DNS6_PROTOCOL               *Dns6;
249   EFI_DNS6_CONFIG_DATA            Dns6ConfigData;
250   EFI_DNS6_COMPLETION_TOKEN       Token;
251   EFI_HANDLE                      Dns6Handle;
252   EFI_IPv6_ADDRESS                *DnsServerList;
253   BOOLEAN                         IsDone;
254 
255   Dns6                = NULL;
256   Dns6Handle          = NULL;
257   DnsServerList       = Private->DnsServer;
258   ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));
259 
260   //
261   // Create a DNSv6 child instance and get the protocol.
262   //
263   Status = NetLibCreateServiceChild (
264              Private->Controller,
265              Private->Image,
266              &gEfiDns6ServiceBindingProtocolGuid,
267              &Dns6Handle
268              );
269   if (EFI_ERROR (Status)) {
270     goto Exit;
271   }
272 
273   Status = gBS->OpenProtocol (
274                   Dns6Handle,
275                   &gEfiDns6ProtocolGuid,
276                   (VOID **) &Dns6,
277                   Private->Image,
278                   Private->Controller,
279                   EFI_OPEN_PROTOCOL_BY_DRIVER
280                   );
281   if (EFI_ERROR (Status)) {
282     goto Exit;
283   }
284 
285   //
286   // Configure DNS6 instance for the DNS server address and protocol.
287   //
288   ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));
289   Dns6ConfigData.DnsServerCount = 1;
290   Dns6ConfigData.DnsServerList  = DnsServerList;
291   Dns6ConfigData.EnableDnsCache = TRUE;
292   Dns6ConfigData.Protocol       = EFI_IP_PROTO_UDP;
293   IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &Private->TmpStationIp.v6);
294   Status = Dns6->Configure (
295                    Dns6,
296                    &Dns6ConfigData
297                    );
298   if (EFI_ERROR (Status)) {
299     goto Exit;
300   }
301 
302   Token.Status = EFI_NOT_READY;
303   IsDone       = FALSE;
304   //
305   // Create event to set the  IsDone flag when name resolution is finished.
306   //
307   Status = gBS->CreateEvent (
308                   EVT_NOTIFY_SIGNAL,
309                   TPL_NOTIFY,
310                   PxeBcCommonNotify,
311                   &IsDone,
312                   &Token.Event
313                   );
314   if (EFI_ERROR (Status)) {
315     goto Exit;
316   }
317 
318   //
319   // Start asynchronous name resolution.
320   //
321   Status = Dns6->HostNameToIp (Dns6, HostName, &Token);
322   if (EFI_ERROR (Status)) {
323     goto Exit;
324   }
325 
326   while (!IsDone) {
327     Dns6->Poll (Dns6);
328   }
329 
330   //
331   // Name resolution is done, check result.
332   //
333   Status = Token.Status;
334   if (!EFI_ERROR (Status)) {
335     if (Token.RspData.H2AData == NULL) {
336       Status = EFI_DEVICE_ERROR;
337       goto Exit;
338     }
339     if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {
340       Status = EFI_DEVICE_ERROR;
341       goto Exit;
342     }
343     //
344     // We just return the first IPv6 address from DNS protocol.
345     //
346     IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);
347     Status = EFI_SUCCESS;
348   }
349 
350 Exit:
351   FreePool (HostName);
352 
353   if (Token.Event != NULL) {
354     gBS->CloseEvent (Token.Event);
355   }
356   if (Token.RspData.H2AData != NULL) {
357     if (Token.RspData.H2AData->IpList != NULL) {
358       FreePool (Token.RspData.H2AData->IpList);
359     }
360     FreePool (Token.RspData.H2AData);
361   }
362 
363   if (Dns6 != NULL) {
364     Dns6->Configure (Dns6, NULL);
365 
366     gBS->CloseProtocol (
367            Dns6Handle,
368            &gEfiDns6ProtocolGuid,
369            Private->Image,
370            Private->Controller
371            );
372   }
373 
374   if (Dns6Handle != NULL) {
375     NetLibDestroyServiceChild (
376       Private->Controller,
377       Private->Image,
378       &gEfiDns6ServiceBindingProtocolGuid,
379       Dns6Handle
380       );
381   }
382 
383   if (DnsServerList != NULL) {
384     FreePool (DnsServerList);
385   }
386 
387   return Status;
388 }
389 
390 /**
391   Parse the Boot File URL option.
392 
393   @param[in]      Private      Pointer to PxeBc private data.
394   @param[out]     FileName     The pointer to the boot file name.
395   @param[in, out] SrvAddr      The pointer to the boot server address.
396   @param[in]      BootFile     The pointer to the boot file URL option data.
397   @param[in]      Length       The length of the boot file URL option data.
398 
399   @retval EFI_ABORTED     User cancel operation.
400   @retval EFI_SUCCESS     Selected the boot menu successfully.
401   @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.
402 
403 **/
404 EFI_STATUS
PxeBcExtractBootFileUrl(IN PXEBC_PRIVATE_DATA * Private,OUT UINT8 ** FileName,IN OUT EFI_IPv6_ADDRESS * SrvAddr,IN CHAR8 * BootFile,IN UINT16 Length)405 PxeBcExtractBootFileUrl (
406   IN PXEBC_PRIVATE_DATA      *Private,
407      OUT UINT8               **FileName,
408   IN OUT EFI_IPv6_ADDRESS    *SrvAddr,
409   IN     CHAR8               *BootFile,
410   IN     UINT16              Length
411   )
412 {
413   UINT16                     PrefixLen;
414   CHAR8                      *BootFileNamePtr;
415   CHAR8                      *BootFileName;
416   UINT16                     BootFileNameLen;
417   CHAR8                      *TmpStr;
418   CHAR8                      TmpChar;
419   CHAR8                      *ServerAddressOption;
420   CHAR8                      *ServerAddress;
421   CHAR8                      *ModeStr;
422   CHAR16                     *HostName;
423   BOOLEAN                    IpExpressedUrl;
424   UINTN                      Len;
425   EFI_STATUS                 Status;
426 
427   IpExpressedUrl = TRUE;
428   //
429   // The format of the Boot File URL option is:
430   //
431   //  0                   1                   2                   3
432   //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
433   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
434   // |       OPT_BOOTFILE_URL        |            option-len         |
435   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
436   // |                                                               |
437   // .                  bootfile-url  (variable length)              .
438   // |                                                               |
439   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440   //
441 
442   //
443   // Based upon RFC 5970 and UEFI 2.6, bootfile-url format can be
444   // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME or tftp://domain_name/BOOTFILE_NAME
445   // As an example where the BOOTFILE_NAME is the EFI loader and
446   // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.
447   //
448   PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);
449 
450   if (Length <= PrefixLen ||
451       CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {
452     return EFI_NOT_FOUND;
453   }
454 
455   BootFile = BootFile + PrefixLen;
456   Length   = (UINT16) (Length - PrefixLen);
457 
458   TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);
459   if (TmpStr == NULL) {
460     return EFI_OUT_OF_RESOURCES;
461   }
462 
463   CopyMem (TmpStr, BootFile, Length);
464   TmpStr[Length] = '\0';
465 
466   //
467   // Get the part of SERVER_ADDRESS string.
468   //
469   ServerAddressOption = TmpStr;
470   if (*ServerAddressOption == PXEBC_ADDR_START_DELIMITER) {
471     ServerAddressOption ++;
472     ServerAddress = ServerAddressOption;
473     while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {
474       ServerAddress++;
475     }
476 
477     if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {
478       FreePool (TmpStr);
479       return EFI_INVALID_PARAMETER;
480     }
481 
482     *ServerAddress = '\0';
483 
484     //
485     // Convert the string of server address to Ipv6 address format and store it.
486     //
487     Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);
488     if (EFI_ERROR (Status)) {
489       FreePool (TmpStr);
490       return Status;
491     }
492 
493   } else {
494     IpExpressedUrl = FALSE;
495     ServerAddress = ServerAddressOption;
496     while (*ServerAddress != '\0' && *ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {
497       ServerAddress++;
498     }
499 
500     if (*ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {
501       FreePool (TmpStr);
502       return EFI_INVALID_PARAMETER;
503     }
504     *ServerAddress = '\0';
505 
506     Len = AsciiStrSize (ServerAddressOption);
507     HostName = AllocateZeroPool (Len * sizeof (CHAR16));
508     if (HostName == NULL) {
509       FreePool (TmpStr);
510       return EFI_OUT_OF_RESOURCES;
511     }
512     AsciiStrToUnicodeStrS (
513       ServerAddressOption,
514       HostName,
515       Len
516       );
517 
518     //
519     // Perform DNS resolution.
520     //
521     Status = PxeBcDns6 (Private,HostName, SrvAddr);
522     if (EFI_ERROR (Status)) {
523       FreePool (TmpStr);
524       return Status;
525     }
526   }
527 
528   //
529   // Get the part of BOOTFILE_NAME string.
530   //
531   BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);
532   if (IpExpressedUrl) {
533     if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {
534       FreePool (TmpStr);
535       return EFI_INVALID_PARAMETER;
536     }
537     ++BootFileNamePtr;
538   }
539 
540   BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);
541   if (BootFileNameLen != 0 || FileName != NULL) {
542     //
543     // Remove trailing mode=octet if present and ignore.  All other modes are
544     // invalid for netboot6, so reject them.
545     //
546     ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet");
547     if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') {
548       *ModeStr = '\0';
549     } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) {
550       return EFI_INVALID_PARAMETER;
551     }
552 
553     //
554     // Extract boot file name from URL.
555     //
556     BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen);
557     if (BootFileName == NULL) {
558       FreePool (TmpStr);
559       return EFI_OUT_OF_RESOURCES;
560     }
561     *FileName = (UINT8*) BootFileName;
562 
563     //
564     // Decode percent-encoding in boot file name.
565     //
566     while (*BootFileNamePtr != '\0') {
567       if (*BootFileNamePtr == '%') {
568         TmpChar = *(BootFileNamePtr+ 3);
569         *(BootFileNamePtr+ 3) = '\0';
570         *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1));
571         BootFileName++;
572         *(BootFileNamePtr+ 3) = TmpChar;
573         BootFileNamePtr += 3;
574       } else {
575         *BootFileName = *BootFileNamePtr;
576         BootFileName++;
577         BootFileNamePtr++;
578       }
579     }
580     *BootFileName = '\0';
581   }
582 
583   FreePool (TmpStr);
584 
585   return EFI_SUCCESS;
586 }
587 
588 
589 /**
590   Parse the Boot File Parameter option.
591 
592   @param[in]  BootFilePara      The pointer to boot file parameter option data.
593   @param[out] BootFileSize      The pointer to the parsed boot file size.
594 
595   @retval EFI_SUCCESS     Successfully obtained the boot file size from parameter option.
596   @retval EFI_NOT_FOUND   Failed to extract the boot file size from parameter option.
597 
598 **/
599 EFI_STATUS
PxeBcExtractBootFileParam(IN CHAR8 * BootFilePara,OUT UINT16 * BootFileSize)600 PxeBcExtractBootFileParam (
601   IN  CHAR8                  *BootFilePara,
602   OUT UINT16                 *BootFileSize
603   )
604 {
605   UINT16                     Length;
606   UINT8                      Index;
607   UINT8                      Digit;
608   UINT32                     Size;
609 
610   CopyMem (&Length, BootFilePara, sizeof (UINT16));
611   Length = NTOHS (Length);
612 
613   //
614   // The BootFile Size should be 1~5 byte ASCII strings
615   //
616   if (Length < 1 || Length > 5) {
617     return EFI_NOT_FOUND;
618   }
619 
620   //
621   // Extract the value of BootFile Size.
622   //
623   BootFilePara = BootFilePara + sizeof (UINT16);
624   Size         = 0;
625   for (Index = 0; Index < Length; Index++) {
626     if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {
627       return EFI_NOT_FOUND;
628     }
629 
630     Size = (Size + Digit) * 10;
631   }
632 
633   Size = Size / 10;
634   if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {
635     return EFI_NOT_FOUND;
636   }
637 
638   *BootFileSize = (UINT16) Size;
639   return EFI_SUCCESS;
640 }
641 
642 
643 /**
644   Parse the cached DHCPv6 packet, including all the options.
645 
646   @param[in]  Cache6           The pointer to a cached DHCPv6 packet.
647 
648   @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.
649   @retval     EFI_DEVICE_ERROR Failed to parse and invalid the packet.
650 
651 **/
652 EFI_STATUS
PxeBcParseDhcp6Packet(IN PXEBC_DHCP6_PACKET_CACHE * Cache6)653 PxeBcParseDhcp6Packet (
654   IN PXEBC_DHCP6_PACKET_CACHE  *Cache6
655   )
656 {
657   EFI_DHCP6_PACKET             *Offer;
658   EFI_DHCP6_PACKET_OPTION      **Options;
659   EFI_DHCP6_PACKET_OPTION      *Option;
660   PXEBC_OFFER_TYPE             OfferType;
661   BOOLEAN                      IsProxyOffer;
662   BOOLEAN                      IsPxeOffer;
663   UINT32                       Offset;
664   UINT32                       Length;
665   UINT32                       EnterpriseNum;
666 
667   IsProxyOffer = TRUE;
668   IsPxeOffer   = FALSE;
669   Offer        = &Cache6->Packet.Offer;
670   Options      = Cache6->OptList;
671 
672   ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));
673 
674   Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);
675   Offset  = 0;
676   Length  = GET_DHCP6_OPTION_SIZE (Offer);
677 
678   //
679   // OpLen and OpCode here are both stored in network order, since they are from original packet.
680   //
681   while (Offset < Length) {
682 
683     if (NTOHS (Option->OpCode) == DHCP6_OPT_IA_NA) {
684       Options[PXEBC_DHCP6_IDX_IA_NA] = Option;
685     } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_URL) {
686       //
687       // The server sends this option to inform the client about an URL to a boot file.
688       //
689       Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;
690     } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_PARAM) {
691       Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
692     } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {
693       Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;
694     } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) {
695       Options[PXEBC_DHCP6_IDX_DNS_SERVER] = Option;
696     }
697 
698     Offset += (NTOHS (Option->OpLen) + 4);
699     Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);
700   }
701 
702   //
703   // The offer with assigned client address is NOT a proxy offer.
704   // An ia_na option, embeded with valid ia_addr option and a status_code of success.
705   //
706   Option = Options[PXEBC_DHCP6_IDX_IA_NA];
707   if (Option != NULL) {
708     Option = PxeBcParseDhcp6Options (
709                Option->Data + 12,
710                NTOHS (Option->OpLen),
711                DHCP6_OPT_STATUS_CODE
712                );
713     if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {
714       IsProxyOffer = FALSE;
715     }
716   }
717 
718   //
719   // The offer with "PXEClient" is a pxe offer.
720   //
721   Option        = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];
722   EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM);
723 
724   if (Option != NULL &&
725       NTOHS(Option->OpLen) >= 13 &&
726       CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&
727       CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0) {
728     IsPxeOffer = TRUE;
729   }
730 
731   //
732   // Determine offer type of the dhcp6 packet.
733   //
734   if (IsPxeOffer) {
735     //
736     // It's a binl offer only with PXEClient.
737     //
738     OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
739   } else {
740     //
741     // It's a dhcp only offer, which is a pure dhcp6 offer packet.
742     //
743     OfferType = PxeOfferTypeDhcpOnly;
744   }
745 
746   Cache6->OfferType = OfferType;
747 
748   return EFI_SUCCESS;
749 }
750 
751 
752 /**
753   Cache the DHCPv6 ack packet, and parse it on demand.
754 
755   @param[in]  Private             The pointer to PxeBc private data.
756   @param[in]  Ack                 The pointer to the DHCPv6 ack packet.
757   @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.
758 
759   @retval     EFI_SUCCESS                Cache and parse the packet successfully.
760   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
761 
762 **/
763 EFI_STATUS
PxeBcCopyDhcp6Ack(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP6_PACKET * Ack,IN BOOLEAN Verified)764 PxeBcCopyDhcp6Ack (
765   IN PXEBC_PRIVATE_DATA   *Private,
766   IN EFI_DHCP6_PACKET     *Ack,
767   IN BOOLEAN              Verified
768   )
769 {
770   EFI_PXE_BASE_CODE_MODE  *Mode;
771   EFI_STATUS              Status;
772 
773   Mode = Private->PxeBc.Mode;
774 
775   Status = PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);
776   if (EFI_ERROR (Status)) {
777     return Status;
778   }
779 
780   if (Verified) {
781     //
782     // Parse the ack packet and store it into mode data if needed.
783     //
784     PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);
785     CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);
786     Mode->DhcpAckReceived = TRUE;
787   }
788 
789   return EFI_SUCCESS;
790 }
791 
792 
793 /**
794   Cache the DHCPv6 proxy offer packet according to the received order.
795 
796   @param[in]  Private               The pointer to PxeBc private data.
797   @param[in]  OfferIndex            The received order of offer packets.
798 
799   @retval     EFI_SUCCESS                Cache and parse the packet successfully.
800   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
801 
802 **/
803 EFI_STATUS
PxeBcCopyDhcp6Proxy(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 OfferIndex)804 PxeBcCopyDhcp6Proxy (
805   IN PXEBC_PRIVATE_DATA     *Private,
806   IN UINT32                 OfferIndex
807   )
808 {
809   EFI_PXE_BASE_CODE_MODE    *Mode;
810   EFI_DHCP6_PACKET          *Offer;
811   EFI_STATUS              Status;
812 
813   ASSERT (OfferIndex < Private->OfferNum);
814   ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
815 
816   Mode  = Private->PxeBc.Mode;
817   Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;
818 
819   //
820   // Cache the proxy offer packet and parse it.
821   //
822   Status = PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);
823   if (EFI_ERROR(Status)) {
824     return Status;
825   }
826   PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);
827 
828   //
829   // Store this packet into mode data.
830   //
831   CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);
832   Mode->ProxyOfferReceived = TRUE;
833 
834   return EFI_SUCCESS;
835 }
836 
837 /**
838   Seek the address of the first byte of the option header.
839 
840   @param[in]  Buf           The pointer to the buffer.
841   @param[in]  SeekLen       The length to seek.
842   @param[in]  OptType       The option type.
843 
844   @retval     NULL          If it failed to seek the option.
845   @retval     others        The position to the option.
846 
847 **/
848 UINT8 *
PxeBcDhcp6SeekOption(IN UINT8 * Buf,IN UINT32 SeekLen,IN UINT16 OptType)849 PxeBcDhcp6SeekOption (
850   IN UINT8           *Buf,
851   IN UINT32          SeekLen,
852   IN UINT16          OptType
853   )
854 {
855   UINT8              *Cursor;
856   UINT8              *Option;
857   UINT16             DataLen;
858   UINT16             OpCode;
859 
860   Option = NULL;
861   Cursor = Buf;
862 
863   while (Cursor < Buf + SeekLen) {
864     OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
865     if (OpCode == HTONS (OptType)) {
866       Option = Cursor;
867       break;
868     }
869     DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
870     Cursor += (DataLen + 4);
871   }
872 
873   return Option;
874 }
875 
876 
877 /**
878   Build and send out the request packet for the bootfile, and parse the reply.
879 
880   @param[in]  Private               The pointer to PxeBc private data.
881   @param[in]  Index                 PxeBc option boot item type.
882 
883   @retval     EFI_SUCCESS           Successfully discovered the boot file.
884   @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
885   @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
886   @retval     Others                Failed to discover the boot file.
887 
888 **/
889 EFI_STATUS
PxeBcRequestBootService(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 Index)890 PxeBcRequestBootService (
891   IN  PXEBC_PRIVATE_DATA              *Private,
892   IN  UINT32                          Index
893   )
894 {
895   EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;
896   EFI_PXE_BASE_CODE_UDP_PORT          DestPort;
897   EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
898   EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;
899   UINTN                               DiscoverLen;
900   EFI_DHCP6_PACKET                    *Request;
901   UINTN                               RequestLen;
902   EFI_DHCP6_PACKET                    *Reply;
903   UINT8                               *RequestOpt;
904   UINT8                               *DiscoverOpt;
905   UINTN                               ReadSize;
906   UINT16                              OpFlags;
907   UINT16                              OpCode;
908   UINT16                              OpLen;
909   EFI_STATUS                          Status;
910   EFI_DHCP6_PACKET                    *ProxyOffer;
911   UINT8                               *Option;
912 
913   PxeBc       = &Private->PxeBc;
914   Request     = Private->Dhcp6Request;
915   ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer;
916   SrcPort     = PXEBC_BS_DISCOVER_PORT;
917   DestPort    = PXEBC_BS_DISCOVER_PORT;
918   OpFlags     = 0;
919 
920   if (Request == NULL) {
921     return EFI_DEVICE_ERROR;
922   }
923 
924   Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
925   if (Discover == NULL) {
926     return EFI_OUT_OF_RESOURCES;
927   }
928 
929   //
930   // Build the request packet by the cached request packet before.
931   //
932   Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId;
933   Discover->MessageType   = Request->Dhcp6.Header.MessageType;
934   RequestOpt              = Request->Dhcp6.Option;
935   DiscoverOpt             = Discover->DhcpOptions;
936   DiscoverLen             = sizeof (EFI_DHCP6_HEADER);
937   RequestLen              = DiscoverLen;
938 
939   //
940   // Find Server ID Option from ProxyOffer.
941   //
942   Option = PxeBcDhcp6SeekOption (
943              ProxyOffer->Dhcp6.Option,
944              ProxyOffer->Length - 4,
945              DHCP6_OPT_SERVER_ID
946              );
947   if (Option == NULL) {
948     return EFI_NOT_FOUND;
949   }
950 
951   //
952   // Add Server ID Option.
953   //
954   OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen);
955   CopyMem (DiscoverOpt, Option, OpLen + 4);
956   DiscoverOpt += (OpLen + 4);
957   DiscoverLen += (OpLen + 4);
958 
959   while (RequestLen < Request->Length) {
960     OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);
961     OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);
962     if (OpCode != EFI_DHCP6_IA_TYPE_NA &&
963         OpCode != EFI_DHCP6_IA_TYPE_TA &&
964         OpCode != DHCP6_OPT_SERVER_ID
965         ) {
966       //
967       // Copy all the options except IA option and Server ID
968       //
969       CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
970       DiscoverOpt += (OpLen + 4);
971       DiscoverLen += (OpLen + 4);
972     }
973     RequestOpt += (OpLen + 4);
974     RequestLen += (OpLen + 4);
975   }
976 
977   //
978   // Update Elapsed option in the package
979   //
980   Option = PxeBcDhcp6SeekOption (
981              Discover->DhcpOptions,
982              (UINT32)(RequestLen - 4),
983              DHCP6_OPT_ELAPSED_TIME
984              );
985   if (Option != NULL) {
986     CalcElapsedTime (Private);
987     WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime));
988   }
989 
990   Status = PxeBc->UdpWrite (
991                     PxeBc,
992                     OpFlags,
993                     &Private->ServerIp,
994                     &DestPort,
995                     NULL,
996                     &Private->StationIp,
997                     &SrcPort,
998                     NULL,
999                     NULL,
1000                     &DiscoverLen,
1001                     (VOID *) Discover
1002                     );
1003 
1004   if (EFI_ERROR (Status)) {
1005     return Status;
1006   }
1007 
1008   //
1009   // Cache the right PXE reply packet here, set valid flag later.
1010   // Especially for PXE discover packet, store it into mode data here.
1011   //
1012   Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
1013   ReadSize = (UINTN) Reply->Size;
1014 
1015   //
1016   // Start Udp6Read instance
1017   //
1018   Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
1019   if (EFI_ERROR (Status)) {
1020     return Status;
1021   }
1022 
1023   Status = PxeBc->UdpRead (
1024                     PxeBc,
1025                     EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
1026                     NULL,
1027                     &SrcPort,
1028                     &Private->ServerIp,
1029                     &DestPort,
1030                     NULL,
1031                     NULL,
1032                     &ReadSize,
1033                     (VOID *) &Reply->Dhcp6
1034                     );
1035   //
1036   // Stop Udp6Read instance
1037   //
1038   Private->Udp6Read->Configure (Private->Udp6Read, NULL);
1039 
1040   if (EFI_ERROR (Status)) {
1041     return Status;
1042   }
1043 
1044   //
1045   // Update length
1046   //
1047   Reply->Length = (UINT32) ReadSize;
1048 
1049   return EFI_SUCCESS;
1050 }
1051 
1052 
1053 /**
1054   Retry to request bootfile name by the BINL offer.
1055 
1056   @param[in]  Private              The pointer to PxeBc private data.
1057   @param[in]  Index                The received order of offer packets.
1058 
1059   @retval     EFI_SUCCESS          Successfully retried a request for the bootfile name.
1060   @retval     EFI_DEVICE_ERROR     Failed to retry the bootfile name.
1061 
1062 **/
1063 EFI_STATUS
PxeBcRetryDhcp6Binl(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 Index)1064 PxeBcRetryDhcp6Binl (
1065   IN PXEBC_PRIVATE_DATA  *Private,
1066   IN UINT32              Index
1067   )
1068 {
1069   EFI_PXE_BASE_CODE_MODE    *Mode;
1070   PXEBC_DHCP6_PACKET_CACHE  *Offer;
1071   PXEBC_DHCP6_PACKET_CACHE  *Cache6;
1072   EFI_STATUS                Status;
1073 
1074   ASSERT (Index < PXEBC_OFFER_MAX_NUM);
1075   ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||
1076           Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);
1077 
1078   Mode                  = Private->PxeBc.Mode;
1079   Private->IsDoDiscover = FALSE;
1080   Offer                 = &Private->OfferBuffer[Index].Dhcp6;
1081   if (Offer->OfferType == PxeOfferTypeDhcpBinl) {
1082     //
1083     // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.
1084     //
1085     CopyMem (
1086       &Private->ServerIp.v6,
1087       &mAllDhcpRelayAndServersAddress,
1088       sizeof (EFI_IPv6_ADDRESS)
1089       );
1090   } else {
1091     ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
1092     //
1093     // Parse out the next server address from the last offer, and store it
1094     //
1095     Status = PxeBcExtractBootFileUrl (
1096                Private,
1097                &Private->BootFileName,
1098                &Private->ServerIp.v6,
1099                (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
1100                NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
1101                );
1102     if (EFI_ERROR (Status)) {
1103       return Status;
1104     }
1105   }
1106 
1107   //
1108   // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
1109   //
1110   Status = PxeBcRequestBootService (Private, Index);
1111 
1112   if (EFI_ERROR (Status)) {
1113     return Status;
1114   }
1115 
1116   Cache6 = &Private->ProxyOffer.Dhcp6;
1117   Status = PxeBcParseDhcp6Packet (Cache6);
1118   if (EFI_ERROR (Status)) {
1119     return Status;
1120   }
1121 
1122   if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&
1123       Cache6->OfferType != PxeOfferTypeProxyWfm11a &&
1124       Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
1125     //
1126     // This BINL ack doesn't have discovery option set or multicast option set
1127     // or bootfile name specified.
1128     //
1129     return EFI_DEVICE_ERROR;
1130   }
1131 
1132   Mode->ProxyOfferReceived = TRUE;
1133   CopyMem (
1134     &Mode->ProxyOffer.Dhcpv6,
1135     &Cache6->Packet.Offer.Dhcp6,
1136     Cache6->Packet.Offer.Length
1137     );
1138 
1139   return EFI_SUCCESS;
1140 }
1141 
1142 
1143 /**
1144   Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
1145 
1146   @param[in]  Private               The pointer to PXEBC_PRIVATE_DATA.
1147   @param[in]  RcvdOffer             The pointer to the received offer packet.
1148 
1149   @retval     EFI_SUCCESS      Cache and parse the packet successfully.
1150   @retval     Others           Operation failed.
1151 **/
1152 EFI_STATUS
PxeBcCacheDhcp6Offer(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP6_PACKET * RcvdOffer)1153 PxeBcCacheDhcp6Offer (
1154   IN PXEBC_PRIVATE_DATA     *Private,
1155   IN EFI_DHCP6_PACKET       *RcvdOffer
1156   )
1157 {
1158   PXEBC_DHCP6_PACKET_CACHE  *Cache6;
1159   EFI_DHCP6_PACKET          *Offer;
1160   PXEBC_OFFER_TYPE          OfferType;
1161   EFI_STATUS                Status;
1162 
1163   Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
1164   Offer  = &Cache6->Packet.Offer;
1165 
1166   //
1167   // Cache the content of DHCPv6 packet firstly.
1168   //
1169   Status = PxeBcCacheDhcp6Packet (Offer, RcvdOffer);
1170   if (EFI_ERROR (Status)) {
1171     return Status;
1172   }
1173 
1174   //
1175   // Validate the DHCPv6 packet, and parse the options and offer type.
1176   //
1177   if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {
1178     return EFI_ABORTED;
1179   }
1180 
1181   //
1182   // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
1183   //
1184   OfferType = Cache6->OfferType;
1185   ASSERT (OfferType < PxeOfferTypeMax);
1186   ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
1187 
1188   if (IS_PROXY_OFFER (OfferType)) {
1189     //
1190     // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
1191     //
1192     Private->IsProxyRecved = TRUE;
1193 
1194     if (OfferType == PxeOfferTypeProxyBinl) {
1195       //
1196       // Cache all proxy BINL offers.
1197       //
1198       Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
1199       Private->OfferCount[OfferType]++;
1200     } else if (Private->OfferCount[OfferType] > 0) {
1201       //
1202       // Only cache the first PXE10/WFM11a offer, and discard the others.
1203       //
1204       Private->OfferIndex[OfferType][0] = Private->OfferNum;
1205       Private->OfferCount[OfferType]    = 1;
1206     } else {
1207       return EFI_ABORTED;
1208     }
1209   } else {
1210     //
1211     // It's a DHCPv6 offer with yiaddr, and cache them all.
1212     //
1213     Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
1214     Private->OfferCount[OfferType]++;
1215   }
1216 
1217   Private->OfferNum++;
1218 
1219   return EFI_SUCCESS;
1220 }
1221 
1222 
1223 /**
1224   Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
1225 
1226   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
1227 
1228 **/
1229 VOID
PxeBcSelectDhcp6Offer(IN PXEBC_PRIVATE_DATA * Private)1230 PxeBcSelectDhcp6Offer (
1231   IN PXEBC_PRIVATE_DATA     *Private
1232   )
1233 {
1234   UINT32                Index;
1235   UINT32                OfferIndex;
1236   PXEBC_OFFER_TYPE      OfferType;
1237 
1238   Private->SelectIndex = 0;
1239 
1240   if (Private->IsOfferSorted) {
1241     //
1242     // Select offer by default policy.
1243     //
1244     if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
1245       //
1246       // 1. DhcpPxe10 offer
1247       //
1248       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
1249 
1250     } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
1251       //
1252       // 2. DhcpWfm11a offer
1253       //
1254       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
1255 
1256     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
1257                Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {
1258       //
1259       // 3. DhcpOnly offer and ProxyPxe10 offer.
1260       //
1261       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1262       Private->SelectProxyType = PxeOfferTypeProxyPxe10;
1263 
1264     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
1265                Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {
1266       //
1267       // 4. DhcpOnly offer and ProxyWfm11a offer.
1268       //
1269       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1270       Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
1271 
1272     } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
1273       //
1274       // 5. DhcpBinl offer.
1275       //
1276       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
1277 
1278     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
1279                Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {
1280       //
1281       // 6. DhcpOnly offer and ProxyBinl offer.
1282       //
1283       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1284       Private->SelectProxyType = PxeOfferTypeProxyBinl;
1285 
1286     } else {
1287       //
1288       // 7. DhcpOnly offer with bootfilename.
1289       //
1290       for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {
1291         OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];
1292         if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {
1293           Private->SelectIndex = OfferIndex + 1;
1294           break;
1295         }
1296       }
1297     }
1298   } else {
1299     //
1300     // Select offer by received order.
1301     //
1302     for (Index = 0; Index < Private->OfferNum; Index++) {
1303 
1304       OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
1305 
1306       if (IS_PROXY_OFFER (OfferType)) {
1307         //
1308         // Skip proxy offers
1309         //
1310         continue;
1311       }
1312 
1313       if (!Private->IsProxyRecved &&
1314           OfferType == PxeOfferTypeDhcpOnly &&
1315           Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
1316         //
1317         // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
1318         //
1319         continue;
1320       }
1321 
1322       Private->SelectIndex = Index + 1;
1323       break;
1324     }
1325   }
1326 }
1327 
1328 
1329 /**
1330   Handle the DHCPv6 offer packet.
1331 
1332   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
1333 
1334   @retval     EFI_SUCCESS           Handled the DHCPv6 offer packet successfully.
1335   @retval     EFI_NO_RESPONSE       No response to the following request packet.
1336   @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
1337   @retval     EFI_BUFFER_TOO_SMALL  Can't cache the offer pacet.
1338 
1339 **/
1340 EFI_STATUS
PxeBcHandleDhcp6Offer(IN PXEBC_PRIVATE_DATA * Private)1341 PxeBcHandleDhcp6Offer (
1342   IN PXEBC_PRIVATE_DATA            *Private
1343   )
1344 {
1345   PXEBC_DHCP6_PACKET_CACHE         *Cache6;
1346   EFI_STATUS                       Status;
1347   PXEBC_OFFER_TYPE                 OfferType;
1348   UINT32                           ProxyIndex;
1349   UINT32                           SelectIndex;
1350   UINT32                           Index;
1351 
1352   ASSERT (Private->SelectIndex > 0);
1353   SelectIndex = (UINT32) (Private->SelectIndex - 1);
1354   ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);
1355   Cache6      = &Private->OfferBuffer[SelectIndex].Dhcp6;
1356   Status      = EFI_SUCCESS;
1357 
1358   //
1359   // First try to cache DNS server address if DHCP6 offer provides.
1360   //
1361   if (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER] != NULL) {
1362     Private->DnsServer = AllocateZeroPool (NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->OpLen));
1363     if (Private->DnsServer == NULL) {
1364       return EFI_OUT_OF_RESOURCES;
1365     }
1366     CopyMem (Private->DnsServer, Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->Data, sizeof (EFI_IPv6_ADDRESS));
1367   }
1368 
1369   if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {
1370     //
1371     // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
1372     //
1373     if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {
1374       Status = EFI_NO_RESPONSE;
1375     }
1376   } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {
1377 
1378     if (Private->IsProxyRecved) {
1379       //
1380       // DhcpOnly offer is selected, so need try to request bootfilename.
1381       //
1382       ProxyIndex = 0;
1383       if (Private->IsOfferSorted) {
1384         //
1385         // The proxy offer should be determined if select by default policy.
1386         // IsOfferSorted means all offers are labeled by OfferIndex.
1387         //
1388         ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
1389 
1390         if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
1391           //
1392           // Try all the cached ProxyBinl offer one by one to request bootfilename.
1393           //
1394           for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
1395 
1396             ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
1397             if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {
1398               break;
1399             }
1400           }
1401           if (Index == Private->OfferCount[Private->SelectProxyType]) {
1402             Status = EFI_NO_RESPONSE;
1403           }
1404         } else {
1405           //
1406           // For other proxy offers (pxe10 or wfm11a), only one is buffered.
1407           //
1408           ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
1409         }
1410       } else {
1411         //
1412         // The proxy offer should not be determined if select by received order.
1413         //
1414         Status = EFI_NO_RESPONSE;
1415 
1416         for (Index = 0; Index < Private->OfferNum; Index++) {
1417 
1418           OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
1419 
1420           if (!IS_PROXY_OFFER (OfferType)) {
1421             //
1422             // Skip non proxy dhcp offers.
1423             //
1424             continue;
1425           }
1426 
1427           if (OfferType == PxeOfferTypeProxyBinl) {
1428             //
1429             // Try all the cached ProxyBinl offer one by one to request bootfilename.
1430             //
1431             if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {
1432               continue;
1433             }
1434           }
1435 
1436           Private->SelectProxyType = OfferType;
1437           ProxyIndex               = Index;
1438           Status                   = EFI_SUCCESS;
1439           break;
1440         }
1441       }
1442 
1443       if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {
1444         //
1445         // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
1446         //
1447         Status = PxeBcCopyDhcp6Proxy (Private, ProxyIndex);
1448       }
1449     } else {
1450       //
1451       //  Othewise, the bootfilename must be included in DhcpOnly offer.
1452       //
1453       ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
1454     }
1455   }
1456 
1457   if (!EFI_ERROR (Status)) {
1458     //
1459     // All PXE boot information is ready by now.
1460     //
1461     Status = PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);
1462     Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;
1463   }
1464 
1465   return Status;
1466 }
1467 
1468 
1469 /**
1470   Unregister the address by Ip6Config protocol.
1471 
1472   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
1473 
1474 **/
1475 VOID
PxeBcUnregisterIp6Address(IN PXEBC_PRIVATE_DATA * Private)1476 PxeBcUnregisterIp6Address (
1477   IN PXEBC_PRIVATE_DATA           *Private
1478   )
1479 {
1480   if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {
1481     //
1482     // PXE driver change the policy of IP6 driver, it's a chance to recover.
1483     // Keep the point and there is no enough requirements to do recovery.
1484     //
1485   }
1486 }
1487 
1488 /**
1489   Check whether IP driver could route the message which will be sent to ServerIp address.
1490 
1491   This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid
1492   route is found in IP6 route table, the address will be filed in GatewayAddr and return.
1493 
1494   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
1495   @param[in]  TimeOutInSecond     Timeout value in seconds.
1496   @param[out] GatewayAddr         Pointer to store the gateway IP address.
1497 
1498   @retval     EFI_SUCCESS         Found a valid gateway address successfully.
1499   @retval     EFI_TIMEOUT         The operation is time out.
1500   @retval     Other               Unexpect error happened.
1501 
1502 **/
1503 EFI_STATUS
PxeBcCheckRouteTable(IN PXEBC_PRIVATE_DATA * Private,IN UINTN TimeOutInSecond,OUT EFI_IPv6_ADDRESS * GatewayAddr)1504 PxeBcCheckRouteTable (
1505   IN  PXEBC_PRIVATE_DATA            *Private,
1506   IN  UINTN                         TimeOutInSecond,
1507   OUT EFI_IPv6_ADDRESS              *GatewayAddr
1508   )
1509 {
1510   EFI_STATUS                       Status;
1511   EFI_IP6_PROTOCOL                 *Ip6;
1512   EFI_IP6_MODE_DATA                Ip6ModeData;
1513   UINTN                            Index;
1514   EFI_EVENT                        TimeOutEvt;
1515   UINTN                            RetryCount;
1516   BOOLEAN                          GatewayIsFound;
1517 
1518   ASSERT (GatewayAddr != NULL);
1519   ASSERT (Private != NULL);
1520 
1521   Ip6            = Private->Ip6;
1522   GatewayIsFound = FALSE;
1523   RetryCount     = 0;
1524   TimeOutEvt     = NULL;
1525   ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));
1526 
1527   while (TRUE) {
1528     Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);
1529     if (EFI_ERROR (Status)) {
1530       goto ON_EXIT;
1531     }
1532 
1533     //
1534     // Find out the gateway address which can route the message which send to ServerIp.
1535     //
1536     for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {
1537       if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {
1538         IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);
1539         GatewayIsFound = TRUE;
1540         break;
1541       }
1542     }
1543 
1544     if (Ip6ModeData.AddressList != NULL) {
1545       FreePool (Ip6ModeData.AddressList);
1546     }
1547     if (Ip6ModeData.GroupTable != NULL) {
1548       FreePool (Ip6ModeData.GroupTable);
1549     }
1550     if (Ip6ModeData.RouteTable != NULL) {
1551       FreePool (Ip6ModeData.RouteTable);
1552     }
1553     if (Ip6ModeData.NeighborCache != NULL) {
1554       FreePool (Ip6ModeData.NeighborCache);
1555     }
1556     if (Ip6ModeData.PrefixTable != NULL) {
1557       FreePool (Ip6ModeData.PrefixTable);
1558     }
1559     if (Ip6ModeData.IcmpTypeList != NULL) {
1560       FreePool (Ip6ModeData.IcmpTypeList);
1561     }
1562 
1563     if (GatewayIsFound || RetryCount == TimeOutInSecond) {
1564       break;
1565     }
1566 
1567     RetryCount++;
1568 
1569     //
1570     // Delay 1 second then recheck it again.
1571     //
1572     if (TimeOutEvt == NULL) {
1573       Status = gBS->CreateEvent (
1574                       EVT_TIMER,
1575                       TPL_CALLBACK,
1576                       NULL,
1577                       NULL,
1578                       &TimeOutEvt
1579                       );
1580       if (EFI_ERROR (Status)) {
1581         goto ON_EXIT;
1582       }
1583     }
1584 
1585     Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);
1586     if (EFI_ERROR (Status)) {
1587       goto ON_EXIT;
1588     }
1589     while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
1590       Ip6->Poll (Ip6);
1591     }
1592   }
1593 
1594 ON_EXIT:
1595   if (TimeOutEvt != NULL) {
1596     gBS->CloseEvent (TimeOutEvt);
1597   }
1598 
1599   if (GatewayIsFound) {
1600     Status = EFI_SUCCESS;
1601   } else if (RetryCount == TimeOutInSecond) {
1602     Status = EFI_TIMEOUT;
1603   }
1604 
1605   return Status;
1606 }
1607 
1608 /**
1609   Register the ready station address and gateway by Ip6Config protocol.
1610 
1611   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
1612   @param[in]  Address             The pointer to the ready address.
1613 
1614   @retval     EFI_SUCCESS         Registered the address succesfully.
1615   @retval     Others              Failed to register the address.
1616 
1617 **/
1618 EFI_STATUS
PxeBcRegisterIp6Address(IN PXEBC_PRIVATE_DATA * Private,IN EFI_IPv6_ADDRESS * Address)1619 PxeBcRegisterIp6Address (
1620   IN PXEBC_PRIVATE_DATA            *Private,
1621   IN EFI_IPv6_ADDRESS              *Address
1622   )
1623 {
1624   EFI_IP6_PROTOCOL                 *Ip6;
1625   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
1626   EFI_IP6_CONFIG_POLICY            Policy;
1627   EFI_IP6_CONFIG_MANUAL_ADDRESS    CfgAddr;
1628   EFI_IPv6_ADDRESS                 GatewayAddr;
1629   UINTN                            DataSize;
1630   EFI_EVENT                        MappedEvt;
1631   EFI_STATUS                       Status;
1632   BOOLEAN                          NoGateway;
1633   EFI_IPv6_ADDRESS                 *Ip6Addr;
1634   UINTN                            Index;
1635 
1636   Status     = EFI_SUCCESS;
1637   MappedEvt  = NULL;
1638   Ip6Addr    = NULL;
1639   DataSize   = sizeof (EFI_IP6_CONFIG_POLICY);
1640   Ip6Cfg     = Private->Ip6Cfg;
1641   Ip6        = Private->Ip6;
1642   NoGateway  = FALSE;
1643 
1644   ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
1645   CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));
1646 
1647   Status = Ip6->Configure (Ip6, &Private->Ip6CfgData);
1648   if (EFI_ERROR (Status)) {
1649     goto ON_EXIT;
1650   }
1651 
1652   //
1653   // Retrieve the gateway address from IP6 route table.
1654   //
1655   Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);
1656   if (EFI_ERROR (Status)) {
1657     NoGateway = TRUE;
1658   }
1659 
1660   //
1661   // There is no channel between IP6 and PXE driver about address setting,
1662   // so it has to set the new address by Ip6ConfigProtocol manually.
1663   //
1664   Policy = Ip6ConfigPolicyManual;
1665   Status = Ip6Cfg->SetData (
1666                      Ip6Cfg,
1667                      Ip6ConfigDataTypePolicy,
1668                      sizeof(EFI_IP6_CONFIG_POLICY),
1669                      &Policy
1670                      );
1671   if (EFI_ERROR (Status)) {
1672     //
1673     // There is no need to recover later.
1674     //
1675     Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
1676     goto ON_EXIT;
1677   }
1678 
1679   //
1680   // Create a notify event to set address flag when DAD if IP6 driver succeeded.
1681   //
1682   Status = gBS->CreateEvent (
1683                   EVT_NOTIFY_SIGNAL,
1684                   TPL_NOTIFY,
1685                   PxeBcCommonNotify,
1686                   &Private->IsAddressOk,
1687                   &MappedEvt
1688                   );
1689   if (EFI_ERROR (Status)) {
1690     goto ON_EXIT;
1691   }
1692 
1693   Private->IsAddressOk = FALSE;
1694   Status = Ip6Cfg->RegisterDataNotify (
1695                      Ip6Cfg,
1696                      Ip6ConfigDataTypeManualAddress,
1697                      MappedEvt
1698                      );
1699   if (EFI_ERROR(Status)) {
1700     goto ON_EXIT;
1701   }
1702 
1703   Status = Ip6Cfg->SetData (
1704                      Ip6Cfg,
1705                      Ip6ConfigDataTypeManualAddress,
1706                      sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),
1707                      &CfgAddr
1708                      );
1709   if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {
1710     goto ON_EXIT;
1711   } else if (Status == EFI_NOT_READY) {
1712     //
1713     // Poll the network until the asynchronous process is finished.
1714     //
1715     while (!Private->IsAddressOk) {
1716       Ip6->Poll (Ip6);
1717     }
1718     //
1719     // Check whether the IP6 address setting is successed.
1720     //
1721     DataSize = 0;
1722     Status = Ip6Cfg->GetData (
1723                        Ip6Cfg,
1724                        Ip6ConfigDataTypeManualAddress,
1725                        &DataSize,
1726                        NULL
1727                        );
1728     if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {
1729       Status = EFI_DEVICE_ERROR;
1730       goto ON_EXIT;
1731     }
1732 
1733     Ip6Addr = AllocatePool (DataSize);
1734     if (Ip6Addr == NULL) {
1735       return EFI_OUT_OF_RESOURCES;
1736     }
1737     Status = Ip6Cfg->GetData (
1738                        Ip6Cfg,
1739                        Ip6ConfigDataTypeManualAddress,
1740                        &DataSize,
1741                        (VOID*) Ip6Addr
1742                        );
1743     if (EFI_ERROR (Status)) {
1744       Status = EFI_DEVICE_ERROR;
1745       goto ON_EXIT;
1746     }
1747 
1748     for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) {
1749       if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) {
1750         break;
1751       }
1752     }
1753     if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {
1754       Status = EFI_ABORTED;
1755       goto ON_EXIT;
1756     }
1757   }
1758 
1759   //
1760   // Set the default gateway address back if needed.
1761   //
1762   if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) {
1763     Status = Ip6Cfg->SetData (
1764                        Ip6Cfg,
1765                        Ip6ConfigDataTypeGateway,
1766                        sizeof (EFI_IPv6_ADDRESS),
1767                        &GatewayAddr
1768                        );
1769     if (EFI_ERROR (Status)) {
1770       goto ON_EXIT;
1771     }
1772   }
1773 
1774 ON_EXIT:
1775   if (MappedEvt != NULL) {
1776     Ip6Cfg->UnregisterDataNotify (
1777               Ip6Cfg,
1778               Ip6ConfigDataTypeManualAddress,
1779               MappedEvt
1780               );
1781     gBS->CloseEvent (MappedEvt);
1782   }
1783   if (Ip6Addr != NULL) {
1784     FreePool (Ip6Addr);
1785   }
1786   return Status;
1787 }
1788 
1789 /**
1790   Set the IP6 policy to Automatic.
1791 
1792   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
1793 
1794   @retval     EFI_SUCCESS         Switch the IP policy succesfully.
1795   @retval     Others              Unexpect error happened.
1796 
1797 **/
1798 EFI_STATUS
PxeBcSetIp6Policy(IN PXEBC_PRIVATE_DATA * Private)1799 PxeBcSetIp6Policy (
1800   IN PXEBC_PRIVATE_DATA            *Private
1801   )
1802 {
1803   EFI_IP6_CONFIG_POLICY            Policy;
1804   EFI_STATUS                       Status;
1805   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
1806   UINTN                            DataSize;
1807 
1808   Ip6Cfg      = Private->Ip6Cfg;
1809   DataSize    = sizeof (EFI_IP6_CONFIG_POLICY);
1810 
1811   //
1812   // Get and store the current policy of IP6 driver.
1813   //
1814   Status = Ip6Cfg->GetData (
1815                      Ip6Cfg,
1816                      Ip6ConfigDataTypePolicy,
1817                      &DataSize,
1818                      &Private->Ip6Policy
1819                      );
1820   if (EFI_ERROR (Status)) {
1821     return Status;
1822   }
1823 
1824   if (Private->Ip6Policy == Ip6ConfigPolicyManual) {
1825     Policy = Ip6ConfigPolicyAutomatic;
1826     Status = Ip6Cfg->SetData (
1827                        Ip6Cfg,
1828                        Ip6ConfigDataTypePolicy,
1829                        sizeof(EFI_IP6_CONFIG_POLICY),
1830                        &Policy
1831                        );
1832     if (EFI_ERROR (Status)) {
1833       //
1834       // There is no need to recover later.
1835       //
1836       Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
1837     }
1838   }
1839 
1840   return Status;
1841 }
1842 
1843 /**
1844   This function will register the station IP address and flush IP instance to start using the new IP address.
1845 
1846   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
1847 
1848   @retval     EFI_SUCCESS         The new IP address has been configured successfully.
1849   @retval     Others              Failed to configure the address.
1850 
1851 **/
1852 EFI_STATUS
PxeBcSetIp6Address(IN PXEBC_PRIVATE_DATA * Private)1853 PxeBcSetIp6Address (
1854   IN  PXEBC_PRIVATE_DATA              *Private
1855   )
1856 {
1857   EFI_STATUS                  Status;
1858   EFI_DHCP6_PROTOCOL          *Dhcp6;
1859 
1860   Dhcp6 = Private->Dhcp6;
1861 
1862   CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS));
1863   CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
1864 
1865   Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);
1866   if (EFI_ERROR (Status)) {
1867     Dhcp6->Stop (Dhcp6);
1868     return Status;
1869   }
1870 
1871   Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL);
1872   if (EFI_ERROR (Status)) {
1873     PxeBcUnregisterIp6Address (Private);
1874     Dhcp6->Stop (Dhcp6);
1875     return Status;
1876   }
1877 
1878   AsciiPrint ("\n  Station IP address is ");
1879   PxeBcShowIp6Addr (&Private->StationIp.v6);
1880 
1881   return EFI_SUCCESS;
1882 }
1883 
1884 /**
1885   EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
1886   to intercept events that occurred in the configuration process.
1887 
1888   @param[in]  This              The pointer to the EFI DHCPv6 Protocol.
1889   @param[in]  Context           The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
1890   @param[in]  CurrentState      The current operational state of the EFI DHCPv Protocol driver.
1891   @param[in]  Dhcp6Event        The event that occurs in the current state, which usually means a
1892                                 state transition.
1893   @param[in]  Packet            The DHCPv6 packet that is going to be sent or was already received.
1894   @param[out] NewPacket         The packet that is used to replace the Packet above.
1895 
1896   @retval EFI_SUCCESS           Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
1897   @retval EFI_NOT_READY         Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
1898                                 driver will continue to wait for more packets.
1899   @retval EFI_ABORTED           Told the EFI DHCPv6 Protocol driver to abort the current process.
1900 
1901 **/
1902 EFI_STATUS
1903 EFIAPI
PxeBcDhcp6CallBack(IN EFI_DHCP6_PROTOCOL * This,IN VOID * Context,IN EFI_DHCP6_STATE CurrentState,IN EFI_DHCP6_EVENT Dhcp6Event,IN EFI_DHCP6_PACKET * Packet,OUT EFI_DHCP6_PACKET ** NewPacket OPTIONAL)1904 PxeBcDhcp6CallBack (
1905   IN  EFI_DHCP6_PROTOCOL           *This,
1906   IN  VOID                         *Context,
1907   IN  EFI_DHCP6_STATE              CurrentState,
1908   IN  EFI_DHCP6_EVENT              Dhcp6Event,
1909   IN  EFI_DHCP6_PACKET             *Packet,
1910   OUT EFI_DHCP6_PACKET             **NewPacket     OPTIONAL
1911   )
1912 {
1913   PXEBC_PRIVATE_DATA                  *Private;
1914   EFI_PXE_BASE_CODE_MODE              *Mode;
1915   EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
1916   EFI_DHCP6_PACKET                    *SelectAd;
1917   EFI_STATUS                          Status;
1918   BOOLEAN                             Received;
1919 
1920   if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&
1921       (Dhcp6Event != Dhcp6SelectAdvertise) &&
1922       (Dhcp6Event != Dhcp6SendSolicit) &&
1923       (Dhcp6Event != Dhcp6SendRequest) &&
1924       (Dhcp6Event != Dhcp6RcvdReply)) {
1925     return EFI_SUCCESS;
1926   }
1927 
1928   ASSERT (Packet != NULL);
1929 
1930   Private   = (PXEBC_PRIVATE_DATA *) Context;
1931   Mode      = Private->PxeBc.Mode;
1932   Callback  = Private->PxeBcCallback;
1933 
1934   //
1935   // Callback to user when any traffic ocurred if has.
1936   //
1937   if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {
1938     Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);
1939     Status = Callback->Callback (
1940                          Callback,
1941                          Private->Function,
1942                          Received,
1943                          Packet->Length,
1944                          (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6
1945                          );
1946     if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
1947       return EFI_ABORTED;
1948     }
1949   }
1950 
1951   Status = EFI_SUCCESS;
1952 
1953   switch (Dhcp6Event) {
1954 
1955   case Dhcp6SendSolicit:
1956     if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
1957       //
1958       // If the to be sent packet exceeds the maximum length, abort the DHCP process.
1959       //
1960       Status = EFI_ABORTED;
1961       break;
1962     }
1963 
1964     //
1965     // Record the first Solicate msg time
1966     //
1967     if (Private->SolicitTimes == 0) {
1968       CalcElapsedTime (Private);
1969       Private->SolicitTimes++;
1970     }
1971     //
1972     // Cache the dhcp discover packet to mode data directly.
1973     //
1974     CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);
1975     break;
1976 
1977   case Dhcp6RcvdAdvertise:
1978     Status = EFI_NOT_READY;
1979     if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
1980       //
1981       // Ignore the incoming packets which exceed the maximum length.
1982       //
1983       break;
1984     }
1985     if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
1986       //
1987       // Cache the dhcp offers to OfferBuffer[] for select later, and record
1988       // the OfferIndex and OfferCount.
1989       //
1990       PxeBcCacheDhcp6Offer (Private, Packet);
1991     }
1992     break;
1993 
1994   case Dhcp6SendRequest:
1995     if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
1996       //
1997       // If the to be sent packet exceeds the maximum length, abort the DHCP process.
1998       //
1999       Status = EFI_ABORTED;
2000       break;
2001     }
2002 
2003     //
2004     // Store the request packet as seed packet for discover.
2005     //
2006     if (Private->Dhcp6Request != NULL) {
2007       FreePool (Private->Dhcp6Request);
2008     }
2009     Private->Dhcp6Request = AllocateZeroPool (Packet->Size);
2010     if (Private->Dhcp6Request != NULL) {
2011       CopyMem (Private->Dhcp6Request, Packet, Packet->Size);
2012     }
2013     break;
2014 
2015   case Dhcp6SelectAdvertise:
2016     //
2017     // Select offer by the default policy or by order, and record the SelectIndex
2018     // and SelectProxyType.
2019     //
2020     PxeBcSelectDhcp6Offer (Private);
2021 
2022     if (Private->SelectIndex == 0) {
2023       Status = EFI_ABORTED;
2024     } else {
2025       ASSERT (NewPacket != NULL);
2026       SelectAd   = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;
2027       *NewPacket = AllocateZeroPool (SelectAd->Size);
2028       ASSERT (*NewPacket != NULL);
2029       CopyMem (*NewPacket, SelectAd, SelectAd->Size);
2030     }
2031     break;
2032 
2033   case Dhcp6RcvdReply:
2034     //
2035     // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
2036     // without verification.
2037     //
2038     ASSERT (Private->SelectIndex != 0);
2039     Status = PxeBcCopyDhcp6Ack (Private, Packet, FALSE);
2040     if (EFI_ERROR (Status)) {
2041       Status = EFI_ABORTED;
2042     }
2043     break;
2044 
2045   default:
2046     ASSERT (0);
2047   }
2048 
2049   return Status;
2050 }
2051 
2052 
2053 /**
2054   Build and send out the request packet for the bootfile, and parse the reply.
2055 
2056   @param[in]  Private               The pointer to PxeBc private data.
2057   @param[in]  Type                  PxeBc option boot item type.
2058   @param[in]  Layer                 The pointer to option boot item layer.
2059   @param[in]  UseBis                Use BIS or not.
2060   @param[in]  DestIp                The pointer to the server address.
2061 
2062   @retval     EFI_SUCCESS           Successfully discovered the boot file.
2063   @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
2064   @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
2065   @retval     Others                Failed to discover the boot file.
2066 
2067 **/
2068 EFI_STATUS
PxeBcDhcp6Discover(IN PXEBC_PRIVATE_DATA * Private,IN UINT16 Type,IN UINT16 * Layer,IN BOOLEAN UseBis,IN EFI_IP_ADDRESS * DestIp)2069 PxeBcDhcp6Discover (
2070   IN  PXEBC_PRIVATE_DATA              *Private,
2071   IN  UINT16                          Type,
2072   IN  UINT16                          *Layer,
2073   IN  BOOLEAN                         UseBis,
2074   IN  EFI_IP_ADDRESS                  *DestIp
2075   )
2076 {
2077   EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;
2078   EFI_PXE_BASE_CODE_UDP_PORT          DestPort;
2079   EFI_PXE_BASE_CODE_MODE              *Mode;
2080   EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
2081   EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;
2082   UINTN                               DiscoverLen;
2083   EFI_DHCP6_PACKET                    *Request;
2084   UINTN                               RequestLen;
2085   EFI_DHCP6_PACKET                    *Reply;
2086   UINT8                               *RequestOpt;
2087   UINT8                               *DiscoverOpt;
2088   UINTN                               ReadSize;
2089   UINT16                              OpCode;
2090   UINT16                              OpLen;
2091   UINT32                              Xid;
2092   EFI_STATUS                          Status;
2093 
2094   PxeBc       = &Private->PxeBc;
2095   Mode        = PxeBc->Mode;
2096   Request     = Private->Dhcp6Request;
2097   SrcPort     = PXEBC_BS_DISCOVER_PORT;
2098   DestPort    = PXEBC_BS_DISCOVER_PORT;
2099 
2100   if (!UseBis && Layer != NULL) {
2101     *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
2102   }
2103 
2104   if (Request == NULL) {
2105     return EFI_DEVICE_ERROR;
2106   }
2107 
2108   Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
2109   if (Discover == NULL) {
2110     return EFI_OUT_OF_RESOURCES;
2111   }
2112 
2113   //
2114   // Build the discover packet by the cached request packet before.
2115   //
2116   Xid                     = NET_RANDOM (NetRandomInitSeed ());
2117   Discover->TransactionId = HTONL (Xid);
2118   Discover->MessageType   = Request->Dhcp6.Header.MessageType;
2119   RequestOpt              = Request->Dhcp6.Option;
2120   DiscoverOpt             = Discover->DhcpOptions;
2121   DiscoverLen             = sizeof (EFI_DHCP6_HEADER);
2122   RequestLen              = DiscoverLen;
2123 
2124   while (RequestLen < Request->Length) {
2125     OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);
2126     OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);
2127     if (OpCode != EFI_DHCP6_IA_TYPE_NA &&
2128         OpCode != EFI_DHCP6_IA_TYPE_TA) {
2129       //
2130       // Copy all the options except IA option.
2131       //
2132       CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
2133       DiscoverOpt += (OpLen + 4);
2134       DiscoverLen += (OpLen + 4);
2135     }
2136     RequestOpt += (OpLen + 4);
2137     RequestLen += (OpLen + 4);
2138   }
2139 
2140   Status = PxeBc->UdpWrite (
2141                     PxeBc,
2142                     0,
2143                     &Private->ServerIp,
2144                     &DestPort,
2145                     NULL,
2146                     &Private->StationIp,
2147                     &SrcPort,
2148                     NULL,
2149                     NULL,
2150                     &DiscoverLen,
2151                     (VOID *) Discover
2152                     );
2153   if (EFI_ERROR (Status)) {
2154     return Status;
2155   }
2156 
2157   //
2158   // Cache the right PXE reply packet here, set valid flag later.
2159   // Especially for PXE discover packet, store it into mode data here.
2160   //
2161   if (Private->IsDoDiscover) {
2162     CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);
2163     Reply = &Private->PxeReply.Dhcp6.Packet.Ack;
2164   } else {
2165     Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
2166   }
2167   ReadSize = (UINTN) Reply->Size;
2168 
2169   //
2170   // Start Udp6Read instance
2171   //
2172   Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
2173   if (EFI_ERROR (Status)) {
2174     return Status;
2175   }
2176 
2177   Status = PxeBc->UdpRead (
2178                     PxeBc,
2179                     EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
2180                     NULL,
2181                     &SrcPort,
2182                     &Private->ServerIp,
2183                     &DestPort,
2184                     NULL,
2185                     NULL,
2186                     &ReadSize,
2187                     (VOID *) &Reply->Dhcp6
2188                     );
2189   //
2190   // Stop Udp6Read instance
2191   //
2192   Private->Udp6Read->Configure (Private->Udp6Read, NULL);
2193   if (EFI_ERROR (Status)) {
2194     return Status;
2195   }
2196 
2197   return EFI_SUCCESS;
2198 }
2199 
2200 
2201 /**
2202   Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
2203 
2204   @param[in]  Private           The pointer to PxeBc private data.
2205   @param[in]  Dhcp6             The pointer to the EFI_DHCP6_PROTOCOL
2206 
2207   @retval EFI_SUCCESS           The S.A.R.R. process successfully finished.
2208   @retval Others                Failed to finish the S.A.R.R. process.
2209 
2210 **/
2211 EFI_STATUS
PxeBcDhcp6Sarr(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP6_PROTOCOL * Dhcp6)2212 PxeBcDhcp6Sarr (
2213   IN PXEBC_PRIVATE_DATA            *Private,
2214   IN EFI_DHCP6_PROTOCOL            *Dhcp6
2215   )
2216 {
2217   EFI_PXE_BASE_CODE_MODE           *PxeMode;
2218   EFI_DHCP6_CONFIG_DATA            Config;
2219   EFI_DHCP6_MODE_DATA              Mode;
2220   EFI_DHCP6_RETRANSMISSION         *Retransmit;
2221   EFI_DHCP6_PACKET_OPTION          *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];
2222   UINT8                            Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];
2223   UINT32                           OptCount;
2224   EFI_STATUS                       Status;
2225   EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
2226   EFI_STATUS                       TimerStatus;
2227   EFI_EVENT                        Timer;
2228   UINT64                           GetMappingTimeOut;
2229   UINTN                            DataSize;
2230   EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS    DadXmits;
2231 
2232   Status     = EFI_SUCCESS;
2233   PxeMode    = Private->PxeBc.Mode;
2234   Ip6Cfg     = Private->Ip6Cfg;
2235   Timer      = NULL;
2236 
2237   //
2238   // Build option list for the request packet.
2239   //
2240   OptCount   = PxeBcBuildDhcp6Options (Private, OptList, Buffer);
2241   ASSERT (OptCount> 0);
2242 
2243   Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
2244   if (Retransmit == NULL) {
2245     return EFI_OUT_OF_RESOURCES;
2246   }
2247 
2248   ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));
2249   ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
2250 
2251   Config.OptionCount           = OptCount;
2252   Config.OptionList            = OptList;
2253   Config.Dhcp6Callback         = PxeBcDhcp6CallBack;
2254   Config.CallbackContext       = Private;
2255   Config.IaInfoEvent           = NULL;
2256   Config.RapidCommit           = FALSE;
2257   Config.ReconfigureAccept     = FALSE;
2258   Config.IaDescriptor.IaId     = Private->IaId;
2259   Config.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;
2260   Config.SolicitRetransmission = Retransmit;
2261   Retransmit->Irt              = 4;
2262   Retransmit->Mrc              = 4;
2263   Retransmit->Mrt              = 32;
2264   Retransmit->Mrd              = 60;
2265 
2266   //
2267   // Configure the DHCPv6 instance for PXE boot.
2268   //
2269   Status = Dhcp6->Configure (Dhcp6, &Config);
2270   FreePool (Retransmit);
2271   if (EFI_ERROR (Status)) {
2272     return Status;
2273   }
2274 
2275   //
2276   // Initialize the record fields for DHCPv6 offer in private data.
2277   //
2278   Private->IsProxyRecved = FALSE;
2279   Private->OfferNum      = 0;
2280   Private->SelectIndex   = 0;
2281   ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
2282   ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
2283 
2284 
2285   //
2286   // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
2287   //
2288   Status = Dhcp6->Start (Dhcp6);
2289   if (Status == EFI_NO_MAPPING) {
2290     //
2291     // IP6 Linklocal address is not available for use, so stop current Dhcp process
2292     // and wait for duplicate address detection to finish.
2293     //
2294     Dhcp6->Stop (Dhcp6);
2295 
2296     //
2297     // Get Duplicate Address Detection Transmits count.
2298     //
2299     DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
2300     Status = Ip6Cfg->GetData (
2301                        Ip6Cfg,
2302                        Ip6ConfigDataTypeDupAddrDetectTransmits,
2303                        &DataSize,
2304                        &DadXmits
2305                        );
2306     if (EFI_ERROR (Status)) {
2307       Dhcp6->Configure (Dhcp6, NULL);
2308       return Status;
2309     }
2310 
2311     Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
2312     if (EFI_ERROR (Status)) {
2313       Dhcp6->Configure (Dhcp6, NULL);
2314       return Status;
2315     }
2316 
2317     GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;
2318     Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);
2319     if (EFI_ERROR (Status)) {
2320       gBS->CloseEvent (Timer);
2321       Dhcp6->Configure (Dhcp6, NULL);
2322       return Status;
2323     }
2324 
2325     do {
2326 
2327       TimerStatus = gBS->CheckEvent (Timer);
2328       if (!EFI_ERROR (TimerStatus)) {
2329         Status = Dhcp6->Start (Dhcp6);
2330       }
2331     } while (TimerStatus == EFI_NOT_READY);
2332 
2333     gBS->CloseEvent (Timer);
2334   }
2335   if (EFI_ERROR (Status)) {
2336     if (Status == EFI_ICMP_ERROR) {
2337       PxeMode->IcmpErrorReceived = TRUE;
2338     }
2339     Dhcp6->Configure (Dhcp6, NULL);
2340     return Status;
2341   }
2342 
2343   //
2344   // Get the acquired IPv6 address and store them.
2345   //
2346   Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);
2347   if (EFI_ERROR (Status)) {
2348     Dhcp6->Stop (Dhcp6);
2349     return Status;
2350   }
2351 
2352   ASSERT ((Mode.Ia != NULL) && (Mode.Ia->State == Dhcp6Bound));
2353   //
2354   // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the
2355   // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when
2356   // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as
2357   // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery
2358   // to find a valid router address.
2359   //
2360   CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));
2361   if (Mode.ClientId != NULL) {
2362     FreePool (Mode.ClientId);
2363   }
2364   if (Mode.Ia != NULL) {
2365     FreePool (Mode.Ia);
2366   }
2367   //
2368   // Check the selected offer whether BINL retry is needed.
2369   //
2370   Status = PxeBcHandleDhcp6Offer (Private);
2371   if (EFI_ERROR (Status)) {
2372     Dhcp6->Stop (Dhcp6);
2373     return Status;
2374   }
2375 
2376   return EFI_SUCCESS;
2377 }
2378