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