2 Functions implementation related with DHCPv6 for UefiPxeBc Driver.
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2017, 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 (DHCP6_OPT_ORO
);
96 OptList
[Index
]->OpLen
= HTONS (8);
97 OptEnt
.Oro
= (PXEBC_DHCP6_OPTION_ORO
*) OptList
[Index
]->Data
;
98 OptEnt
.Oro
->OpCode
[0] = HTONS(DHCP6_OPT_BOOT_FILE_URL
);
99 OptEnt
.Oro
->OpCode
[1] = HTONS(DHCP6_OPT_BOOT_FILE_PARAM
);
100 OptEnt
.Oro
->OpCode
[2] = HTONS(DHCP6_OPT_DNS_SERVERS
);
101 OptEnt
.Oro
->OpCode
[3] = HTONS(DHCP6_OPT_VENDOR_CLASS
);
103 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
106 // Append client network device interface option
108 OptList
[Index
]->OpCode
= HTONS (DHCP6_OPT_UNDI
);
109 OptList
[Index
]->OpLen
= HTONS ((UINT16
)3);
110 OptEnt
.Undi
= (PXEBC_DHCP6_OPTION_UNDI
*) OptList
[Index
]->Data
;
112 if (Private
->Nii
!= NULL
) {
113 OptEnt
.Undi
->Type
= Private
->Nii
->Type
;
114 OptEnt
.Undi
->MajorVer
= Private
->Nii
->MajorVer
;
115 OptEnt
.Undi
->MinorVer
= Private
->Nii
->MinorVer
;
117 OptEnt
.Undi
->Type
= DEFAULT_UNDI_TYPE
;
118 OptEnt
.Undi
->MajorVer
= DEFAULT_UNDI_MAJOR
;
119 OptEnt
.Undi
->MinorVer
= DEFAULT_UNDI_MINOR
;
123 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
126 // Append client system architecture option
128 OptList
[Index
]->OpCode
= HTONS (DHCP6_OPT_ARCH
);
129 OptList
[Index
]->OpLen
= HTONS ((UINT16
) sizeof (PXEBC_DHCP6_OPTION_ARCH
));
130 OptEnt
.Arch
= (PXEBC_DHCP6_OPTION_ARCH
*) OptList
[Index
]->Data
;
131 Value
= HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE
);
132 CopyMem (&OptEnt
.Arch
->Type
, &Value
, sizeof (UINT16
));
134 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
137 // Append vendor class option to store the PXE class identifier.
139 OptList
[Index
]->OpCode
= HTONS (DHCP6_OPT_VENDOR_CLASS
);
140 OptList
[Index
]->OpLen
= HTONS ((UINT16
) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS
));
141 OptEnt
.VendorClass
= (PXEBC_DHCP6_OPTION_VENDOR_CLASS
*) OptList
[Index
]->Data
;
142 OptEnt
.VendorClass
->Vendor
= HTONL (PXEBC_DHCP6_ENTERPRISE_NUM
);
143 OptEnt
.VendorClass
->ClassLen
= HTONS ((UINT16
) sizeof (PXEBC_CLASS_ID
));
145 &OptEnt
.VendorClass
->ClassId
,
146 DEFAULT_CLASS_ID_DATA
,
147 sizeof (PXEBC_CLASS_ID
)
149 PxeBcUintnToAscDecWithFormat (
150 EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE
,
151 OptEnt
.VendorClass
->ClassId
.ArchitectureType
,
152 sizeof (OptEnt
.VendorClass
->ClassId
.ArchitectureType
)
155 if (Private
->Nii
!= NULL
) {
157 OptEnt
.VendorClass
->ClassId
.InterfaceName
,
158 Private
->Nii
->StringId
,
159 sizeof (OptEnt
.VendorClass
->ClassId
.InterfaceName
)
161 PxeBcUintnToAscDecWithFormat (
162 Private
->Nii
->MajorVer
,
163 OptEnt
.VendorClass
->ClassId
.UndiMajor
,
164 sizeof (OptEnt
.VendorClass
->ClassId
.UndiMajor
)
166 PxeBcUintnToAscDecWithFormat (
167 Private
->Nii
->MinorVer
,
168 OptEnt
.VendorClass
->ClassId
.UndiMinor
,
169 sizeof (OptEnt
.VendorClass
->ClassId
.UndiMinor
)
180 Cache the DHCPv6 packet.
182 @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.
183 @param[in] Src The pointer to the DHCPv6 packet to be cached.
185 @retval EFI_SUCCESS Packet is copied.
186 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
190 PxeBcCacheDhcp6Packet (
191 IN EFI_DHCP6_PACKET
*Dst
,
192 IN EFI_DHCP6_PACKET
*Src
195 if (Dst
->Size
< Src
->Length
) {
196 return EFI_BUFFER_TOO_SMALL
;
199 CopyMem (&Dst
->Dhcp6
, &Src
->Dhcp6
, Src
->Length
);
200 Dst
->Length
= Src
->Length
;
207 Free all the nodes in the list for boot file.
209 @param[in] Head The pointer to the head of list.
213 PxeBcFreeBootFileOption (
218 LIST_ENTRY
*NextEntry
;
219 PXEBC_DHCP6_OPTION_NODE
*Node
;
221 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, Head
) {
222 Node
= NET_LIST_USER_STRUCT (Entry
, PXEBC_DHCP6_OPTION_NODE
, Link
);
223 RemoveEntryList (Entry
);
229 Retrieve the boot server address using the EFI_DNS6_PROTOCOL.
231 @param[in] Private Pointer to PxeBc private data.
232 @param[in] HostName Pointer to buffer containing hostname.
233 @param[out] IpAddress On output, pointer to buffer containing IPv6 address.
235 @retval EFI_SUCCESS Operation succeeded.
236 @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
237 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
238 @retval Others Other errors as indicated.
243 IN PXEBC_PRIVATE_DATA
*Private
,
245 OUT EFI_IPv6_ADDRESS
*IpAddress
249 EFI_DNS6_PROTOCOL
*Dns6
;
250 EFI_DNS6_CONFIG_DATA Dns6ConfigData
;
251 EFI_DNS6_COMPLETION_TOKEN Token
;
252 EFI_HANDLE Dns6Handle
;
253 EFI_IPv6_ADDRESS
*DnsServerList
;
258 DnsServerList
= Private
->DnsServer
;
259 ZeroMem (&Token
, sizeof (EFI_DNS6_COMPLETION_TOKEN
));
262 // Create a DNSv6 child instance and get the protocol.
264 Status
= NetLibCreateServiceChild (
267 &gEfiDns6ServiceBindingProtocolGuid
,
270 if (EFI_ERROR (Status
)) {
274 Status
= gBS
->OpenProtocol (
276 &gEfiDns6ProtocolGuid
,
280 EFI_OPEN_PROTOCOL_BY_DRIVER
282 if (EFI_ERROR (Status
)) {
287 // Configure DNS6 instance for the DNS server address and protocol.
289 ZeroMem (&Dns6ConfigData
, sizeof (EFI_DNS6_CONFIG_DATA
));
290 Dns6ConfigData
.DnsServerCount
= 1;
291 Dns6ConfigData
.DnsServerList
= DnsServerList
;
292 Dns6ConfigData
.EnableDnsCache
= TRUE
;
293 Dns6ConfigData
.Protocol
= EFI_IP_PROTO_UDP
;
294 IP6_COPY_ADDRESS (&Dns6ConfigData
.StationIp
, &Private
->TmpStationIp
.v6
);
295 Status
= Dns6
->Configure (
299 if (EFI_ERROR (Status
)) {
303 Token
.Status
= EFI_NOT_READY
;
306 // Create event to set the IsDone flag when name resolution is finished.
308 Status
= gBS
->CreateEvent (
315 if (EFI_ERROR (Status
)) {
320 // Start asynchronous name resolution.
322 Status
= Dns6
->HostNameToIp (Dns6
, HostName
, &Token
);
323 if (EFI_ERROR (Status
)) {
332 // Name resolution is done, check result.
334 Status
= Token
.Status
;
335 if (!EFI_ERROR (Status
)) {
336 if (Token
.RspData
.H2AData
== NULL
) {
337 Status
= EFI_DEVICE_ERROR
;
340 if (Token
.RspData
.H2AData
->IpCount
== 0 || Token
.RspData
.H2AData
->IpList
== NULL
) {
341 Status
= EFI_DEVICE_ERROR
;
345 // We just return the first IPv6 address from DNS protocol.
347 IP6_COPY_ADDRESS (IpAddress
, Token
.RspData
.H2AData
->IpList
);
348 Status
= EFI_SUCCESS
;
354 if (Token
.Event
!= NULL
) {
355 gBS
->CloseEvent (Token
.Event
);
357 if (Token
.RspData
.H2AData
!= NULL
) {
358 if (Token
.RspData
.H2AData
->IpList
!= NULL
) {
359 FreePool (Token
.RspData
.H2AData
->IpList
);
361 FreePool (Token
.RspData
.H2AData
);
365 Dns6
->Configure (Dns6
, NULL
);
369 &gEfiDns6ProtocolGuid
,
375 if (Dns6Handle
!= NULL
) {
376 NetLibDestroyServiceChild (
379 &gEfiDns6ServiceBindingProtocolGuid
,
384 if (DnsServerList
!= NULL
) {
385 FreePool (DnsServerList
);
392 Parse the Boot File URL option.
394 @param[in] Private Pointer to PxeBc private data.
395 @param[out] FileName The pointer to the boot file name.
396 @param[in, out] SrvAddr The pointer to the boot server address.
397 @param[in] BootFile The pointer to the boot file URL option data.
398 @param[in] Length The length of the boot file URL option data.
400 @retval EFI_ABORTED User cancel operation.
401 @retval EFI_SUCCESS Selected the boot menu successfully.
402 @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
406 PxeBcExtractBootFileUrl (
407 IN PXEBC_PRIVATE_DATA
*Private
,
408 OUT UINT8
**FileName
,
409 IN OUT EFI_IPv6_ADDRESS
*SrvAddr
,
415 CHAR8
*BootFileNamePtr
;
417 UINT16 BootFileNameLen
;
420 CHAR8
*ServerAddressOption
;
421 CHAR8
*ServerAddress
;
424 BOOLEAN IpExpressedUrl
;
428 IpExpressedUrl
= TRUE
;
430 // The format of the Boot File URL option is:
433 // 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
434 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
435 // | OPT_BOOTFILE_URL | option-len |
436 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
438 // . bootfile-url (variable length) .
440 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
444 // Based upon RFC 5970 and UEFI 2.6, bootfile-url format can be
445 // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME or tftp://domain_name/BOOTFILE_NAME
446 // As an example where the BOOTFILE_NAME is the EFI loader and
447 // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.
449 PrefixLen
= (UINT16
) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX
);
451 if (Length
<= PrefixLen
||
452 CompareMem (BootFile
, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX
, PrefixLen
) != 0) {
453 return EFI_NOT_FOUND
;
456 BootFile
= BootFile
+ PrefixLen
;
457 Length
= (UINT16
) (Length
- PrefixLen
);
459 TmpStr
= (CHAR8
*) AllocateZeroPool (Length
+ 1);
460 if (TmpStr
== NULL
) {
461 return EFI_OUT_OF_RESOURCES
;
464 CopyMem (TmpStr
, BootFile
, Length
);
465 TmpStr
[Length
] = '\0';
468 // Get the part of SERVER_ADDRESS string.
470 ServerAddressOption
= TmpStr
;
471 if (*ServerAddressOption
== PXEBC_ADDR_START_DELIMITER
) {
472 ServerAddressOption
++;
473 ServerAddress
= ServerAddressOption
;
474 while (*ServerAddress
!= '\0' && *ServerAddress
!= PXEBC_ADDR_END_DELIMITER
) {
478 if (*ServerAddress
!= PXEBC_ADDR_END_DELIMITER
) {
480 return EFI_INVALID_PARAMETER
;
483 *ServerAddress
= '\0';
486 // Convert the string of server address to Ipv6 address format and store it.
488 Status
= NetLibAsciiStrToIp6 (ServerAddressOption
, SrvAddr
);
489 if (EFI_ERROR (Status
)) {
495 IpExpressedUrl
= FALSE
;
496 ServerAddress
= ServerAddressOption
;
497 while (*ServerAddress
!= '\0' && *ServerAddress
!= PXEBC_TFTP_URL_SEPARATOR
) {
501 if (*ServerAddress
!= PXEBC_TFTP_URL_SEPARATOR
) {
503 return EFI_INVALID_PARAMETER
;
505 *ServerAddress
= '\0';
507 Len
= AsciiStrSize (ServerAddressOption
);
508 HostName
= AllocateZeroPool (Len
* sizeof (CHAR16
));
509 if (HostName
== NULL
) {
511 return EFI_OUT_OF_RESOURCES
;
513 AsciiStrToUnicodeStrS (
520 // Perform DNS resolution.
522 Status
= PxeBcDns6 (Private
,HostName
, SrvAddr
);
523 if (EFI_ERROR (Status
)) {
530 // Get the part of BOOTFILE_NAME string.
532 BootFileNamePtr
= (CHAR8
*)((UINTN
)ServerAddress
+ 1);
533 if (IpExpressedUrl
) {
534 if (*BootFileNamePtr
!= PXEBC_TFTP_URL_SEPARATOR
) {
536 return EFI_INVALID_PARAMETER
;
541 BootFileNameLen
= (UINT16
)(Length
- (UINT16
) ((UINTN
)BootFileNamePtr
- (UINTN
)TmpStr
) + 1);
542 if (BootFileNameLen
!= 0 || FileName
!= NULL
) {
544 // Remove trailing mode=octet if present and ignore. All other modes are
545 // invalid for netboot6, so reject them.
547 ModeStr
= AsciiStrStr (BootFileNamePtr
, ";mode=octet");
548 if (ModeStr
!= NULL
&& *(ModeStr
+ AsciiStrLen (";mode=octet")) == '\0') {
550 } else if (AsciiStrStr (BootFileNamePtr
, ";mode=") != NULL
) {
551 return EFI_INVALID_PARAMETER
;
555 // Extract boot file name from URL.
557 BootFileName
= (CHAR8
*) AllocateZeroPool (BootFileNameLen
);
558 if (BootFileName
== NULL
) {
560 return EFI_OUT_OF_RESOURCES
;
562 *FileName
= (UINT8
*) BootFileName
;
565 // Decode percent-encoding in boot file name.
567 while (*BootFileNamePtr
!= '\0') {
568 if (*BootFileNamePtr
== '%') {
569 TmpChar
= *(BootFileNamePtr
+ 3);
570 *(BootFileNamePtr
+ 3) = '\0';
571 *BootFileName
= (UINT8
) AsciiStrHexToUintn ((CHAR8
*)(BootFileNamePtr
+ 1));
573 *(BootFileNamePtr
+ 3) = TmpChar
;
574 BootFileNamePtr
+= 3;
576 *BootFileName
= *BootFileNamePtr
;
581 *BootFileName
= '\0';
591 Parse the Boot File Parameter option.
593 @param[in] BootFilePara The pointer to boot file parameter option data.
594 @param[out] BootFileSize The pointer to the parsed boot file size.
596 @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option.
597 @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option.
601 PxeBcExtractBootFileParam (
602 IN CHAR8
*BootFilePara
,
603 OUT UINT16
*BootFileSize
611 CopyMem (&Length
, BootFilePara
, sizeof (UINT16
));
612 Length
= NTOHS (Length
);
615 // The BootFile Size should be 1~5 byte ASCII strings
617 if (Length
< 1 || Length
> 5) {
618 return EFI_NOT_FOUND
;
622 // Extract the value of BootFile Size.
624 BootFilePara
= BootFilePara
+ sizeof (UINT16
);
626 for (Index
= 0; Index
< Length
; Index
++) {
627 if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit
, *(BootFilePara
+ Index
)))) {
628 return EFI_NOT_FOUND
;
631 Size
= (Size
+ Digit
) * 10;
635 if (Size
> PXEBC_DHCP6_MAX_BOOT_FILE_SIZE
) {
636 return EFI_NOT_FOUND
;
639 *BootFileSize
= (UINT16
) Size
;
645 Parse the cached DHCPv6 packet, including all the options.
647 @param[in] Cache6 The pointer to a cached DHCPv6 packet.
649 @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
650 @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.
654 PxeBcParseDhcp6Packet (
655 IN PXEBC_DHCP6_PACKET_CACHE
*Cache6
658 EFI_DHCP6_PACKET
*Offer
;
659 EFI_DHCP6_PACKET_OPTION
**Options
;
660 EFI_DHCP6_PACKET_OPTION
*Option
;
661 PXEBC_OFFER_TYPE OfferType
;
662 BOOLEAN IsProxyOffer
;
666 UINT32 EnterpriseNum
;
670 Offer
= &Cache6
->Packet
.Offer
;
671 Options
= Cache6
->OptList
;
673 ZeroMem (Cache6
->OptList
, sizeof (Cache6
->OptList
));
675 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Offer
->Dhcp6
.Option
);
677 Length
= GET_DHCP6_OPTION_SIZE (Offer
);
680 // OpLen and OpCode here are both stored in network order, since they are from original packet.
682 while (Offset
< Length
) {
684 if (NTOHS (Option
->OpCode
) == DHCP6_OPT_IA_NA
) {
685 Options
[PXEBC_DHCP6_IDX_IA_NA
] = Option
;
686 } else if (NTOHS (Option
->OpCode
) == DHCP6_OPT_BOOT_FILE_URL
) {
688 // The server sends this option to inform the client about an URL to a boot file.
690 Options
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] = Option
;
691 } else if (NTOHS (Option
->OpCode
) == DHCP6_OPT_BOOT_FILE_PARAM
) {
692 Options
[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM
] = Option
;
693 } else if (NTOHS (Option
->OpCode
) == DHCP6_OPT_VENDOR_CLASS
) {
694 Options
[PXEBC_DHCP6_IDX_VENDOR_CLASS
] = Option
;
695 } else if (NTOHS (Option
->OpCode
) == DHCP6_OPT_DNS_SERVERS
) {
696 Options
[PXEBC_DHCP6_IDX_DNS_SERVER
] = Option
;
699 Offset
+= (NTOHS (Option
->OpLen
) + 4);
700 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Offer
->Dhcp6
.Option
+ Offset
);
704 // The offer with assigned client address is NOT a proxy offer.
705 // An ia_na option, embeded with valid ia_addr option and a status_code of success.
707 Option
= Options
[PXEBC_DHCP6_IDX_IA_NA
];
708 if (Option
!= NULL
) {
709 Option
= PxeBcParseDhcp6Options (
711 NTOHS (Option
->OpLen
),
712 DHCP6_OPT_STATUS_CODE
714 if ((Option
!= NULL
&& Option
->Data
[0] == 0) || (Option
== NULL
)) {
715 IsProxyOffer
= FALSE
;
720 // The offer with "PXEClient" is a pxe offer.
722 Option
= Options
[PXEBC_DHCP6_IDX_VENDOR_CLASS
];
723 EnterpriseNum
= HTONL(PXEBC_DHCP6_ENTERPRISE_NUM
);
725 if (Option
!= NULL
&&
726 NTOHS(Option
->OpLen
) >= 13 &&
727 CompareMem (Option
->Data
, &EnterpriseNum
, sizeof (UINT32
)) == 0 &&
728 CompareMem (&Option
->Data
[6], DEFAULT_CLASS_ID_DATA
, 9) == 0) {
733 // Determine offer type of the dhcp6 packet.
737 // It's a binl offer only with PXEClient.
739 OfferType
= IsProxyOffer
? PxeOfferTypeProxyBinl
: PxeOfferTypeDhcpBinl
;
742 // It's a dhcp only offer, which is a pure dhcp6 offer packet.
744 OfferType
= PxeOfferTypeDhcpOnly
;
747 Cache6
->OfferType
= OfferType
;
754 Cache the DHCPv6 ack packet, and parse it on demand.
756 @param[in] Private The pointer to PxeBc private data.
757 @param[in] Ack The pointer to the DHCPv6 ack packet.
758 @param[in] Verified If TRUE, parse the ACK packet and store info into mode data.
760 @retval EFI_SUCCESS Cache and parse the packet successfully.
761 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
766 IN PXEBC_PRIVATE_DATA
*Private
,
767 IN EFI_DHCP6_PACKET
*Ack
,
771 EFI_PXE_BASE_CODE_MODE
*Mode
;
774 Mode
= Private
->PxeBc
.Mode
;
776 Status
= PxeBcCacheDhcp6Packet (&Private
->DhcpAck
.Dhcp6
.Packet
.Ack
, Ack
);
777 if (EFI_ERROR (Status
)) {
783 // Parse the ack packet and store it into mode data if needed.
785 PxeBcParseDhcp6Packet (&Private
->DhcpAck
.Dhcp6
);
786 CopyMem (&Mode
->DhcpAck
.Dhcpv6
, &Ack
->Dhcp6
, Ack
->Length
);
787 Mode
->DhcpAckReceived
= TRUE
;
795 Cache the DHCPv6 proxy offer packet according to the received order.
797 @param[in] Private The pointer to PxeBc private data.
798 @param[in] OfferIndex The received order of offer packets.
800 @retval EFI_SUCCESS Cache and parse the packet successfully.
801 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
805 PxeBcCopyDhcp6Proxy (
806 IN PXEBC_PRIVATE_DATA
*Private
,
810 EFI_PXE_BASE_CODE_MODE
*Mode
;
811 EFI_DHCP6_PACKET
*Offer
;
814 ASSERT (OfferIndex
< Private
->OfferNum
);
815 ASSERT (OfferIndex
< PXEBC_OFFER_MAX_NUM
);
817 Mode
= Private
->PxeBc
.Mode
;
818 Offer
= &Private
->OfferBuffer
[OfferIndex
].Dhcp6
.Packet
.Offer
;
821 // Cache the proxy offer packet and parse it.
823 Status
= PxeBcCacheDhcp6Packet (&Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
, Offer
);
824 if (EFI_ERROR(Status
)) {
827 PxeBcParseDhcp6Packet (&Private
->ProxyOffer
.Dhcp6
);
830 // Store this packet into mode data.
832 CopyMem (&Mode
->ProxyOffer
.Dhcpv6
, &Offer
->Dhcp6
, Offer
->Length
);
833 Mode
->ProxyOfferReceived
= TRUE
;
839 Seek the address of the first byte of the option header.
841 @param[in] Buf The pointer to the buffer.
842 @param[in] SeekLen The length to seek.
843 @param[in] OptType The option type.
845 @retval NULL If it failed to seek the option.
846 @retval others The position to the option.
850 PxeBcDhcp6SeekOption (
864 while (Cursor
< Buf
+ SeekLen
) {
865 OpCode
= ReadUnaligned16 ((UINT16
*) Cursor
);
866 if (OpCode
== HTONS (OptType
)) {
870 DataLen
= NTOHS (ReadUnaligned16 ((UINT16
*) (Cursor
+ 2)));
871 Cursor
+= (DataLen
+ 4);
879 Build and send out the request packet for the bootfile, and parse the reply.
881 @param[in] Private The pointer to PxeBc private data.
882 @param[in] Index PxeBc option boot item type.
884 @retval EFI_SUCCESS Successfully discovered the boot file.
885 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
886 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
887 @retval Others Failed to discover the boot file.
891 PxeBcRequestBootService (
892 IN PXEBC_PRIVATE_DATA
*Private
,
896 EFI_PXE_BASE_CODE_UDP_PORT SrcPort
;
897 EFI_PXE_BASE_CODE_UDP_PORT DestPort
;
898 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
899 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*Discover
;
901 EFI_DHCP6_PACKET
*Request
;
903 EFI_DHCP6_PACKET
*Reply
;
911 EFI_DHCP6_PACKET
*IndexOffer
;
914 PxeBc
= &Private
->PxeBc
;
915 Request
= Private
->Dhcp6Request
;
916 IndexOffer
= &Private
->OfferBuffer
[Index
].Dhcp6
.Packet
.Offer
;
917 SrcPort
= PXEBC_BS_DISCOVER_PORT
;
918 DestPort
= PXEBC_BS_DISCOVER_PORT
;
921 if (Request
== NULL
) {
922 return EFI_DEVICE_ERROR
;
925 Discover
= AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET
));
926 if (Discover
== NULL
) {
927 return EFI_OUT_OF_RESOURCES
;
931 // Build the request packet by the cached request packet before.
933 Discover
->TransactionId
= IndexOffer
->Dhcp6
.Header
.TransactionId
;
934 Discover
->MessageType
= Request
->Dhcp6
.Header
.MessageType
;
935 RequestOpt
= Request
->Dhcp6
.Option
;
936 DiscoverOpt
= Discover
->DhcpOptions
;
937 DiscoverLen
= sizeof (EFI_DHCP6_HEADER
);
938 RequestLen
= DiscoverLen
;
941 // Find Server ID Option from ProxyOffer.
943 if (Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
== PxeOfferTypeProxyBinl
) {
944 Option
= PxeBcDhcp6SeekOption (
945 IndexOffer
->Dhcp6
.Option
,
946 IndexOffer
->Length
- 4,
949 if (Option
== NULL
) {
950 return EFI_NOT_FOUND
;
954 // Add Server ID Option.
956 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) Option
)->OpLen
);
957 CopyMem (DiscoverOpt
, Option
, OpLen
+ 4);
958 DiscoverOpt
+= (OpLen
+ 4);
959 DiscoverLen
+= (OpLen
+ 4);
962 while (RequestLen
< Request
->Length
) {
963 OpCode
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpCode
);
964 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpLen
);
965 if (OpCode
!= EFI_DHCP6_IA_TYPE_NA
&&
966 OpCode
!= EFI_DHCP6_IA_TYPE_TA
&&
967 OpCode
!= DHCP6_OPT_SERVER_ID
970 // Copy all the options except IA option and Server ID
972 CopyMem (DiscoverOpt
, RequestOpt
, OpLen
+ 4);
973 DiscoverOpt
+= (OpLen
+ 4);
974 DiscoverLen
+= (OpLen
+ 4);
976 RequestOpt
+= (OpLen
+ 4);
977 RequestLen
+= (OpLen
+ 4);
981 // Update Elapsed option in the package
983 Option
= PxeBcDhcp6SeekOption (
984 Discover
->DhcpOptions
,
985 (UINT32
)(RequestLen
- 4),
986 DHCP6_OPT_ELAPSED_TIME
988 if (Option
!= NULL
) {
989 CalcElapsedTime (Private
);
990 WriteUnaligned16 ((UINT16
*)(Option
+ 4), HTONS((UINT16
) Private
->ElapsedTime
));
993 Status
= PxeBc
->UdpWrite (
1007 if (EFI_ERROR (Status
)) {
1012 // Cache the right PXE reply packet here, set valid flag later.
1013 // Especially for PXE discover packet, store it into mode data here.
1015 Reply
= &Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
;
1016 ReadSize
= (UINTN
) Reply
->Size
;
1019 // Start Udp6Read instance
1021 Status
= Private
->Udp6Read
->Configure (Private
->Udp6Read
, &Private
->Udp6CfgData
);
1022 if (EFI_ERROR (Status
)) {
1026 Status
= PxeBc
->UdpRead (
1028 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
| EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP
,
1036 (VOID
*) &Reply
->Dhcp6
1039 // Stop Udp6Read instance
1041 Private
->Udp6Read
->Configure (Private
->Udp6Read
, NULL
);
1043 if (EFI_ERROR (Status
)) {
1050 Reply
->Length
= (UINT32
) ReadSize
;
1057 Retry to request bootfile name by the BINL offer.
1059 @param[in] Private The pointer to PxeBc private data.
1060 @param[in] Index The received order of offer packets.
1062 @retval EFI_SUCCESS Successfully retried a request for the bootfile name.
1063 @retval EFI_DEVICE_ERROR Failed to retry the bootfile name.
1067 PxeBcRetryDhcp6Binl (
1068 IN PXEBC_PRIVATE_DATA
*Private
,
1072 EFI_PXE_BASE_CODE_MODE
*Mode
;
1073 PXEBC_DHCP6_PACKET_CACHE
*Offer
;
1074 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
1077 ASSERT (Index
< PXEBC_OFFER_MAX_NUM
);
1078 ASSERT (Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
== PxeOfferTypeDhcpBinl
||
1079 Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
== PxeOfferTypeProxyBinl
);
1081 Mode
= Private
->PxeBc
.Mode
;
1082 Private
->IsDoDiscover
= FALSE
;
1083 Offer
= &Private
->OfferBuffer
[Index
].Dhcp6
;
1084 if (Offer
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] == NULL
) {
1086 // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.
1089 &Private
->ServerIp
.v6
,
1090 &mAllDhcpRelayAndServersAddress
,
1091 sizeof (EFI_IPv6_ADDRESS
)
1094 ASSERT (Offer
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] != NULL
);
1096 // Parse out the next server address from the last offer, and store it
1098 Status
= PxeBcExtractBootFileUrl (
1100 &Private
->BootFileName
,
1101 &Private
->ServerIp
.v6
,
1102 (CHAR8
*) (Offer
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
]->Data
),
1103 NTOHS (Offer
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
]->OpLen
)
1105 if (EFI_ERROR (Status
)) {
1111 // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
1113 Status
= PxeBcRequestBootService (Private
, Index
);
1115 if (EFI_ERROR (Status
)) {
1119 Cache6
= &Private
->ProxyOffer
.Dhcp6
;
1120 Status
= PxeBcParseDhcp6Packet (Cache6
);
1121 if (EFI_ERROR (Status
)) {
1125 if (Cache6
->OfferType
!= PxeOfferTypeProxyPxe10
&&
1126 Cache6
->OfferType
!= PxeOfferTypeProxyWfm11a
&&
1127 Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] == NULL
) {
1129 // This BINL ack doesn't have discovery option set or multicast option set
1130 // or bootfile name specified.
1132 return EFI_DEVICE_ERROR
;
1135 Mode
->ProxyOfferReceived
= TRUE
;
1137 &Mode
->ProxyOffer
.Dhcpv6
,
1138 &Cache6
->Packet
.Offer
.Dhcp6
,
1139 Cache6
->Packet
.Offer
.Length
1147 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
1149 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1150 @param[in] RcvdOffer The pointer to the received offer packet.
1152 @retval EFI_SUCCESS Cache and parse the packet successfully.
1153 @retval Others Operation failed.
1156 PxeBcCacheDhcp6Offer (
1157 IN PXEBC_PRIVATE_DATA
*Private
,
1158 IN EFI_DHCP6_PACKET
*RcvdOffer
1161 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
1162 EFI_DHCP6_PACKET
*Offer
;
1163 PXEBC_OFFER_TYPE OfferType
;
1166 Cache6
= &Private
->OfferBuffer
[Private
->OfferNum
].Dhcp6
;
1167 Offer
= &Cache6
->Packet
.Offer
;
1170 // Cache the content of DHCPv6 packet firstly.
1172 Status
= PxeBcCacheDhcp6Packet (Offer
, RcvdOffer
);
1173 if (EFI_ERROR (Status
)) {
1178 // Validate the DHCPv6 packet, and parse the options and offer type.
1180 if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6
))) {
1185 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
1187 OfferType
= Cache6
->OfferType
;
1188 ASSERT (OfferType
< PxeOfferTypeMax
);
1189 ASSERT (Private
->OfferCount
[OfferType
] < PXEBC_OFFER_MAX_NUM
);
1191 if (IS_PROXY_OFFER (OfferType
)) {
1193 // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
1195 Private
->IsProxyRecved
= TRUE
;
1197 if (OfferType
== PxeOfferTypeProxyBinl
) {
1199 // Cache all proxy BINL offers.
1201 Private
->OfferIndex
[OfferType
][Private
->OfferCount
[OfferType
]] = Private
->OfferNum
;
1202 Private
->OfferCount
[OfferType
]++;
1203 } else if ((OfferType
== PxeOfferTypeProxyPxe10
|| OfferType
== PxeOfferTypeProxyWfm11a
) &&
1204 Private
->OfferCount
[OfferType
] < 1) {
1206 // Only cache the first PXE10/WFM11a offer, and discard the others.
1208 Private
->OfferIndex
[OfferType
][0] = Private
->OfferNum
;
1209 Private
->OfferCount
[OfferType
] = 1;
1215 // It's a DHCPv6 offer with yiaddr, and cache them all.
1217 Private
->OfferIndex
[OfferType
][Private
->OfferCount
[OfferType
]] = Private
->OfferNum
;
1218 Private
->OfferCount
[OfferType
]++;
1221 Private
->OfferNum
++;
1228 Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
1230 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1234 PxeBcSelectDhcp6Offer (
1235 IN PXEBC_PRIVATE_DATA
*Private
1240 PXEBC_OFFER_TYPE OfferType
;
1242 Private
->SelectIndex
= 0;
1244 if (Private
->IsOfferSorted
) {
1246 // Select offer by default policy.
1248 if (Private
->OfferCount
[PxeOfferTypeDhcpPxe10
] > 0) {
1250 // 1. DhcpPxe10 offer
1252 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpPxe10
][0] + 1;
1254 } else if (Private
->OfferCount
[PxeOfferTypeDhcpWfm11a
] > 0) {
1256 // 2. DhcpWfm11a offer
1258 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpWfm11a
][0] + 1;
1260 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1261 Private
->OfferCount
[PxeOfferTypeProxyPxe10
] > 0) {
1263 // 3. DhcpOnly offer and ProxyPxe10 offer.
1265 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1266 Private
->SelectProxyType
= PxeOfferTypeProxyPxe10
;
1268 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1269 Private
->OfferCount
[PxeOfferTypeProxyWfm11a
] > 0) {
1271 // 4. DhcpOnly offer and ProxyWfm11a offer.
1273 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1274 Private
->SelectProxyType
= PxeOfferTypeProxyWfm11a
;
1276 } else if (Private
->OfferCount
[PxeOfferTypeDhcpBinl
] > 0) {
1278 // 5. DhcpBinl offer.
1280 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpBinl
][0] + 1;
1282 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1283 Private
->OfferCount
[PxeOfferTypeProxyBinl
] > 0) {
1285 // 6. DhcpOnly offer and ProxyBinl offer.
1287 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1288 Private
->SelectProxyType
= PxeOfferTypeProxyBinl
;
1292 // 7. DhcpOnly offer with bootfilename.
1294 for (Index
= 0; Index
< Private
->OfferCount
[PxeOfferTypeDhcpOnly
]; Index
++) {
1295 OfferIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][Index
];
1296 if (Private
->OfferBuffer
[OfferIndex
].Dhcp6
.OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] != NULL
) {
1297 Private
->SelectIndex
= OfferIndex
+ 1;
1304 // Select offer by received order.
1306 for (Index
= 0; Index
< Private
->OfferNum
; Index
++) {
1308 OfferType
= Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
;
1310 if (IS_PROXY_OFFER (OfferType
)) {
1312 // Skip proxy offers
1317 if (!Private
->IsProxyRecved
&&
1318 OfferType
== PxeOfferTypeDhcpOnly
&&
1319 Private
->OfferBuffer
[Index
].Dhcp6
.OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] == NULL
) {
1321 // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
1326 Private
->SelectIndex
= Index
+ 1;
1334 Handle the DHCPv6 offer packet.
1336 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1338 @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully.
1339 @retval EFI_NO_RESPONSE No response to the following request packet.
1340 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1341 @retval EFI_BUFFER_TOO_SMALL Can't cache the offer pacet.
1345 PxeBcHandleDhcp6Offer (
1346 IN PXEBC_PRIVATE_DATA
*Private
1349 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
1351 PXEBC_OFFER_TYPE OfferType
;
1356 ASSERT (Private
->SelectIndex
> 0);
1357 SelectIndex
= (UINT32
) (Private
->SelectIndex
- 1);
1358 ASSERT (SelectIndex
< PXEBC_OFFER_MAX_NUM
);
1359 Cache6
= &Private
->OfferBuffer
[SelectIndex
].Dhcp6
;
1360 Status
= EFI_SUCCESS
;
1363 // First try to cache DNS server address if DHCP6 offer provides.
1365 if (Cache6
->OptList
[PXEBC_DHCP6_IDX_DNS_SERVER
] != NULL
) {
1366 Private
->DnsServer
= AllocateZeroPool (NTOHS (Cache6
->OptList
[PXEBC_DHCP6_IDX_DNS_SERVER
]->OpLen
));
1367 if (Private
->DnsServer
== NULL
) {
1368 return EFI_OUT_OF_RESOURCES
;
1370 CopyMem (Private
->DnsServer
, Cache6
->OptList
[PXEBC_DHCP6_IDX_DNS_SERVER
]->Data
, sizeof (EFI_IPv6_ADDRESS
));
1373 if (Cache6
->OfferType
== PxeOfferTypeDhcpBinl
) {
1375 // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
1377 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, SelectIndex
))) {
1378 Status
= EFI_NO_RESPONSE
;
1380 } else if (Cache6
->OfferType
== PxeOfferTypeDhcpOnly
) {
1382 if (Private
->IsProxyRecved
) {
1384 // DhcpOnly offer is selected, so need try to request bootfilename.
1387 if (Private
->IsOfferSorted
) {
1389 // The proxy offer should be determined if select by default policy.
1390 // IsOfferSorted means all offers are labeled by OfferIndex.
1392 ASSERT (Private
->OfferCount
[Private
->SelectProxyType
] > 0);
1394 if (Private
->SelectProxyType
== PxeOfferTypeProxyBinl
) {
1396 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1398 for (Index
= 0; Index
< Private
->OfferCount
[Private
->SelectProxyType
]; Index
++) {
1400 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][Index
];
1401 if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, ProxyIndex
))) {
1405 if (Index
== Private
->OfferCount
[Private
->SelectProxyType
]) {
1406 Status
= EFI_NO_RESPONSE
;
1410 // For other proxy offers (pxe10 or wfm11a), only one is buffered.
1412 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][0];
1416 // The proxy offer should not be determined if select by received order.
1418 Status
= EFI_NO_RESPONSE
;
1420 for (Index
= 0; Index
< Private
->OfferNum
; Index
++) {
1422 OfferType
= Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
;
1424 if (!IS_PROXY_OFFER (OfferType
)) {
1426 // Skip non proxy dhcp offers.
1431 if (OfferType
== PxeOfferTypeProxyBinl
) {
1433 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1435 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, Index
))) {
1440 Private
->SelectProxyType
= OfferType
;
1442 Status
= EFI_SUCCESS
;
1447 if (!EFI_ERROR (Status
) && Private
->SelectProxyType
!= PxeOfferTypeProxyBinl
) {
1449 // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
1451 Status
= PxeBcCopyDhcp6Proxy (Private
, ProxyIndex
);
1455 // Othewise, the bootfilename must be included in DhcpOnly offer.
1457 ASSERT (Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] != NULL
);
1461 if (!EFI_ERROR (Status
)) {
1463 // All PXE boot information is ready by now.
1465 Status
= PxeBcCopyDhcp6Ack (Private
, &Private
->DhcpAck
.Dhcp6
.Packet
.Ack
, TRUE
);
1466 Private
->PxeBc
.Mode
->DhcpDiscoverValid
= TRUE
;
1474 Unregister the address by Ip6Config protocol.
1476 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1480 PxeBcUnregisterIp6Address (
1481 IN PXEBC_PRIVATE_DATA
*Private
1484 if (Private
->Ip6Policy
!= PXEBC_IP6_POLICY_MAX
) {
1486 // PXE driver change the policy of IP6 driver, it's a chance to recover.
1487 // Keep the point and there is no enough requirements to do recovery.
1493 Check whether IP driver could route the message which will be sent to ServerIp address.
1495 This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid
1496 route is found in IP6 route table, the address will be filed in GatewayAddr and return.
1498 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1499 @param[in] TimeOutInSecond Timeout value in seconds.
1500 @param[out] GatewayAddr Pointer to store the gateway IP address.
1502 @retval EFI_SUCCESS Found a valid gateway address successfully.
1503 @retval EFI_TIMEOUT The operation is time out.
1504 @retval Other Unexpect error happened.
1508 PxeBcCheckRouteTable (
1509 IN PXEBC_PRIVATE_DATA
*Private
,
1510 IN UINTN TimeOutInSecond
,
1511 OUT EFI_IPv6_ADDRESS
*GatewayAddr
1515 EFI_IP6_PROTOCOL
*Ip6
;
1516 EFI_IP6_MODE_DATA Ip6ModeData
;
1518 EFI_EVENT TimeOutEvt
;
1520 BOOLEAN GatewayIsFound
;
1522 ASSERT (GatewayAddr
!= NULL
);
1523 ASSERT (Private
!= NULL
);
1526 GatewayIsFound
= FALSE
;
1529 ZeroMem (GatewayAddr
, sizeof (EFI_IPv6_ADDRESS
));
1532 Status
= Ip6
->GetModeData (Ip6
, &Ip6ModeData
, NULL
, NULL
);
1533 if (EFI_ERROR (Status
)) {
1538 // Find out the gateway address which can route the message which send to ServerIp.
1540 for (Index
= 0; Index
< Ip6ModeData
.RouteCount
; Index
++) {
1541 if (NetIp6IsNetEqual (&Private
->ServerIp
.v6
, &Ip6ModeData
.RouteTable
[Index
].Destination
, Ip6ModeData
.RouteTable
[Index
].PrefixLength
)) {
1542 IP6_COPY_ADDRESS (GatewayAddr
, &Ip6ModeData
.RouteTable
[Index
].Gateway
);
1543 GatewayIsFound
= TRUE
;
1548 if (Ip6ModeData
.AddressList
!= NULL
) {
1549 FreePool (Ip6ModeData
.AddressList
);
1551 if (Ip6ModeData
.GroupTable
!= NULL
) {
1552 FreePool (Ip6ModeData
.GroupTable
);
1554 if (Ip6ModeData
.RouteTable
!= NULL
) {
1555 FreePool (Ip6ModeData
.RouteTable
);
1557 if (Ip6ModeData
.NeighborCache
!= NULL
) {
1558 FreePool (Ip6ModeData
.NeighborCache
);
1560 if (Ip6ModeData
.PrefixTable
!= NULL
) {
1561 FreePool (Ip6ModeData
.PrefixTable
);
1563 if (Ip6ModeData
.IcmpTypeList
!= NULL
) {
1564 FreePool (Ip6ModeData
.IcmpTypeList
);
1567 if (GatewayIsFound
|| RetryCount
== TimeOutInSecond
) {
1574 // Delay 1 second then recheck it again.
1576 if (TimeOutEvt
== NULL
) {
1577 Status
= gBS
->CreateEvent (
1584 if (EFI_ERROR (Status
)) {
1589 Status
= gBS
->SetTimer (TimeOutEvt
, TimerRelative
, TICKS_PER_SECOND
);
1590 if (EFI_ERROR (Status
)) {
1593 while (EFI_ERROR (gBS
->CheckEvent (TimeOutEvt
))) {
1599 if (TimeOutEvt
!= NULL
) {
1600 gBS
->CloseEvent (TimeOutEvt
);
1603 if (GatewayIsFound
) {
1604 Status
= EFI_SUCCESS
;
1605 } else if (RetryCount
== TimeOutInSecond
) {
1606 Status
= EFI_TIMEOUT
;
1613 Register the ready station address and gateway by Ip6Config protocol.
1615 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1616 @param[in] Address The pointer to the ready address.
1618 @retval EFI_SUCCESS Registered the address succesfully.
1619 @retval Others Failed to register the address.
1623 PxeBcRegisterIp6Address (
1624 IN PXEBC_PRIVATE_DATA
*Private
,
1625 IN EFI_IPv6_ADDRESS
*Address
1628 EFI_IP6_PROTOCOL
*Ip6
;
1629 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1630 EFI_IP6_CONFIG_POLICY Policy
;
1631 EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr
;
1632 EFI_IPv6_ADDRESS GatewayAddr
;
1634 EFI_EVENT MappedEvt
;
1637 EFI_IPv6_ADDRESS
*Ip6Addr
;
1640 Status
= EFI_SUCCESS
;
1643 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1644 Ip6Cfg
= Private
->Ip6Cfg
;
1648 ZeroMem (&CfgAddr
, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS
));
1649 CopyMem (&CfgAddr
.Address
, Address
, sizeof (EFI_IPv6_ADDRESS
));
1651 Status
= Ip6
->Configure (Ip6
, &Private
->Ip6CfgData
);
1652 if (EFI_ERROR (Status
)) {
1657 // Retrieve the gateway address from IP6 route table.
1659 Status
= PxeBcCheckRouteTable (Private
, PXEBC_IP6_ROUTE_TABLE_TIMEOUT
, &GatewayAddr
);
1660 if (EFI_ERROR (Status
)) {
1665 // There is no channel between IP6 and PXE driver about address setting,
1666 // so it has to set the new address by Ip6ConfigProtocol manually.
1668 Policy
= Ip6ConfigPolicyManual
;
1669 Status
= Ip6Cfg
->SetData (
1671 Ip6ConfigDataTypePolicy
,
1672 sizeof(EFI_IP6_CONFIG_POLICY
),
1675 if (EFI_ERROR (Status
)) {
1677 // There is no need to recover later.
1679 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1684 // Create a notify event to set address flag when DAD if IP6 driver succeeded.
1686 Status
= gBS
->CreateEvent (
1690 &Private
->IsAddressOk
,
1693 if (EFI_ERROR (Status
)) {
1697 Private
->IsAddressOk
= FALSE
;
1698 Status
= Ip6Cfg
->RegisterDataNotify (
1700 Ip6ConfigDataTypeManualAddress
,
1703 if (EFI_ERROR(Status
)) {
1707 Status
= Ip6Cfg
->SetData (
1709 Ip6ConfigDataTypeManualAddress
,
1710 sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS
),
1713 if (EFI_ERROR(Status
) && Status
!= EFI_NOT_READY
) {
1715 } else if (Status
== EFI_NOT_READY
) {
1717 // Poll the network until the asynchronous process is finished.
1719 while (!Private
->IsAddressOk
) {
1723 // Check whether the IP6 address setting is successed.
1726 Status
= Ip6Cfg
->GetData (
1728 Ip6ConfigDataTypeManualAddress
,
1732 if (Status
!= EFI_BUFFER_TOO_SMALL
|| DataSize
== 0) {
1733 Status
= EFI_DEVICE_ERROR
;
1737 Ip6Addr
= AllocatePool (DataSize
);
1738 if (Ip6Addr
== NULL
) {
1739 return EFI_OUT_OF_RESOURCES
;
1741 Status
= Ip6Cfg
->GetData (
1743 Ip6ConfigDataTypeManualAddress
,
1747 if (EFI_ERROR (Status
)) {
1748 Status
= EFI_DEVICE_ERROR
;
1752 for (Index
= 0; Index
< DataSize
/ sizeof (EFI_IPv6_ADDRESS
); Index
++) {
1753 if (CompareMem (Ip6Addr
+ Index
, Address
, sizeof (EFI_IPv6_ADDRESS
)) == 0) {
1757 if (Index
== DataSize
/ sizeof (EFI_IPv6_ADDRESS
)) {
1758 Status
= EFI_ABORTED
;
1764 // Set the default gateway address back if needed.
1766 if (!NoGateway
&& !NetIp6IsUnspecifiedAddr (&GatewayAddr
)) {
1767 Status
= Ip6Cfg
->SetData (
1769 Ip6ConfigDataTypeGateway
,
1770 sizeof (EFI_IPv6_ADDRESS
),
1773 if (EFI_ERROR (Status
)) {
1779 if (MappedEvt
!= NULL
) {
1780 Ip6Cfg
->UnregisterDataNotify (
1782 Ip6ConfigDataTypeManualAddress
,
1785 gBS
->CloseEvent (MappedEvt
);
1787 if (Ip6Addr
!= NULL
) {
1794 Set the IP6 policy to Automatic.
1796 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1798 @retval EFI_SUCCESS Switch the IP policy succesfully.
1799 @retval Others Unexpect error happened.
1804 IN PXEBC_PRIVATE_DATA
*Private
1807 EFI_IP6_CONFIG_POLICY Policy
;
1809 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1812 Ip6Cfg
= Private
->Ip6Cfg
;
1813 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1816 // Get and store the current policy of IP6 driver.
1818 Status
= Ip6Cfg
->GetData (
1820 Ip6ConfigDataTypePolicy
,
1824 if (EFI_ERROR (Status
)) {
1828 if (Private
->Ip6Policy
== Ip6ConfigPolicyManual
) {
1829 Policy
= Ip6ConfigPolicyAutomatic
;
1830 Status
= Ip6Cfg
->SetData (
1832 Ip6ConfigDataTypePolicy
,
1833 sizeof(EFI_IP6_CONFIG_POLICY
),
1836 if (EFI_ERROR (Status
)) {
1838 // There is no need to recover later.
1840 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1848 This function will register the station IP address and flush IP instance to start using the new IP address.
1850 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1852 @retval EFI_SUCCESS The new IP address has been configured successfully.
1853 @retval Others Failed to configure the address.
1857 PxeBcSetIp6Address (
1858 IN PXEBC_PRIVATE_DATA
*Private
1862 EFI_DHCP6_PROTOCOL
*Dhcp6
;
1864 Dhcp6
= Private
->Dhcp6
;
1866 CopyMem (&Private
->StationIp
.v6
, &Private
->TmpStationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1867 CopyMem (&Private
->PxeBc
.Mode
->StationIp
.v6
, &Private
->StationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1869 Status
= PxeBcRegisterIp6Address (Private
, &Private
->StationIp
.v6
);
1870 if (EFI_ERROR (Status
)) {
1871 Dhcp6
->Stop (Dhcp6
);
1875 Status
= PxeBcFlushStationIp (Private
, &Private
->StationIp
, NULL
);
1876 if (EFI_ERROR (Status
)) {
1877 PxeBcUnregisterIp6Address (Private
);
1878 Dhcp6
->Stop (Dhcp6
);
1882 AsciiPrint ("\n Station IP address is ");
1883 PxeBcShowIp6Addr (&Private
->StationIp
.v6
);
1889 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
1890 to intercept events that occurred in the configuration process.
1892 @param[in] This The pointer to the EFI DHCPv6 Protocol.
1893 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
1894 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
1895 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
1897 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
1898 @param[out] NewPacket The packet that is used to replace the Packet above.
1900 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
1901 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
1902 driver will continue to wait for more packets.
1903 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
1908 PxeBcDhcp6CallBack (
1909 IN EFI_DHCP6_PROTOCOL
*This
,
1911 IN EFI_DHCP6_STATE CurrentState
,
1912 IN EFI_DHCP6_EVENT Dhcp6Event
,
1913 IN EFI_DHCP6_PACKET
*Packet
,
1914 OUT EFI_DHCP6_PACKET
**NewPacket OPTIONAL
1917 PXEBC_PRIVATE_DATA
*Private
;
1918 EFI_PXE_BASE_CODE_MODE
*Mode
;
1919 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
*Callback
;
1920 EFI_DHCP6_PACKET
*SelectAd
;
1924 if ((Dhcp6Event
!= Dhcp6RcvdAdvertise
) &&
1925 (Dhcp6Event
!= Dhcp6SelectAdvertise
) &&
1926 (Dhcp6Event
!= Dhcp6SendSolicit
) &&
1927 (Dhcp6Event
!= Dhcp6SendRequest
) &&
1928 (Dhcp6Event
!= Dhcp6RcvdReply
)) {
1932 ASSERT (Packet
!= NULL
);
1934 Private
= (PXEBC_PRIVATE_DATA
*) Context
;
1935 Mode
= Private
->PxeBc
.Mode
;
1936 Callback
= Private
->PxeBcCallback
;
1939 // Callback to user when any traffic ocurred if has.
1941 if (Dhcp6Event
!= Dhcp6SelectAdvertise
&& Callback
!= NULL
) {
1942 Received
= (BOOLEAN
) (Dhcp6Event
== Dhcp6RcvdAdvertise
|| Dhcp6Event
== Dhcp6RcvdReply
);
1943 Status
= Callback
->Callback (
1948 (EFI_PXE_BASE_CODE_PACKET
*) &Packet
->Dhcp6
1950 if (Status
!= EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
) {
1955 Status
= EFI_SUCCESS
;
1957 switch (Dhcp6Event
) {
1959 case Dhcp6SendSolicit
:
1960 if (Packet
->Length
> PXEBC_DHCP6_PACKET_MAX_SIZE
) {
1962 // If the to be sent packet exceeds the maximum length, abort the DHCP process.
1964 Status
= EFI_ABORTED
;
1969 // Record the first Solicate msg time
1971 if (Private
->SolicitTimes
== 0) {
1972 CalcElapsedTime (Private
);
1973 Private
->SolicitTimes
++;
1976 // Cache the dhcp discover packet to mode data directly.
1978 CopyMem (&Mode
->DhcpDiscover
.Dhcpv4
, &Packet
->Dhcp6
, Packet
->Length
);
1981 case Dhcp6RcvdAdvertise
:
1982 Status
= EFI_NOT_READY
;
1983 if (Packet
->Length
> PXEBC_DHCP6_PACKET_MAX_SIZE
) {
1985 // Ignore the incoming packets which exceed the maximum length.
1989 if (Private
->OfferNum
< PXEBC_OFFER_MAX_NUM
) {
1991 // Cache the dhcp offers to OfferBuffer[] for select later, and record
1992 // the OfferIndex and OfferCount.
1994 PxeBcCacheDhcp6Offer (Private
, Packet
);
1998 case Dhcp6SendRequest
:
1999 if (Packet
->Length
> PXEBC_DHCP6_PACKET_MAX_SIZE
) {
2001 // If the to be sent packet exceeds the maximum length, abort the DHCP process.
2003 Status
= EFI_ABORTED
;
2008 // Store the request packet as seed packet for discover.
2010 if (Private
->Dhcp6Request
!= NULL
) {
2011 FreePool (Private
->Dhcp6Request
);
2013 Private
->Dhcp6Request
= AllocateZeroPool (Packet
->Size
);
2014 if (Private
->Dhcp6Request
!= NULL
) {
2015 CopyMem (Private
->Dhcp6Request
, Packet
, Packet
->Size
);
2019 case Dhcp6SelectAdvertise
:
2021 // Select offer by the default policy or by order, and record the SelectIndex
2022 // and SelectProxyType.
2024 PxeBcSelectDhcp6Offer (Private
);
2026 if (Private
->SelectIndex
== 0) {
2027 Status
= EFI_ABORTED
;
2029 ASSERT (NewPacket
!= NULL
);
2030 SelectAd
= &Private
->OfferBuffer
[Private
->SelectIndex
- 1].Dhcp6
.Packet
.Offer
;
2031 *NewPacket
= AllocateZeroPool (SelectAd
->Size
);
2032 ASSERT (*NewPacket
!= NULL
);
2033 CopyMem (*NewPacket
, SelectAd
, SelectAd
->Size
);
2037 case Dhcp6RcvdReply
:
2039 // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
2040 // without verification.
2042 ASSERT (Private
->SelectIndex
!= 0);
2043 Status
= PxeBcCopyDhcp6Ack (Private
, Packet
, FALSE
);
2044 if (EFI_ERROR (Status
)) {
2045 Status
= EFI_ABORTED
;
2058 Build and send out the request packet for the bootfile, and parse the reply.
2060 @param[in] Private The pointer to PxeBc private data.
2061 @param[in] Type PxeBc option boot item type.
2062 @param[in] Layer The pointer to option boot item layer.
2063 @param[in] UseBis Use BIS or not.
2064 @param[in] DestIp The pointer to the server address.
2066 @retval EFI_SUCCESS Successfully discovered the boot file.
2067 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
2068 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
2069 @retval Others Failed to discover the boot file.
2073 PxeBcDhcp6Discover (
2074 IN PXEBC_PRIVATE_DATA
*Private
,
2078 IN EFI_IP_ADDRESS
*DestIp
2081 EFI_PXE_BASE_CODE_UDP_PORT SrcPort
;
2082 EFI_PXE_BASE_CODE_UDP_PORT DestPort
;
2083 EFI_PXE_BASE_CODE_MODE
*Mode
;
2084 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
2085 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*Discover
;
2087 EFI_DHCP6_PACKET
*Request
;
2089 EFI_DHCP6_PACKET
*Reply
;
2098 PxeBc
= &Private
->PxeBc
;
2100 Request
= Private
->Dhcp6Request
;
2101 SrcPort
= PXEBC_BS_DISCOVER_PORT
;
2102 DestPort
= PXEBC_BS_DISCOVER_PORT
;
2104 if (!UseBis
&& Layer
!= NULL
) {
2105 *Layer
&= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK
;
2108 if (Request
== NULL
) {
2109 return EFI_DEVICE_ERROR
;
2112 Discover
= AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET
));
2113 if (Discover
== NULL
) {
2114 return EFI_OUT_OF_RESOURCES
;
2118 // Build the discover packet by the cached request packet before.
2120 Xid
= NET_RANDOM (NetRandomInitSeed ());
2121 Discover
->TransactionId
= HTONL (Xid
);
2122 Discover
->MessageType
= Request
->Dhcp6
.Header
.MessageType
;
2123 RequestOpt
= Request
->Dhcp6
.Option
;
2124 DiscoverOpt
= Discover
->DhcpOptions
;
2125 DiscoverLen
= sizeof (EFI_DHCP6_HEADER
);
2126 RequestLen
= DiscoverLen
;
2128 while (RequestLen
< Request
->Length
) {
2129 OpCode
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpCode
);
2130 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpLen
);
2131 if (OpCode
!= EFI_DHCP6_IA_TYPE_NA
&&
2132 OpCode
!= EFI_DHCP6_IA_TYPE_TA
) {
2134 // Copy all the options except IA option.
2136 CopyMem (DiscoverOpt
, RequestOpt
, OpLen
+ 4);
2137 DiscoverOpt
+= (OpLen
+ 4);
2138 DiscoverLen
+= (OpLen
+ 4);
2140 RequestOpt
+= (OpLen
+ 4);
2141 RequestLen
+= (OpLen
+ 4);
2144 Status
= PxeBc
->UdpWrite (
2150 &Private
->StationIp
,
2157 if (EFI_ERROR (Status
)) {
2162 // Cache the right PXE reply packet here, set valid flag later.
2163 // Especially for PXE discover packet, store it into mode data here.
2165 if (Private
->IsDoDiscover
) {
2166 CopyMem (&Mode
->PxeDiscover
.Dhcpv6
, Discover
, DiscoverLen
);
2167 Reply
= &Private
->PxeReply
.Dhcp6
.Packet
.Ack
;
2169 Reply
= &Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
;
2171 ReadSize
= (UINTN
) Reply
->Size
;
2174 // Start Udp6Read instance
2176 Status
= Private
->Udp6Read
->Configure (Private
->Udp6Read
, &Private
->Udp6CfgData
);
2177 if (EFI_ERROR (Status
)) {
2181 Status
= PxeBc
->UdpRead (
2183 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP
,
2191 (VOID
*) &Reply
->Dhcp6
2194 // Stop Udp6Read instance
2196 Private
->Udp6Read
->Configure (Private
->Udp6Read
, NULL
);
2197 if (EFI_ERROR (Status
)) {
2206 Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
2208 @param[in] Private The pointer to PxeBc private data.
2209 @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL
2211 @retval EFI_SUCCESS The S.A.R.R. process successfully finished.
2212 @retval Others Failed to finish the S.A.R.R. process.
2217 IN PXEBC_PRIVATE_DATA
*Private
,
2218 IN EFI_DHCP6_PROTOCOL
*Dhcp6
2221 EFI_PXE_BASE_CODE_MODE
*PxeMode
;
2222 EFI_DHCP6_CONFIG_DATA Config
;
2223 EFI_DHCP6_MODE_DATA Mode
;
2224 EFI_DHCP6_RETRANSMISSION
*Retransmit
;
2225 EFI_DHCP6_PACKET_OPTION
*OptList
[PXEBC_DHCP6_OPTION_MAX_NUM
];
2226 UINT8 Buffer
[PXEBC_DHCP6_OPTION_MAX_SIZE
];
2229 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
2230 EFI_STATUS TimerStatus
;
2232 UINT64 GetMappingTimeOut
;
2234 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits
;
2236 Status
= EFI_SUCCESS
;
2237 PxeMode
= Private
->PxeBc
.Mode
;
2238 Ip6Cfg
= Private
->Ip6Cfg
;
2242 // Build option list for the request packet.
2244 OptCount
= PxeBcBuildDhcp6Options (Private
, OptList
, Buffer
);
2245 ASSERT (OptCount
> 0);
2247 Retransmit
= AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION
));
2248 if (Retransmit
== NULL
) {
2249 return EFI_OUT_OF_RESOURCES
;
2252 ZeroMem (&Mode
, sizeof (EFI_DHCP6_MODE_DATA
));
2253 ZeroMem (&Config
, sizeof (EFI_DHCP6_CONFIG_DATA
));
2255 Config
.OptionCount
= OptCount
;
2256 Config
.OptionList
= OptList
;
2257 Config
.Dhcp6Callback
= PxeBcDhcp6CallBack
;
2258 Config
.CallbackContext
= Private
;
2259 Config
.IaInfoEvent
= NULL
;
2260 Config
.RapidCommit
= FALSE
;
2261 Config
.ReconfigureAccept
= FALSE
;
2262 Config
.IaDescriptor
.IaId
= Private
->IaId
;
2263 Config
.IaDescriptor
.Type
= EFI_DHCP6_IA_TYPE_NA
;
2264 Config
.SolicitRetransmission
= Retransmit
;
2265 Retransmit
->Irt
= 4;
2266 Retransmit
->Mrc
= 4;
2267 Retransmit
->Mrt
= 32;
2268 Retransmit
->Mrd
= 60;
2271 // Configure the DHCPv6 instance for PXE boot.
2273 Status
= Dhcp6
->Configure (Dhcp6
, &Config
);
2274 FreePool (Retransmit
);
2275 if (EFI_ERROR (Status
)) {
2280 // Initialize the record fields for DHCPv6 offer in private data.
2282 Private
->IsProxyRecved
= FALSE
;
2283 Private
->OfferNum
= 0;
2284 Private
->SelectIndex
= 0;
2285 ZeroMem (Private
->OfferCount
, sizeof (Private
->OfferCount
));
2286 ZeroMem (Private
->OfferIndex
, sizeof (Private
->OfferIndex
));
2290 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
2292 Status
= Dhcp6
->Start (Dhcp6
);
2293 if (Status
== EFI_NO_MAPPING
) {
2295 // IP6 Linklocal address is not available for use, so stop current Dhcp process
2296 // and wait for duplicate address detection to finish.
2298 Dhcp6
->Stop (Dhcp6
);
2301 // Get Duplicate Address Detection Transmits count.
2303 DataSize
= sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS
);
2304 Status
= Ip6Cfg
->GetData (
2306 Ip6ConfigDataTypeDupAddrDetectTransmits
,
2310 if (EFI_ERROR (Status
)) {
2311 Dhcp6
->Configure (Dhcp6
, NULL
);
2315 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
2316 if (EFI_ERROR (Status
)) {
2317 Dhcp6
->Configure (Dhcp6
, NULL
);
2321 GetMappingTimeOut
= TICKS_PER_SECOND
* DadXmits
.DupAddrDetectTransmits
+ PXEBC_DAD_ADDITIONAL_DELAY
;
2322 Status
= gBS
->SetTimer (Timer
, TimerRelative
, GetMappingTimeOut
);
2323 if (EFI_ERROR (Status
)) {
2324 gBS
->CloseEvent (Timer
);
2325 Dhcp6
->Configure (Dhcp6
, NULL
);
2331 TimerStatus
= gBS
->CheckEvent (Timer
);
2332 if (!EFI_ERROR (TimerStatus
)) {
2333 Status
= Dhcp6
->Start (Dhcp6
);
2335 } while (TimerStatus
== EFI_NOT_READY
);
2337 gBS
->CloseEvent (Timer
);
2339 if (EFI_ERROR (Status
)) {
2340 if (Status
== EFI_ICMP_ERROR
) {
2341 PxeMode
->IcmpErrorReceived
= TRUE
;
2343 Dhcp6
->Configure (Dhcp6
, NULL
);
2348 // Get the acquired IPv6 address and store them.
2350 Status
= Dhcp6
->GetModeData (Dhcp6
, &Mode
, NULL
);
2351 if (EFI_ERROR (Status
)) {
2352 Dhcp6
->Stop (Dhcp6
);
2356 ASSERT ((Mode
.Ia
!= NULL
) && (Mode
.Ia
->State
== Dhcp6Bound
));
2358 // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the
2359 // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when
2360 // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as
2361 // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery
2362 // to find a valid router address.
2364 CopyMem (&Private
->TmpStationIp
.v6
, &Mode
.Ia
->IaAddress
[0].IpAddress
, sizeof (EFI_IPv6_ADDRESS
));
2365 if (Mode
.ClientId
!= NULL
) {
2366 FreePool (Mode
.ClientId
);
2368 if (Mode
.Ia
!= NULL
) {
2372 // Check the selected offer whether BINL retry is needed.
2374 Status
= PxeBcHandleDhcp6Offer (Private
);
2375 if (EFI_ERROR (Status
)) {
2376 Dhcp6
->Stop (Dhcp6
);