2 Functions implementation related with DHCPv6 for UefiPxeBc Driver.
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
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.
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.
17 #include "PxeBcImpl.h"
20 // Well-known multi-cast address defined in section-24.1 of rfc-3315
22 // ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
24 EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress
= {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
27 Parse out a DHCPv6 option by OptTag, and find the position in buffer.
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.
33 @retval NULL Failed to parse the required option.
34 @retval Others The postion of the required option in buffer.
37 EFI_DHCP6_PACKET_OPTION
*
38 PxeBcParseDhcp6Options (
44 EFI_DHCP6_PACKET_OPTION
*Option
;
47 Option
= (EFI_DHCP6_PACKET_OPTION
*) Buffer
;
51 // OpLen and OpCode here are both stored in network order.
53 while (Offset
< Length
) {
55 if (NTOHS (Option
->OpCode
) == OptTag
) {
60 Offset
+= (NTOHS(Option
->OpLen
) + 4);
61 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Buffer
+ Offset
);
69 Build the options buffer for the DHCPv6 request packet.
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.
75 @return Index The count of the built-in options.
79 PxeBcBuildDhcp6Options (
80 IN PXEBC_PRIVATE_DATA
*Private
,
81 OUT EFI_DHCP6_PACKET_OPTION
**OptList
,
85 PXEBC_DHCP6_OPTION_ENTRY OptEnt
;
90 OptList
[0] = (EFI_DHCP6_PACKET_OPTION
*) Buffer
;
93 // Append client option request option
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
);
101 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
104 // Append client network device interface option
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
;
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
;
115 OptEnt
.Undi
->Type
= DEFAULT_UNDI_TYPE
;
116 OptEnt
.Undi
->MajorVer
= DEFAULT_UNDI_MAJOR
;
117 OptEnt
.Undi
->MinorVer
= DEFAULT_UNDI_MINOR
;
121 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
124 // Append client system architecture option
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
));
132 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
135 // Append vendor class option to store the PXE class identifier.
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
));
143 &OptEnt
.VendorClass
->ClassId
,
144 DEFAULT_CLASS_ID_DATA
,
145 sizeof (PXEBC_CLASS_ID
)
147 PxeBcUintnToAscDecWithFormat (
148 EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE
,
149 OptEnt
.VendorClass
->ClassId
.ArchitectureType
,
150 sizeof (OptEnt
.VendorClass
->ClassId
.ArchitectureType
)
153 if (Private
->Nii
!= NULL
) {
155 OptEnt
.VendorClass
->ClassId
.InterfaceName
,
156 Private
->Nii
->StringId
,
157 sizeof (OptEnt
.VendorClass
->ClassId
.InterfaceName
)
159 PxeBcUintnToAscDecWithFormat (
160 Private
->Nii
->MajorVer
,
161 OptEnt
.VendorClass
->ClassId
.UndiMajor
,
162 sizeof (OptEnt
.VendorClass
->ClassId
.UndiMajor
)
164 PxeBcUintnToAscDecWithFormat (
165 Private
->Nii
->MinorVer
,
166 OptEnt
.VendorClass
->ClassId
.UndiMinor
,
167 sizeof (OptEnt
.VendorClass
->ClassId
.UndiMinor
)
178 Cache the DHCPv6 packet.
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.
185 PxeBcCacheDhcp6Packet (
186 IN EFI_DHCP6_PACKET
*Dst
,
187 IN EFI_DHCP6_PACKET
*Src
190 ASSERT (Dst
->Size
>= Src
->Length
);
192 CopyMem (&Dst
->Dhcp6
, &Src
->Dhcp6
, Src
->Length
);
193 Dst
->Length
= Src
->Length
;
198 Free all the nodes in the list for boot file.
200 @param[in] Head The pointer to the head of list.
204 PxeBcFreeBootFileOption (
209 LIST_ENTRY
*NextEntry
;
210 PXEBC_DHCP6_OPTION_NODE
*Node
;
212 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, Head
) {
213 Node
= NET_LIST_USER_STRUCT (Entry
, PXEBC_DHCP6_OPTION_NODE
, Link
);
214 RemoveEntryList (Entry
);
221 Parse the Boot File URL option.
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.
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.
234 PxeBcExtractBootFileUrl (
235 OUT UINT8
**FileName
,
236 IN OUT EFI_IPv6_ADDRESS
*SrvAddr
,
242 CHAR8
*BootFileNamePtr
;
244 UINT16 BootFileNameLen
;
247 CHAR8
*ServerAddressOption
;
248 CHAR8
*ServerAddress
;
253 // The format of the Boot File URL option is:
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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
261 // . bootfile-url (variable length) .
263 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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.
272 PrefixLen
= (UINT16
) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX
);
274 if (Length
<= PrefixLen
||
275 CompareMem (BootFile
, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX
, PrefixLen
) != 0) {
276 return EFI_NOT_FOUND
;
279 BootFile
= BootFile
+ PrefixLen
;
280 Length
= (UINT16
) (Length
- PrefixLen
);
282 TmpStr
= (CHAR8
*) AllocateZeroPool (Length
+ 1);
283 if (TmpStr
== NULL
) {
284 return EFI_OUT_OF_RESOURCES
;
287 CopyMem (TmpStr
, BootFile
, Length
);
288 TmpStr
[Length
] = '\0';
291 // Get the part of SERVER_ADDRESS string.
293 ServerAddressOption
= TmpStr
;
294 if (*ServerAddressOption
!= PXEBC_ADDR_START_DELIMITER
) {
296 return EFI_INVALID_PARAMETER
;
299 ServerAddressOption
++;
300 ServerAddress
= ServerAddressOption
;
301 while (*ServerAddress
!= '\0' && *ServerAddress
!= PXEBC_ADDR_END_DELIMITER
) {
305 if (*ServerAddress
!= PXEBC_ADDR_END_DELIMITER
) {
307 return EFI_INVALID_PARAMETER
;
310 *ServerAddress
= '\0';
313 // Convert the string of server address to Ipv6 address format and store it.
315 Status
= NetLibAsciiStrToIp6 (ServerAddressOption
, SrvAddr
);
316 if (EFI_ERROR (Status
)) {
322 // Get the part of BOOTFILE_NAME string.
324 BootFileNamePtr
= (CHAR8
*)((UINTN
)ServerAddress
+ 1);
325 if (*BootFileNamePtr
!= PXEBC_TFTP_URL_SEPARATOR
) {
327 return EFI_INVALID_PARAMETER
;
331 BootFileNameLen
= (UINT16
)(Length
- (UINT16
) ((UINTN
)BootFileNamePtr
- (UINTN
)TmpStr
) + 1);
332 if (BootFileNameLen
!= 0 || FileName
!= NULL
) {
334 // Remove trailing mode=octet if present and ignore. All other modes are
335 // invalid for netboot6, so reject them.
337 ModeStr
= AsciiStrStr (BootFileNamePtr
, ";mode=octet");
338 if (ModeStr
!= NULL
&& *(ModeStr
+ AsciiStrLen (";mode=octet")) == '\0') {
340 } else if (AsciiStrStr (BootFileNamePtr
, ";mode=") != NULL
) {
341 return EFI_INVALID_PARAMETER
;
345 // Extract boot file name from URL.
347 BootFileName
= (CHAR8
*) AllocateZeroPool (BootFileNameLen
);
348 if (BootFileName
== NULL
) {
350 return EFI_OUT_OF_RESOURCES
;
352 *FileName
= (UINT8
*) BootFileName
;
355 // Decode percent-encoding in boot file name.
357 while (*BootFileNamePtr
!= '\0') {
358 if (*BootFileNamePtr
== '%') {
359 TmpChar
= *(BootFileNamePtr
+ 3);
360 *(BootFileNamePtr
+ 3) = '\0';
361 *BootFileName
= (UINT8
) AsciiStrHexToUintn ((CHAR8
*)(BootFileNamePtr
+ 1));
363 *(BootFileNamePtr
+ 3) = TmpChar
;
364 BootFileNamePtr
+= 3;
366 *BootFileName
= *BootFileNamePtr
;
371 *BootFileName
= '\0';
381 Parse the Boot File Parameter option.
383 @param[in] BootFilePara The pointer to boot file parameter option data.
384 @param[out] BootFileSize The pointer to the parsed boot file size.
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.
391 PxeBcExtractBootFileParam (
392 IN CHAR8
*BootFilePara
,
393 OUT UINT16
*BootFileSize
401 CopyMem (&Length
, BootFilePara
, sizeof (UINT16
));
402 Length
= NTOHS (Length
);
405 // The BootFile Size should be 1~5 byte ASCII strings
407 if (Length
< 1 || Length
> 5) {
408 return EFI_NOT_FOUND
;
412 // Extract the value of BootFile Size.
414 BootFilePara
= BootFilePara
+ sizeof (UINT16
);
416 for (Index
= 0; Index
< Length
; Index
++) {
417 if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit
, *(BootFilePara
+ Index
)))) {
418 return EFI_NOT_FOUND
;
421 Size
= (Size
+ Digit
) * 10;
425 if (Size
> PXEBC_DHCP6_MAX_BOOT_FILE_SIZE
) {
426 return EFI_NOT_FOUND
;
429 *BootFileSize
= (UINT16
) Size
;
435 Parse the cached DHCPv6 packet, including all the options.
437 @param[in] Cache6 The pointer to a cached DHCPv6 packet.
439 @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
440 @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.
444 PxeBcParseDhcp6Packet (
445 IN PXEBC_DHCP6_PACKET_CACHE
*Cache6
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
;
456 UINT32 EnterpriseNum
;
460 Offer
= &Cache6
->Packet
.Offer
;
461 Options
= Cache6
->OptList
;
463 ZeroMem (Cache6
->OptList
, sizeof (Cache6
->OptList
));
465 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Offer
->Dhcp6
.Option
);
467 Length
= GET_DHCP6_OPTION_SIZE (Offer
);
470 // OpLen and OpCode here are both stored in network order, since they are from original packet.
472 while (Offset
< Length
) {
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
) {
478 // The server sends this option to inform the client about an URL to a boot file.
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
;
487 Offset
+= (NTOHS (Option
->OpLen
) + 4);
488 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Offer
->Dhcp6
.Option
+ Offset
);
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.
495 Option
= Options
[PXEBC_DHCP6_IDX_IA_NA
];
496 if (Option
!= NULL
) {
497 Option
= PxeBcParseDhcp6Options (
499 NTOHS (Option
->OpLen
),
500 PXEBC_DHCP6_OPT_STATUS_CODE
502 if ((Option
!= NULL
&& Option
->Data
[0] == 0) || (Option
== NULL
)) {
503 IsProxyOffer
= FALSE
;
508 // The offer with "PXEClient" is a pxe offer.
510 Option
= Options
[PXEBC_DHCP6_IDX_VENDOR_CLASS
];
511 EnterpriseNum
= HTONL(PXEBC_DHCP6_ENTERPRISE_NUM
);
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) {
521 // Determine offer type of the dhcp6 packet.
525 // It's a binl offer only with PXEClient.
527 OfferType
= IsProxyOffer
? PxeOfferTypeProxyBinl
: PxeOfferTypeDhcpBinl
;
530 // It's a dhcp only offer, which is a pure dhcp6 offer packet.
532 OfferType
= PxeOfferTypeDhcpOnly
;
535 Cache6
->OfferType
= OfferType
;
542 Cache the DHCPv6 ack packet, and parse it on demand.
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.
551 IN PXEBC_PRIVATE_DATA
*Private
,
552 IN EFI_DHCP6_PACKET
*Ack
,
556 EFI_PXE_BASE_CODE_MODE
*Mode
;
558 Mode
= Private
->PxeBc
.Mode
;
560 PxeBcCacheDhcp6Packet (&Private
->DhcpAck
.Dhcp6
.Packet
.Ack
, Ack
);
564 // Parse the ack packet and store it into mode data if needed.
566 PxeBcParseDhcp6Packet (&Private
->DhcpAck
.Dhcp6
);
567 CopyMem (&Mode
->DhcpAck
.Dhcpv6
, &Ack
->Dhcp6
, Ack
->Length
);
568 Mode
->DhcpAckReceived
= TRUE
;
574 Cache the DHCPv6 proxy offer packet according to the received order.
576 @param[in] Private The pointer to PxeBc private data.
577 @param[in] OfferIndex The received order of offer packets.
581 PxeBcCopyDhcp6Proxy (
582 IN PXEBC_PRIVATE_DATA
*Private
,
586 EFI_PXE_BASE_CODE_MODE
*Mode
;
587 EFI_DHCP6_PACKET
*Offer
;
589 ASSERT (OfferIndex
< Private
->OfferNum
);
590 ASSERT (OfferIndex
< PXEBC_OFFER_MAX_NUM
);
592 Mode
= Private
->PxeBc
.Mode
;
593 Offer
= &Private
->OfferBuffer
[OfferIndex
].Dhcp6
.Packet
.Offer
;
596 // Cache the proxy offer packet and parse it.
598 PxeBcCacheDhcp6Packet (&Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
, Offer
);
599 PxeBcParseDhcp6Packet (&Private
->ProxyOffer
.Dhcp6
);
602 // Store this packet into mode data.
604 CopyMem (&Mode
->ProxyOffer
.Dhcpv6
, &Offer
->Dhcp6
, Offer
->Length
);
605 Mode
->ProxyOfferReceived
= TRUE
;
609 Seek the address of the first byte of the option header.
611 @param[in] Buf The pointer to the buffer.
612 @param[in] SeekLen The length to seek.
613 @param[in] OptType The option type.
615 @retval NULL If it failed to seek the option.
616 @retval others The position to the option.
620 PxeBcDhcp6SeekOption (
634 while (Cursor
< Buf
+ SeekLen
) {
635 OpCode
= ReadUnaligned16 ((UINT16
*) Cursor
);
636 if (OpCode
== HTONS (OptType
)) {
640 DataLen
= NTOHS (ReadUnaligned16 ((UINT16
*) (Cursor
+ 2)));
641 Cursor
+= (DataLen
+ 4);
649 Build and send out the request packet for the bootfile, and parse the reply.
651 @param[in] Private The pointer to PxeBc private data.
652 @param[in] Index PxeBc option boot item type.
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.
661 PxeBcRequestBootService (
662 IN PXEBC_PRIVATE_DATA
*Private
,
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
;
671 EFI_DHCP6_PACKET
*Request
;
673 EFI_DHCP6_PACKET
*Reply
;
681 EFI_DHCP6_PACKET
*ProxyOffer
;
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
;
691 if (Request
== NULL
) {
692 return EFI_DEVICE_ERROR
;
695 Discover
= AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET
));
696 if (Discover
== NULL
) {
697 return EFI_OUT_OF_RESOURCES
;
701 // Build the request packet by the cached request packet before.
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
;
711 // Find Server ID Option from ProxyOffer.
713 Option
= PxeBcDhcp6SeekOption (
714 ProxyOffer
->Dhcp6
.Option
,
715 ProxyOffer
->Length
- 4,
716 PXEBC_DHCP6_OPT_SERVER_ID
718 if (Option
== NULL
) {
719 return EFI_NOT_FOUND
;
723 // Add Server ID Option.
725 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) Option
)->OpLen
);
726 CopyMem (DiscoverOpt
, Option
, OpLen
+ 4);
727 DiscoverOpt
+= (OpLen
+ 4);
728 DiscoverLen
+= (OpLen
+ 4);
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
738 // Copy all the options except IA option and Server ID
740 CopyMem (DiscoverOpt
, RequestOpt
, OpLen
+ 4);
741 DiscoverOpt
+= (OpLen
+ 4);
742 DiscoverLen
+= (OpLen
+ 4);
744 RequestOpt
+= (OpLen
+ 4);
745 RequestLen
+= (OpLen
+ 4);
749 // Update Elapsed option in the package
751 Option
= PxeBcDhcp6SeekOption (
752 Discover
->DhcpOptions
,
753 (UINT32
)(RequestLen
- 4),
754 PXEBC_DHCP6_OPT_ELAPSED_TIME
756 if (Option
!= NULL
) {
757 CalcElapsedTime (Private
);
758 WriteUnaligned16 ((UINT16
*)(Option
+ 4), HTONS((UINT16
) Private
->ElapsedTime
));
761 Status
= PxeBc
->UdpWrite (
775 if (EFI_ERROR (Status
)) {
780 // Cache the right PXE reply packet here, set valid flag later.
781 // Especially for PXE discover packet, store it into mode data here.
783 Reply
= &Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
;
784 ReadSize
= (UINTN
) Reply
->Size
;
787 // Start Udp6Read instance
789 Status
= Private
->Udp6Read
->Configure (Private
->Udp6Read
, &Private
->Udp6CfgData
);
790 if (EFI_ERROR (Status
)) {
794 Status
= PxeBc
->UdpRead (
796 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
,
804 (VOID
*) &Reply
->Dhcp6
807 // Stop Udp6Read instance
809 Private
->Udp6Read
->Configure (Private
->Udp6Read
, NULL
);
811 if (EFI_ERROR (Status
)) {
818 Reply
->Length
= (UINT32
) ReadSize
;
825 Retry to request bootfile name by the BINL offer.
827 @param[in] Private The pointer to PxeBc private data.
828 @param[in] Index The received order of offer packets.
830 @retval EFI_SUCCESS Successfully retried a request for the bootfile name.
831 @retval EFI_DEVICE_ERROR Failed to retry the bootfile name.
835 PxeBcRetryDhcp6Binl (
836 IN PXEBC_PRIVATE_DATA
*Private
,
840 EFI_PXE_BASE_CODE_MODE
*Mode
;
841 PXEBC_DHCP6_PACKET_CACHE
*Offer
;
842 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
845 ASSERT (Index
< PXEBC_OFFER_MAX_NUM
);
846 ASSERT (Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
== PxeOfferTypeDhcpBinl
||
847 Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
== PxeOfferTypeProxyBinl
);
849 Mode
= Private
->PxeBc
.Mode
;
850 Private
->IsDoDiscover
= FALSE
;
851 Offer
= &Private
->OfferBuffer
[Index
].Dhcp6
;
852 if (Offer
->OfferType
== PxeOfferTypeDhcpBinl
) {
854 // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.
857 &Private
->ServerIp
.v6
,
858 &mAllDhcpRelayAndServersAddress
,
859 sizeof (EFI_IPv6_ADDRESS
)
862 ASSERT (Offer
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] != NULL
);
864 // Parse out the next server address from the last offer, and store it
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
)
872 if (EFI_ERROR (Status
)) {
878 // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
880 Status
= PxeBcRequestBootService (Private
, Index
);
882 if (EFI_ERROR (Status
)) {
886 Cache6
= &Private
->ProxyOffer
.Dhcp6
;
887 Status
= PxeBcParseDhcp6Packet (Cache6
);
888 if (EFI_ERROR (Status
)) {
892 if (Cache6
->OfferType
!= PxeOfferTypeProxyPxe10
&&
893 Cache6
->OfferType
!= PxeOfferTypeProxyWfm11a
&&
894 Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] == NULL
) {
896 // This BINL ack doesn't have discovery option set or multicast option set
897 // or bootfile name specified.
899 return EFI_DEVICE_ERROR
;
902 Mode
->ProxyOfferReceived
= TRUE
;
904 &Mode
->ProxyOffer
.Dhcpv6
,
905 &Cache6
->Packet
.Offer
.Dhcp6
,
906 Cache6
->Packet
.Offer
.Length
914 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
916 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
917 @param[in] RcvdOffer The pointer to the received offer packet.
921 PxeBcCacheDhcp6Offer (
922 IN PXEBC_PRIVATE_DATA
*Private
,
923 IN EFI_DHCP6_PACKET
*RcvdOffer
926 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
927 EFI_DHCP6_PACKET
*Offer
;
928 PXEBC_OFFER_TYPE OfferType
;
930 Cache6
= &Private
->OfferBuffer
[Private
->OfferNum
].Dhcp6
;
931 Offer
= &Cache6
->Packet
.Offer
;
934 // Cache the content of DHCPv6 packet firstly.
936 PxeBcCacheDhcp6Packet (Offer
, RcvdOffer
);
939 // Validate the DHCPv6 packet, and parse the options and offer type.
941 if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6
))) {
946 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
948 OfferType
= Cache6
->OfferType
;
949 ASSERT (OfferType
< PxeOfferTypeMax
);
950 ASSERT (Private
->OfferCount
[OfferType
] < PXEBC_OFFER_MAX_NUM
);
952 if (IS_PROXY_OFFER (OfferType
)) {
954 // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
956 Private
->IsProxyRecved
= TRUE
;
958 if (OfferType
== PxeOfferTypeProxyBinl
) {
960 // Cache all proxy BINL offers.
962 Private
->OfferIndex
[OfferType
][Private
->OfferCount
[OfferType
]] = Private
->OfferNum
;
963 Private
->OfferCount
[OfferType
]++;
964 } else if (Private
->OfferCount
[OfferType
] > 0) {
966 // Only cache the first PXE10/WFM11a offer, and discard the others.
968 Private
->OfferIndex
[OfferType
][0] = Private
->OfferNum
;
969 Private
->OfferCount
[OfferType
] = 1;
975 // It's a DHCPv6 offer with yiaddr, and cache them all.
977 Private
->OfferIndex
[OfferType
][Private
->OfferCount
[OfferType
]] = Private
->OfferNum
;
978 Private
->OfferCount
[OfferType
]++;
986 Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
988 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
992 PxeBcSelectDhcp6Offer (
993 IN PXEBC_PRIVATE_DATA
*Private
998 PXEBC_OFFER_TYPE OfferType
;
1000 Private
->SelectIndex
= 0;
1002 if (Private
->IsOfferSorted
) {
1004 // Select offer by default policy.
1006 if (Private
->OfferCount
[PxeOfferTypeDhcpPxe10
] > 0) {
1008 // 1. DhcpPxe10 offer
1010 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpPxe10
][0] + 1;
1012 } else if (Private
->OfferCount
[PxeOfferTypeDhcpWfm11a
] > 0) {
1014 // 2. DhcpWfm11a offer
1016 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpWfm11a
][0] + 1;
1018 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1019 Private
->OfferCount
[PxeOfferTypeProxyPxe10
] > 0) {
1021 // 3. DhcpOnly offer and ProxyPxe10 offer.
1023 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1024 Private
->SelectProxyType
= PxeOfferTypeProxyPxe10
;
1026 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1027 Private
->OfferCount
[PxeOfferTypeProxyWfm11a
] > 0) {
1029 // 4. DhcpOnly offer and ProxyWfm11a offer.
1031 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1032 Private
->SelectProxyType
= PxeOfferTypeProxyWfm11a
;
1034 } else if (Private
->OfferCount
[PxeOfferTypeDhcpBinl
] > 0) {
1036 // 5. DhcpBinl offer.
1038 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpBinl
][0] + 1;
1040 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1041 Private
->OfferCount
[PxeOfferTypeProxyBinl
] > 0) {
1043 // 6. DhcpOnly offer and ProxyBinl offer.
1045 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1046 Private
->SelectProxyType
= PxeOfferTypeProxyBinl
;
1050 // 7. DhcpOnly offer with bootfilename.
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;
1062 // Select offer by received order.
1064 for (Index
= 0; Index
< Private
->OfferNum
; Index
++) {
1066 OfferType
= Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
;
1068 if (IS_PROXY_OFFER (OfferType
)) {
1070 // Skip proxy offers
1075 if (!Private
->IsProxyRecved
&&
1076 OfferType
== PxeOfferTypeDhcpOnly
&&
1077 Private
->OfferBuffer
[Index
].Dhcp6
.OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] == NULL
) {
1079 // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
1084 Private
->SelectIndex
= Index
+ 1;
1092 Handle the DHCPv6 offer packet.
1094 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1096 @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully.
1097 @retval EFI_NO_RESPONSE No response to the following request packet.
1101 PxeBcHandleDhcp6Offer (
1102 IN PXEBC_PRIVATE_DATA
*Private
1105 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
1107 PXEBC_OFFER_TYPE OfferType
;
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
;
1118 if (Cache6
->OfferType
== PxeOfferTypeDhcpBinl
) {
1120 // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
1122 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, SelectIndex
))) {
1123 Status
= EFI_NO_RESPONSE
;
1125 } else if (Cache6
->OfferType
== PxeOfferTypeDhcpOnly
) {
1127 if (Private
->IsProxyRecved
) {
1129 // DhcpOnly offer is selected, so need try to request bootfilename.
1132 if (Private
->IsOfferSorted
) {
1134 // The proxy offer should be determined if select by default policy.
1135 // IsOfferSorted means all offers are labeled by OfferIndex.
1137 ASSERT (Private
->OfferCount
[Private
->SelectProxyType
] > 0);
1139 if (Private
->SelectProxyType
== PxeOfferTypeProxyBinl
) {
1141 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1143 for (Index
= 0; Index
< Private
->OfferCount
[Private
->SelectProxyType
]; Index
++) {
1145 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][Index
];
1146 if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, ProxyIndex
))) {
1150 if (Index
== Private
->OfferCount
[Private
->SelectProxyType
]) {
1151 Status
= EFI_NO_RESPONSE
;
1155 // For other proxy offers (pxe10 or wfm11a), only one is buffered.
1157 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][0];
1161 // The proxy offer should not be determined if select by received order.
1163 Status
= EFI_NO_RESPONSE
;
1165 for (Index
= 0; Index
< Private
->OfferNum
; Index
++) {
1167 OfferType
= Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
;
1169 if (!IS_PROXY_OFFER (OfferType
)) {
1171 // Skip non proxy dhcp offers.
1176 if (OfferType
== PxeOfferTypeProxyBinl
) {
1178 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1180 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, Index
))) {
1185 Private
->SelectProxyType
= OfferType
;
1187 Status
= EFI_SUCCESS
;
1192 if (!EFI_ERROR (Status
) && Private
->SelectProxyType
!= PxeOfferTypeProxyBinl
) {
1194 // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
1196 PxeBcCopyDhcp6Proxy (Private
, ProxyIndex
);
1200 // Othewise, the bootfilename must be included in DhcpOnly offer.
1202 ASSERT (Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] != NULL
);
1206 if (!EFI_ERROR (Status
)) {
1208 // All PXE boot information is ready by now.
1210 PxeBcCopyDhcp6Ack (Private
, &Private
->DhcpAck
.Dhcp6
.Packet
.Ack
, TRUE
);
1211 Private
->PxeBc
.Mode
->DhcpDiscoverValid
= TRUE
;
1219 Unregister the address by Ip6Config protocol.
1221 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1225 PxeBcUnregisterIp6Address (
1226 IN PXEBC_PRIVATE_DATA
*Private
1229 if (Private
->Ip6Policy
!= PXEBC_IP6_POLICY_MAX
) {
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.
1238 Check whether IP driver could route the message which will be sent to ServerIp address.
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.
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.
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.
1253 PxeBcCheckRouteTable (
1254 IN PXEBC_PRIVATE_DATA
*Private
,
1255 IN UINTN TimeOutInSecond
,
1256 OUT EFI_IPv6_ADDRESS
*GatewayAddr
1260 EFI_IP6_PROTOCOL
*Ip6
;
1261 EFI_IP6_MODE_DATA Ip6ModeData
;
1263 EFI_EVENT TimeOutEvt
;
1265 BOOLEAN GatewayIsFound
;
1267 ASSERT (GatewayAddr
!= NULL
);
1268 ASSERT (Private
!= NULL
);
1271 GatewayIsFound
= FALSE
;
1274 ZeroMem (GatewayAddr
, sizeof (EFI_IPv6_ADDRESS
));
1277 Status
= Ip6
->GetModeData (Ip6
, &Ip6ModeData
, NULL
, NULL
);
1278 if (EFI_ERROR (Status
)) {
1283 // Find out the gateway address which can route the message whcih send to ServerIp.
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
;
1293 if (Ip6ModeData
.AddressList
!= NULL
) {
1294 FreePool (Ip6ModeData
.AddressList
);
1296 if (Ip6ModeData
.GroupTable
!= NULL
) {
1297 FreePool (Ip6ModeData
.GroupTable
);
1299 if (Ip6ModeData
.RouteTable
!= NULL
) {
1300 FreePool (Ip6ModeData
.RouteTable
);
1302 if (Ip6ModeData
.NeighborCache
!= NULL
) {
1303 FreePool (Ip6ModeData
.NeighborCache
);
1305 if (Ip6ModeData
.PrefixTable
!= NULL
) {
1306 FreePool (Ip6ModeData
.PrefixTable
);
1308 if (Ip6ModeData
.IcmpTypeList
!= NULL
) {
1309 FreePool (Ip6ModeData
.IcmpTypeList
);
1312 if (GatewayIsFound
|| RetryCount
== TimeOutInSecond
) {
1319 // Delay 1 second then recheck it again.
1321 if (TimeOutEvt
== NULL
) {
1322 Status
= gBS
->CreateEvent (
1329 if (EFI_ERROR (Status
)) {
1334 Status
= gBS
->SetTimer (TimeOutEvt
, TimerRelative
, TICKS_PER_SECOND
);
1335 if (EFI_ERROR (Status
)) {
1338 while (EFI_ERROR (gBS
->CheckEvent (TimeOutEvt
))) {
1344 if (TimeOutEvt
!= NULL
) {
1345 gBS
->CloseEvent (TimeOutEvt
);
1348 if (GatewayIsFound
) {
1349 Status
= EFI_SUCCESS
;
1350 } else if (RetryCount
== TimeOutInSecond
) {
1351 Status
= EFI_TIMEOUT
;
1358 Register the ready station address and gateway by Ip6Config protocol.
1360 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1361 @param[in] Address The pointer to the ready address.
1363 @retval EFI_SUCCESS Registered the address succesfully.
1364 @retval Others Failed to register the address.
1368 PxeBcRegisterIp6Address (
1369 IN PXEBC_PRIVATE_DATA
*Private
,
1370 IN EFI_IPv6_ADDRESS
*Address
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
;
1379 EFI_EVENT TimeOutEvt
;
1380 EFI_EVENT MappedEvt
;
1382 UINT64 DadTriggerTime
;
1383 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits
;
1385 Status
= EFI_SUCCESS
;
1388 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1389 Ip6Cfg
= Private
->Ip6Cfg
;
1392 ZeroMem (&CfgAddr
, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS
));
1393 CopyMem (&CfgAddr
.Address
, Address
, sizeof (EFI_IPv6_ADDRESS
));
1395 Status
= Ip6
->Configure (Ip6
, &Private
->Ip6CfgData
);
1396 if (EFI_ERROR (Status
)) {
1401 // Retrieve the gateway address from IP6 route table.
1403 Status
= PxeBcCheckRouteTable (Private
, PXEBC_IP6_ROUTE_TABLE_TIMEOUT
, &GatewayAddr
);
1404 if (EFI_ERROR (Status
)) {
1409 // There is no channel between IP6 and PXE driver about address setting,
1410 // so it has to set the new address by Ip6ConfigProtocol manually.
1412 Policy
= Ip6ConfigPolicyManual
;
1413 Status
= Ip6Cfg
->SetData (
1415 Ip6ConfigDataTypePolicy
,
1416 sizeof(EFI_IP6_CONFIG_POLICY
),
1419 if (EFI_ERROR (Status
)) {
1421 // There is no need to recover later.
1423 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1428 // Get Duplicate Address Detection Transmits count.
1430 DataSize
= sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS
);
1431 Status
= Ip6Cfg
->GetData (
1433 Ip6ConfigDataTypeDupAddrDetectTransmits
,
1437 if (EFI_ERROR (Status
)) {
1442 // Create a timer as setting address timeout event since DAD in IP6 driver.
1444 Status
= gBS
->CreateEvent (
1451 if (EFI_ERROR (Status
)) {
1456 // Create a notify event to set address flag when DAD if IP6 driver succeeded.
1458 Status
= gBS
->CreateEvent (
1462 &Private
->IsAddressOk
,
1465 if (EFI_ERROR (Status
)) {
1469 Status
= Ip6Cfg
->RegisterDataNotify (
1471 Ip6ConfigDataTypeManualAddress
,
1474 if (EFI_ERROR(Status
)) {
1478 Status
= Ip6Cfg
->SetData (
1480 Ip6ConfigDataTypeManualAddress
,
1481 sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS
),
1484 if (EFI_ERROR(Status
) && Status
!= EFI_NOT_READY
) {
1489 // Start the 5 secondes timer to wait for setting address.
1491 Status
= EFI_NO_MAPPING
;
1492 DadTriggerTime
= TICKS_PER_SECOND
* DadXmits
.DupAddrDetectTransmits
+ PXEBC_DAD_ADDITIONAL_DELAY
;
1493 gBS
->SetTimer (TimeOutEvt
, TimerRelative
, DadTriggerTime
);
1495 while (EFI_ERROR (gBS
->CheckEvent (TimeOutEvt
))) {
1497 if (Private
->IsAddressOk
) {
1498 Status
= EFI_SUCCESS
;
1504 // Set the default gateway address back if needed.
1506 if (!NetIp6IsUnspecifiedAddr (&GatewayAddr
)) {
1507 Status
= Ip6Cfg
->SetData (
1509 Ip6ConfigDataTypeGateway
,
1510 sizeof (EFI_IPv6_ADDRESS
),
1513 if (EFI_ERROR (Status
)) {
1519 if (MappedEvt
!= NULL
) {
1520 Ip6Cfg
->UnregisterDataNotify (
1522 Ip6ConfigDataTypeManualAddress
,
1525 gBS
->CloseEvent (MappedEvt
);
1527 if (TimeOutEvt
!= NULL
) {
1528 gBS
->CloseEvent (TimeOutEvt
);
1534 Set the IP6 policy to Automatic.
1536 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1538 @retval EFI_SUCCESS Switch the IP policy succesfully.
1539 @retval Others Unexpect error happened.
1544 IN PXEBC_PRIVATE_DATA
*Private
1547 EFI_IP6_CONFIG_POLICY Policy
;
1549 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1552 Ip6Cfg
= Private
->Ip6Cfg
;
1553 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1556 // Get and store the current policy of IP6 driver.
1558 Status
= Ip6Cfg
->GetData (
1560 Ip6ConfigDataTypePolicy
,
1564 if (EFI_ERROR (Status
)) {
1568 if (Private
->Ip6Policy
== Ip6ConfigPolicyManual
) {
1569 Policy
= Ip6ConfigPolicyAutomatic
;
1570 Status
= Ip6Cfg
->SetData (
1572 Ip6ConfigDataTypePolicy
,
1573 sizeof(EFI_IP6_CONFIG_POLICY
),
1576 if (EFI_ERROR (Status
)) {
1578 // There is no need to recover later.
1580 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1588 This function will register the station IP address and flush IP instance to start using the new IP address.
1590 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1592 @retval EFI_SUCCESS The new IP address has been configured successfully.
1593 @retval Others Failed to configure the address.
1597 PxeBcSetIp6Address (
1598 IN PXEBC_PRIVATE_DATA
*Private
1602 EFI_DHCP6_PROTOCOL
*Dhcp6
;
1604 Dhcp6
= Private
->Dhcp6
;
1606 CopyMem (&Private
->StationIp
.v6
, &Private
->TmpStationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1607 CopyMem (&Private
->PxeBc
.Mode
->StationIp
.v6
, &Private
->StationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1609 Status
= PxeBcRegisterIp6Address (Private
, &Private
->StationIp
.v6
);
1610 if (EFI_ERROR (Status
)) {
1611 Dhcp6
->Stop (Dhcp6
);
1615 Status
= PxeBcFlushStationIp (Private
, &Private
->StationIp
, NULL
);
1616 if (EFI_ERROR (Status
)) {
1617 PxeBcUnregisterIp6Address (Private
);
1618 Dhcp6
->Stop (Dhcp6
);
1622 AsciiPrint ("\n Station IP address is ");
1623 PxeBcShowIp6Addr (&Private
->StationIp
.v6
);
1629 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
1630 to intercept events that occurred in the configuration process.
1632 @param[in] This The pointer to the EFI DHCPv6 Protocol.
1633 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
1634 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
1635 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
1637 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
1638 @param[out] NewPacket The packet that is used to replace the Packet above.
1640 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
1641 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
1642 driver will continue to wait for more packets.
1643 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
1648 PxeBcDhcp6CallBack (
1649 IN EFI_DHCP6_PROTOCOL
*This
,
1651 IN EFI_DHCP6_STATE CurrentState
,
1652 IN EFI_DHCP6_EVENT Dhcp6Event
,
1653 IN EFI_DHCP6_PACKET
*Packet
,
1654 OUT EFI_DHCP6_PACKET
**NewPacket OPTIONAL
1657 PXEBC_PRIVATE_DATA
*Private
;
1658 EFI_PXE_BASE_CODE_MODE
*Mode
;
1659 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
*Callback
;
1660 EFI_DHCP6_PACKET
*SelectAd
;
1664 if ((Dhcp6Event
!= Dhcp6RcvdAdvertise
) &&
1665 (Dhcp6Event
!= Dhcp6SelectAdvertise
) &&
1666 (Dhcp6Event
!= Dhcp6SendSolicit
) &&
1667 (Dhcp6Event
!= Dhcp6SendRequest
) &&
1668 (Dhcp6Event
!= Dhcp6RcvdReply
)) {
1672 ASSERT (Packet
!= NULL
);
1674 Private
= (PXEBC_PRIVATE_DATA
*) Context
;
1675 Mode
= Private
->PxeBc
.Mode
;
1676 Callback
= Private
->PxeBcCallback
;
1679 // Callback to user when any traffic ocurred if has.
1681 if (Dhcp6Event
!= Dhcp6SelectAdvertise
&& Callback
!= NULL
) {
1682 Received
= (BOOLEAN
) (Dhcp6Event
== Dhcp6RcvdAdvertise
|| Dhcp6Event
== Dhcp6RcvdReply
);
1683 Status
= Callback
->Callback (
1688 (EFI_PXE_BASE_CODE_PACKET
*) &Packet
->Dhcp6
1690 if (Status
!= EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
) {
1695 Status
= EFI_SUCCESS
;
1697 switch (Dhcp6Event
) {
1699 case Dhcp6SendSolicit
:
1701 // Record the first Solicate msg time
1703 if (Private
->SolicitTimes
== 0) {
1704 CalcElapsedTime (Private
);
1705 Private
->SolicitTimes
++;
1708 // Cache the dhcp discover packet to mode data directly.
1710 CopyMem (&Mode
->DhcpDiscover
.Dhcpv4
, &Packet
->Dhcp6
, Packet
->Length
);
1713 case Dhcp6RcvdAdvertise
:
1714 Status
= EFI_NOT_READY
;
1715 if (Private
->OfferNum
< PXEBC_OFFER_MAX_NUM
) {
1717 // Cache the dhcp offers to OfferBuffer[] for select later, and record
1718 // the OfferIndex and OfferCount.
1720 PxeBcCacheDhcp6Offer (Private
, Packet
);
1724 case Dhcp6SendRequest
:
1726 // Store the request packet as seed packet for discover.
1728 if (Private
->Dhcp6Request
!= NULL
) {
1729 FreePool (Private
->Dhcp6Request
);
1731 Private
->Dhcp6Request
= AllocateZeroPool (Packet
->Size
);
1732 if (Private
->Dhcp6Request
!= NULL
) {
1733 CopyMem (Private
->Dhcp6Request
, Packet
, Packet
->Size
);
1737 case Dhcp6SelectAdvertise
:
1739 // Select offer by the default policy or by order, and record the SelectIndex
1740 // and SelectProxyType.
1742 PxeBcSelectDhcp6Offer (Private
);
1744 if (Private
->SelectIndex
== 0) {
1745 Status
= EFI_ABORTED
;
1747 ASSERT (NewPacket
!= NULL
);
1748 SelectAd
= &Private
->OfferBuffer
[Private
->SelectIndex
- 1].Dhcp6
.Packet
.Offer
;
1749 *NewPacket
= AllocateZeroPool (SelectAd
->Size
);
1750 ASSERT (*NewPacket
!= NULL
);
1751 CopyMem (*NewPacket
, SelectAd
, SelectAd
->Size
);
1755 case Dhcp6RcvdReply
:
1757 // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
1758 // without verification.
1760 ASSERT (Private
->SelectIndex
!= 0);
1761 PxeBcCopyDhcp6Ack (Private
, Packet
, FALSE
);
1773 Build and send out the request packet for the bootfile, and parse the reply.
1775 @param[in] Private The pointer to PxeBc private data.
1776 @param[in] Type PxeBc option boot item type.
1777 @param[in] Layer The pointer to option boot item layer.
1778 @param[in] UseBis Use BIS or not.
1779 @param[in] DestIp The pointer to the server address.
1781 @retval EFI_SUCCESS Successfully discovered the boot file.
1782 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1783 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
1784 @retval Others Failed to discover the boot file.
1788 PxeBcDhcp6Discover (
1789 IN PXEBC_PRIVATE_DATA
*Private
,
1793 IN EFI_IP_ADDRESS
*DestIp
1796 EFI_PXE_BASE_CODE_UDP_PORT SrcPort
;
1797 EFI_PXE_BASE_CODE_UDP_PORT DestPort
;
1798 EFI_PXE_BASE_CODE_MODE
*Mode
;
1799 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
1800 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*Discover
;
1802 EFI_DHCP6_PACKET
*Request
;
1804 EFI_DHCP6_PACKET
*Reply
;
1814 PxeBc
= &Private
->PxeBc
;
1816 Request
= Private
->Dhcp6Request
;
1817 SrcPort
= PXEBC_BS_DISCOVER_PORT
;
1818 DestPort
= PXEBC_BS_DISCOVER_PORT
;
1821 if (!UseBis
&& Layer
!= NULL
) {
1822 *Layer
&= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK
;
1825 if (Request
== NULL
) {
1826 return EFI_DEVICE_ERROR
;
1829 Discover
= AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET
));
1830 if (Discover
== NULL
) {
1831 return EFI_OUT_OF_RESOURCES
;
1835 // Build the discover packet by the cached request packet before.
1837 Xid
= NET_RANDOM (NetRandomInitSeed ());
1838 Discover
->TransactionId
= HTONL (Xid
);
1839 Discover
->MessageType
= Request
->Dhcp6
.Header
.MessageType
;
1840 RequestOpt
= Request
->Dhcp6
.Option
;
1841 DiscoverOpt
= Discover
->DhcpOptions
;
1842 DiscoverLen
= sizeof (EFI_DHCP6_HEADER
);
1843 RequestLen
= DiscoverLen
;
1845 while (RequestLen
< Request
->Length
) {
1846 OpCode
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpCode
);
1847 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpLen
);
1848 if (OpCode
!= EFI_DHCP6_IA_TYPE_NA
&&
1849 OpCode
!= EFI_DHCP6_IA_TYPE_TA
) {
1851 // Copy all the options except IA option.
1853 CopyMem (DiscoverOpt
, RequestOpt
, OpLen
+ 4);
1854 DiscoverOpt
+= (OpLen
+ 4);
1855 DiscoverLen
+= (OpLen
+ 4);
1857 RequestOpt
+= (OpLen
+ 4);
1858 RequestLen
+= (OpLen
+ 4);
1861 Status
= PxeBc
->UdpWrite (
1867 &Private
->StationIp
,
1874 if (EFI_ERROR (Status
)) {
1879 // Cache the right PXE reply packet here, set valid flag later.
1880 // Especially for PXE discover packet, store it into mode data here.
1882 if (Private
->IsDoDiscover
) {
1883 CopyMem (&Mode
->PxeDiscover
.Dhcpv6
, Discover
, DiscoverLen
);
1884 Reply
= &Private
->PxeReply
.Dhcp6
.Packet
.Ack
;
1886 Reply
= &Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
;
1888 ReadSize
= (UINTN
) Reply
->Size
;
1891 // Start Udp6Read instance
1893 Status
= Private
->Udp6Read
->Configure (Private
->Udp6Read
, &Private
->Udp6CfgData
);
1894 if (EFI_ERROR (Status
)) {
1898 Status
= PxeBc
->UdpRead (
1901 &Private
->StationIp
,
1908 (VOID
*) &Reply
->Dhcp6
1911 // Stop Udp6Read instance
1913 Private
->Udp6Read
->Configure (Private
->Udp6Read
, NULL
);
1914 if (EFI_ERROR (Status
)) {
1923 Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
1925 @param[in] Private The pointer to PxeBc private data.
1926 @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL
1928 @retval EFI_SUCCESS The S.A.R.R. process successfully finished.
1929 @retval Others Failed to finish the S.A.R.R. process.
1934 IN PXEBC_PRIVATE_DATA
*Private
,
1935 IN EFI_DHCP6_PROTOCOL
*Dhcp6
1938 EFI_PXE_BASE_CODE_MODE
*PxeMode
;
1939 EFI_DHCP6_CONFIG_DATA Config
;
1940 EFI_DHCP6_MODE_DATA Mode
;
1941 EFI_DHCP6_RETRANSMISSION
*Retransmit
;
1942 EFI_DHCP6_PACKET_OPTION
*OptList
[PXEBC_DHCP6_OPTION_MAX_NUM
];
1943 UINT8 Buffer
[PXEBC_DHCP6_OPTION_MAX_SIZE
];
1946 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1947 EFI_STATUS TimerStatus
;
1949 UINT64 GetMappingTimeOut
;
1951 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits
;
1953 Status
= EFI_SUCCESS
;
1954 PxeMode
= Private
->PxeBc
.Mode
;
1955 Ip6Cfg
= Private
->Ip6Cfg
;
1959 // Build option list for the request packet.
1961 OptCount
= PxeBcBuildDhcp6Options (Private
, OptList
, Buffer
);
1962 ASSERT (OptCount
> 0);
1964 Retransmit
= AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION
));
1965 if (Retransmit
== NULL
) {
1966 return EFI_OUT_OF_RESOURCES
;
1969 ZeroMem (&Mode
, sizeof (EFI_DHCP6_MODE_DATA
));
1970 ZeroMem (&Config
, sizeof (EFI_DHCP6_CONFIG_DATA
));
1972 Config
.OptionCount
= OptCount
;
1973 Config
.OptionList
= OptList
;
1974 Config
.Dhcp6Callback
= PxeBcDhcp6CallBack
;
1975 Config
.CallbackContext
= Private
;
1976 Config
.IaInfoEvent
= NULL
;
1977 Config
.RapidCommit
= FALSE
;
1978 Config
.ReconfigureAccept
= FALSE
;
1979 Config
.IaDescriptor
.IaId
= Private
->IaId
;
1980 Config
.IaDescriptor
.Type
= EFI_DHCP6_IA_TYPE_NA
;
1981 Config
.SolicitRetransmission
= Retransmit
;
1982 Retransmit
->Irt
= 4;
1983 Retransmit
->Mrc
= 4;
1984 Retransmit
->Mrt
= 32;
1985 Retransmit
->Mrd
= 60;
1988 // Configure the DHCPv6 instance for PXE boot.
1990 Status
= Dhcp6
->Configure (Dhcp6
, &Config
);
1991 FreePool (Retransmit
);
1992 if (EFI_ERROR (Status
)) {
1997 // Initialize the record fields for DHCPv6 offer in private data.
1999 Private
->IsProxyRecved
= FALSE
;
2000 Private
->OfferNum
= 0;
2001 Private
->SelectIndex
= 0;
2002 ZeroMem (Private
->OfferCount
, sizeof (Private
->OfferCount
));
2003 ZeroMem (Private
->OfferIndex
, sizeof (Private
->OfferIndex
));
2007 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
2009 Status
= Dhcp6
->Start (Dhcp6
);
2010 if (Status
== EFI_NO_MAPPING
) {
2012 // IP6 Linklocal address is not available for use, so stop current Dhcp process
2013 // and wait for duplicate address detection to finish.
2015 Dhcp6
->Stop (Dhcp6
);
2018 // Get Duplicate Address Detection Transmits count.
2020 DataSize
= sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS
);
2021 Status
= Ip6Cfg
->GetData (
2023 Ip6ConfigDataTypeDupAddrDetectTransmits
,
2027 if (EFI_ERROR (Status
)) {
2028 Dhcp6
->Configure (Dhcp6
, NULL
);
2032 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
2033 if (EFI_ERROR (Status
)) {
2034 Dhcp6
->Configure (Dhcp6
, NULL
);
2038 GetMappingTimeOut
= TICKS_PER_SECOND
* DadXmits
.DupAddrDetectTransmits
+ PXEBC_DAD_ADDITIONAL_DELAY
;
2039 Status
= gBS
->SetTimer (Timer
, TimerRelative
, GetMappingTimeOut
);
2040 if (EFI_ERROR (Status
)) {
2041 gBS
->CloseEvent (Timer
);
2042 Dhcp6
->Configure (Dhcp6
, NULL
);
2048 TimerStatus
= gBS
->CheckEvent (Timer
);
2049 if (!EFI_ERROR (TimerStatus
)) {
2050 Status
= Dhcp6
->Start (Dhcp6
);
2052 } while (TimerStatus
== EFI_NOT_READY
);
2054 gBS
->CloseEvent (Timer
);
2056 if (EFI_ERROR (Status
)) {
2057 if (Status
== EFI_ICMP_ERROR
) {
2058 PxeMode
->IcmpErrorReceived
= TRUE
;
2060 Dhcp6
->Configure (Dhcp6
, NULL
);
2065 // Get the acquired IPv6 address and store them.
2067 Status
= Dhcp6
->GetModeData (Dhcp6
, &Mode
, NULL
);
2068 if (EFI_ERROR (Status
)) {
2069 Dhcp6
->Stop (Dhcp6
);
2073 ASSERT (Mode
.Ia
->State
== Dhcp6Bound
);
2075 // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the
2076 // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when
2077 // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as
2078 // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery
2079 // to find a valid router address.
2081 CopyMem (&Private
->TmpStationIp
.v6
, &Mode
.Ia
->IaAddress
[0].IpAddress
, sizeof (EFI_IPv6_ADDRESS
));
2084 // Check the selected offer whether BINL retry is needed.
2086 Status
= PxeBcHandleDhcp6Offer (Private
);
2087 if (EFI_ERROR (Status
)) {
2088 Dhcp6
->Stop (Dhcp6
);