2 Functions implementation related with DHCPv6 for UefiPxeBc Driver.
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2015, 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
| EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_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 which 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
;
1386 Status
= EFI_SUCCESS
;
1389 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1390 Ip6Cfg
= Private
->Ip6Cfg
;
1394 ZeroMem (&CfgAddr
, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS
));
1395 CopyMem (&CfgAddr
.Address
, Address
, sizeof (EFI_IPv6_ADDRESS
));
1397 Status
= Ip6
->Configure (Ip6
, &Private
->Ip6CfgData
);
1398 if (EFI_ERROR (Status
)) {
1403 // Retrieve the gateway address from IP6 route table.
1405 Status
= PxeBcCheckRouteTable (Private
, PXEBC_IP6_ROUTE_TABLE_TIMEOUT
, &GatewayAddr
);
1406 if (EFI_ERROR (Status
)) {
1411 // There is no channel between IP6 and PXE driver about address setting,
1412 // so it has to set the new address by Ip6ConfigProtocol manually.
1414 Policy
= Ip6ConfigPolicyManual
;
1415 Status
= Ip6Cfg
->SetData (
1417 Ip6ConfigDataTypePolicy
,
1418 sizeof(EFI_IP6_CONFIG_POLICY
),
1421 if (EFI_ERROR (Status
)) {
1423 // There is no need to recover later.
1425 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1430 // Get Duplicate Address Detection Transmits count.
1432 DataSize
= sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS
);
1433 Status
= Ip6Cfg
->GetData (
1435 Ip6ConfigDataTypeDupAddrDetectTransmits
,
1439 if (EFI_ERROR (Status
)) {
1444 // Create a timer as setting address timeout event since DAD in IP6 driver.
1446 Status
= gBS
->CreateEvent (
1453 if (EFI_ERROR (Status
)) {
1458 // Create a notify event to set address flag when DAD if IP6 driver succeeded.
1460 Status
= gBS
->CreateEvent (
1464 &Private
->IsAddressOk
,
1467 if (EFI_ERROR (Status
)) {
1471 Status
= Ip6Cfg
->RegisterDataNotify (
1473 Ip6ConfigDataTypeManualAddress
,
1476 if (EFI_ERROR(Status
)) {
1480 Status
= Ip6Cfg
->SetData (
1482 Ip6ConfigDataTypeManualAddress
,
1483 sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS
),
1486 if (EFI_ERROR(Status
) && Status
!= EFI_NOT_READY
) {
1491 // Start the 5 secondes timer to wait for setting address.
1493 Status
= EFI_NO_MAPPING
;
1494 DadTriggerTime
= TICKS_PER_SECOND
* DadXmits
.DupAddrDetectTransmits
+ PXEBC_DAD_ADDITIONAL_DELAY
;
1495 gBS
->SetTimer (TimeOutEvt
, TimerRelative
, DadTriggerTime
);
1497 while (EFI_ERROR (gBS
->CheckEvent (TimeOutEvt
))) {
1499 if (Private
->IsAddressOk
) {
1500 Status
= EFI_SUCCESS
;
1506 // Set the default gateway address back if needed.
1508 if (!NoGateway
&& !NetIp6IsUnspecifiedAddr (&GatewayAddr
)) {
1509 Status
= Ip6Cfg
->SetData (
1511 Ip6ConfigDataTypeGateway
,
1512 sizeof (EFI_IPv6_ADDRESS
),
1515 if (EFI_ERROR (Status
)) {
1521 if (MappedEvt
!= NULL
) {
1522 Ip6Cfg
->UnregisterDataNotify (
1524 Ip6ConfigDataTypeManualAddress
,
1527 gBS
->CloseEvent (MappedEvt
);
1529 if (TimeOutEvt
!= NULL
) {
1530 gBS
->CloseEvent (TimeOutEvt
);
1536 Set the IP6 policy to Automatic.
1538 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1540 @retval EFI_SUCCESS Switch the IP policy succesfully.
1541 @retval Others Unexpect error happened.
1546 IN PXEBC_PRIVATE_DATA
*Private
1549 EFI_IP6_CONFIG_POLICY Policy
;
1551 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1554 Ip6Cfg
= Private
->Ip6Cfg
;
1555 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1558 // Get and store the current policy of IP6 driver.
1560 Status
= Ip6Cfg
->GetData (
1562 Ip6ConfigDataTypePolicy
,
1566 if (EFI_ERROR (Status
)) {
1570 if (Private
->Ip6Policy
== Ip6ConfigPolicyManual
) {
1571 Policy
= Ip6ConfigPolicyAutomatic
;
1572 Status
= Ip6Cfg
->SetData (
1574 Ip6ConfigDataTypePolicy
,
1575 sizeof(EFI_IP6_CONFIG_POLICY
),
1578 if (EFI_ERROR (Status
)) {
1580 // There is no need to recover later.
1582 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1590 This function will register the station IP address and flush IP instance to start using the new IP address.
1592 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1594 @retval EFI_SUCCESS The new IP address has been configured successfully.
1595 @retval Others Failed to configure the address.
1599 PxeBcSetIp6Address (
1600 IN PXEBC_PRIVATE_DATA
*Private
1604 EFI_DHCP6_PROTOCOL
*Dhcp6
;
1606 Dhcp6
= Private
->Dhcp6
;
1608 CopyMem (&Private
->StationIp
.v6
, &Private
->TmpStationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1609 CopyMem (&Private
->PxeBc
.Mode
->StationIp
.v6
, &Private
->StationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1611 Status
= PxeBcRegisterIp6Address (Private
, &Private
->StationIp
.v6
);
1612 if (EFI_ERROR (Status
)) {
1613 Dhcp6
->Stop (Dhcp6
);
1617 Status
= PxeBcFlushStationIp (Private
, &Private
->StationIp
, NULL
);
1618 if (EFI_ERROR (Status
)) {
1619 PxeBcUnregisterIp6Address (Private
);
1620 Dhcp6
->Stop (Dhcp6
);
1624 AsciiPrint ("\n Station IP address is ");
1625 PxeBcShowIp6Addr (&Private
->StationIp
.v6
);
1631 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
1632 to intercept events that occurred in the configuration process.
1634 @param[in] This The pointer to the EFI DHCPv6 Protocol.
1635 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
1636 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
1637 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
1639 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
1640 @param[out] NewPacket The packet that is used to replace the Packet above.
1642 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
1643 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
1644 driver will continue to wait for more packets.
1645 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
1650 PxeBcDhcp6CallBack (
1651 IN EFI_DHCP6_PROTOCOL
*This
,
1653 IN EFI_DHCP6_STATE CurrentState
,
1654 IN EFI_DHCP6_EVENT Dhcp6Event
,
1655 IN EFI_DHCP6_PACKET
*Packet
,
1656 OUT EFI_DHCP6_PACKET
**NewPacket OPTIONAL
1659 PXEBC_PRIVATE_DATA
*Private
;
1660 EFI_PXE_BASE_CODE_MODE
*Mode
;
1661 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
*Callback
;
1662 EFI_DHCP6_PACKET
*SelectAd
;
1666 if ((Dhcp6Event
!= Dhcp6RcvdAdvertise
) &&
1667 (Dhcp6Event
!= Dhcp6SelectAdvertise
) &&
1668 (Dhcp6Event
!= Dhcp6SendSolicit
) &&
1669 (Dhcp6Event
!= Dhcp6SendRequest
) &&
1670 (Dhcp6Event
!= Dhcp6RcvdReply
)) {
1674 ASSERT (Packet
!= NULL
);
1676 Private
= (PXEBC_PRIVATE_DATA
*) Context
;
1677 Mode
= Private
->PxeBc
.Mode
;
1678 Callback
= Private
->PxeBcCallback
;
1681 // Callback to user when any traffic ocurred if has.
1683 if (Dhcp6Event
!= Dhcp6SelectAdvertise
&& Callback
!= NULL
) {
1684 Received
= (BOOLEAN
) (Dhcp6Event
== Dhcp6RcvdAdvertise
|| Dhcp6Event
== Dhcp6RcvdReply
);
1685 Status
= Callback
->Callback (
1690 (EFI_PXE_BASE_CODE_PACKET
*) &Packet
->Dhcp6
1692 if (Status
!= EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
) {
1697 Status
= EFI_SUCCESS
;
1699 switch (Dhcp6Event
) {
1701 case Dhcp6SendSolicit
:
1703 // Record the first Solicate msg time
1705 if (Private
->SolicitTimes
== 0) {
1706 CalcElapsedTime (Private
);
1707 Private
->SolicitTimes
++;
1710 // Cache the dhcp discover packet to mode data directly.
1712 CopyMem (&Mode
->DhcpDiscover
.Dhcpv4
, &Packet
->Dhcp6
, Packet
->Length
);
1715 case Dhcp6RcvdAdvertise
:
1716 Status
= EFI_NOT_READY
;
1717 if (Private
->OfferNum
< PXEBC_OFFER_MAX_NUM
) {
1719 // Cache the dhcp offers to OfferBuffer[] for select later, and record
1720 // the OfferIndex and OfferCount.
1722 PxeBcCacheDhcp6Offer (Private
, Packet
);
1726 case Dhcp6SendRequest
:
1728 // Store the request packet as seed packet for discover.
1730 if (Private
->Dhcp6Request
!= NULL
) {
1731 FreePool (Private
->Dhcp6Request
);
1733 Private
->Dhcp6Request
= AllocateZeroPool (Packet
->Size
);
1734 if (Private
->Dhcp6Request
!= NULL
) {
1735 CopyMem (Private
->Dhcp6Request
, Packet
, Packet
->Size
);
1739 case Dhcp6SelectAdvertise
:
1741 // Select offer by the default policy or by order, and record the SelectIndex
1742 // and SelectProxyType.
1744 PxeBcSelectDhcp6Offer (Private
);
1746 if (Private
->SelectIndex
== 0) {
1747 Status
= EFI_ABORTED
;
1749 ASSERT (NewPacket
!= NULL
);
1750 SelectAd
= &Private
->OfferBuffer
[Private
->SelectIndex
- 1].Dhcp6
.Packet
.Offer
;
1751 *NewPacket
= AllocateZeroPool (SelectAd
->Size
);
1752 ASSERT (*NewPacket
!= NULL
);
1753 CopyMem (*NewPacket
, SelectAd
, SelectAd
->Size
);
1757 case Dhcp6RcvdReply
:
1759 // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
1760 // without verification.
1762 ASSERT (Private
->SelectIndex
!= 0);
1763 PxeBcCopyDhcp6Ack (Private
, Packet
, FALSE
);
1775 Build and send out the request packet for the bootfile, and parse the reply.
1777 @param[in] Private The pointer to PxeBc private data.
1778 @param[in] Type PxeBc option boot item type.
1779 @param[in] Layer The pointer to option boot item layer.
1780 @param[in] UseBis Use BIS or not.
1781 @param[in] DestIp The pointer to the server address.
1783 @retval EFI_SUCCESS Successfully discovered the boot file.
1784 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1785 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
1786 @retval Others Failed to discover the boot file.
1790 PxeBcDhcp6Discover (
1791 IN PXEBC_PRIVATE_DATA
*Private
,
1795 IN EFI_IP_ADDRESS
*DestIp
1798 EFI_PXE_BASE_CODE_UDP_PORT SrcPort
;
1799 EFI_PXE_BASE_CODE_UDP_PORT DestPort
;
1800 EFI_PXE_BASE_CODE_MODE
*Mode
;
1801 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
1802 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*Discover
;
1804 EFI_DHCP6_PACKET
*Request
;
1806 EFI_DHCP6_PACKET
*Reply
;
1815 PxeBc
= &Private
->PxeBc
;
1817 Request
= Private
->Dhcp6Request
;
1818 SrcPort
= PXEBC_BS_DISCOVER_PORT
;
1819 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 (
1900 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP
,
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
);