2 Functions implementation related with DHCPv6 for UefiPxeBc Driver.
4 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "PxeBcImpl.h"
19 // Well-known multi-cast address defined in section-24.1 of rfc-3315
21 // ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
23 EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress
= {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
26 Parse out a DHCPv6 option by OptTag, and find the position in buffer.
28 @param[in] Buffer The pointer to the option buffer.
29 @param[in] Length Length of the option buffer.
30 @param[in] OptTag The required option tag.
32 @retval NULL Failed to parse the required option.
33 @retval Others The postion of the required option in buffer.
36 EFI_DHCP6_PACKET_OPTION
*
37 PxeBcParseDhcp6Options (
43 EFI_DHCP6_PACKET_OPTION
*Option
;
46 Option
= (EFI_DHCP6_PACKET_OPTION
*) Buffer
;
50 // OpLen and OpCode here are both stored in network order.
52 while (Offset
< Length
) {
54 if (NTOHS (Option
->OpCode
) == OptTag
) {
59 Offset
+= (NTOHS(Option
->OpLen
) + 4);
60 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Buffer
+ Offset
);
68 Build the options buffer for the DHCPv6 request packet.
70 @param[in] Private The pointer to PxeBc private data.
71 @param[out] OptList The pointer to the option pointer array.
72 @param[in] Buffer The pointer to the buffer to contain the option list.
74 @return Index The count of the built-in options.
78 PxeBcBuildDhcp6Options (
79 IN PXEBC_PRIVATE_DATA
*Private
,
80 OUT EFI_DHCP6_PACKET_OPTION
**OptList
,
84 PXEBC_DHCP6_OPTION_ENTRY OptEnt
;
89 OptList
[0] = (EFI_DHCP6_PACKET_OPTION
*) Buffer
;
92 // Append client option request option
94 OptList
[Index
]->OpCode
= HTONS (PXEBC_DHCP6_OPT_ORO
);
95 OptList
[Index
]->OpLen
= HTONS (4);
96 OptEnt
.Oro
= (PXEBC_DHCP6_OPTION_ORO
*) OptList
[Index
]->Data
;
97 OptEnt
.Oro
->OpCode
[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL
);
98 OptEnt
.Oro
->OpCode
[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM
);
100 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
103 // Append client network device interface option
105 OptList
[Index
]->OpCode
= HTONS (PXEBC_DHCP6_OPT_UNDI
);
106 OptList
[Index
]->OpLen
= HTONS ((UINT16
)3);
107 OptEnt
.Undi
= (PXEBC_DHCP6_OPTION_UNDI
*) OptList
[Index
]->Data
;
109 if (Private
->Nii
!= NULL
) {
110 OptEnt
.Undi
->Type
= Private
->Nii
->Type
;
111 OptEnt
.Undi
->MajorVer
= Private
->Nii
->MajorVer
;
112 OptEnt
.Undi
->MinorVer
= Private
->Nii
->MinorVer
;
114 OptEnt
.Undi
->Type
= DEFAULT_UNDI_TYPE
;
115 OptEnt
.Undi
->MajorVer
= DEFAULT_UNDI_MAJOR
;
116 OptEnt
.Undi
->MinorVer
= DEFAULT_UNDI_MINOR
;
120 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
123 // Append client system architecture option
125 OptList
[Index
]->OpCode
= HTONS (PXEBC_DHCP6_OPT_ARCH
);
126 OptList
[Index
]->OpLen
= HTONS ((UINT16
) sizeof (PXEBC_DHCP6_OPTION_ARCH
));
127 OptEnt
.Arch
= (PXEBC_DHCP6_OPTION_ARCH
*) OptList
[Index
]->Data
;
128 Value
= HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE
);
129 CopyMem (&OptEnt
.Arch
->Type
, &Value
, sizeof (UINT16
));
131 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
134 // Append vendor class option to store the PXE class identifier.
136 OptList
[Index
]->OpCode
= HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS
);
137 OptList
[Index
]->OpLen
= HTONS ((UINT16
) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS
));
138 OptEnt
.VendorClass
= (PXEBC_DHCP6_OPTION_VENDOR_CLASS
*) OptList
[Index
]->Data
;
139 OptEnt
.VendorClass
->Vendor
= HTONL (PXEBC_DHCP6_ENTERPRISE_NUM
);
140 OptEnt
.VendorClass
->ClassLen
= HTONS ((UINT16
) sizeof (PXEBC_CLASS_ID
));
142 &OptEnt
.VendorClass
->ClassId
,
143 DEFAULT_CLASS_ID_DATA
,
144 sizeof (PXEBC_CLASS_ID
)
146 PxeBcUintnToAscDecWithFormat (
147 EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE
,
148 OptEnt
.VendorClass
->ClassId
.ArchitectureType
,
149 sizeof (OptEnt
.VendorClass
->ClassId
.ArchitectureType
)
152 if (Private
->Nii
!= NULL
) {
154 OptEnt
.VendorClass
->ClassId
.InterfaceName
,
155 Private
->Nii
->StringId
,
156 sizeof (OptEnt
.VendorClass
->ClassId
.InterfaceName
)
158 PxeBcUintnToAscDecWithFormat (
159 Private
->Nii
->MajorVer
,
160 OptEnt
.VendorClass
->ClassId
.UndiMajor
,
161 sizeof (OptEnt
.VendorClass
->ClassId
.UndiMajor
)
163 PxeBcUintnToAscDecWithFormat (
164 Private
->Nii
->MinorVer
,
165 OptEnt
.VendorClass
->ClassId
.UndiMinor
,
166 sizeof (OptEnt
.VendorClass
->ClassId
.UndiMinor
)
177 Cache the DHCPv6 packet.
179 @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.
180 @param[in] Src The pointer to the DHCPv6 packet to be cached.
184 PxeBcCacheDhcp6Packet (
185 IN EFI_DHCP6_PACKET
*Dst
,
186 IN EFI_DHCP6_PACKET
*Src
189 ASSERT (Dst
->Size
>= Src
->Length
);
191 CopyMem (&Dst
->Dhcp6
, &Src
->Dhcp6
, Src
->Length
);
192 Dst
->Length
= Src
->Length
;
197 Free all the nodes in the list for boot file.
199 @param[in] Head The pointer to the head of list.
203 PxeBcFreeBootFileOption (
208 LIST_ENTRY
*NextEntry
;
209 PXEBC_DHCP6_OPTION_NODE
*Node
;
211 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, Head
) {
212 Node
= NET_LIST_USER_STRUCT (Entry
, PXEBC_DHCP6_OPTION_NODE
, Link
);
213 RemoveEntryList (Entry
);
220 Parse the Boot File URL option.
222 @param[out] FileName The pointer to the boot file name.
223 @param[in, out] SrvAddr The pointer to the boot server address.
224 @param[in] BootFile The pointer to the boot file URL option data.
225 @param[in] Length The length of the boot file URL option data.
227 @retval EFI_ABORTED User cancel operation.
228 @retval EFI_SUCCESS Selected the boot menu successfully.
229 @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
233 PxeBcExtractBootFileUrl (
234 OUT UINT8
**FileName
,
235 IN OUT EFI_IPv6_ADDRESS
*SrvAddr
,
241 CHAR8
*BootFileNamePtr
;
243 UINT16 BootFileNameLen
;
246 CHAR8
*ServerAddressOption
;
247 CHAR8
*ServerAddress
;
252 // The format of the Boot File URL option is:
255 // 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
256 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
257 // | OPT_BOOTFILE_URL | option-len |
258 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
260 // . bootfile-url (variable length) .
262 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
266 // Based upon RFC 5970 and UEFI 2.3 Errata D specification, bootfile-url format
267 // is tftp://[SERVER_ADDRESS]/BOOTFILE_NAME
268 // As an example where the BOOTFILE_NAME is the EFI loader and
269 // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.
271 PrefixLen
= (UINT16
) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX
);
273 if (Length
<= PrefixLen
||
274 CompareMem (BootFile
, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX
, PrefixLen
) != 0) {
275 return EFI_NOT_FOUND
;
278 BootFile
= BootFile
+ PrefixLen
;
279 Length
= (UINT16
) (Length
- PrefixLen
);
281 TmpStr
= (CHAR8
*) AllocateZeroPool (Length
+ 1);
282 if (TmpStr
== NULL
) {
283 return EFI_OUT_OF_RESOURCES
;
286 CopyMem (TmpStr
, BootFile
, Length
);
287 TmpStr
[Length
] = '\0';
290 // Get the part of SERVER_ADDRESS string.
292 ServerAddressOption
= TmpStr
;
293 if (*ServerAddressOption
!= PXEBC_ADDR_START_DELIMITER
) {
295 return EFI_INVALID_PARAMETER
;
298 ServerAddressOption
++;
299 ServerAddress
= ServerAddressOption
;
300 while (*ServerAddress
!= '\0' && *ServerAddress
!= PXEBC_ADDR_END_DELIMITER
) {
304 if (*ServerAddress
!= PXEBC_ADDR_END_DELIMITER
) {
306 return EFI_INVALID_PARAMETER
;
309 *ServerAddress
= '\0';
312 // Convert the string of server address to Ipv6 address format and store it.
314 Status
= NetLibAsciiStrToIp6 (ServerAddressOption
, SrvAddr
);
315 if (EFI_ERROR (Status
)) {
321 // Get the part of BOOTFILE_NAME string.
323 BootFileNamePtr
= (CHAR8
*)((UINTN
)ServerAddress
+ 1);
324 if (*BootFileNamePtr
!= PXEBC_TFTP_URL_SEPARATOR
) {
326 return EFI_INVALID_PARAMETER
;
330 BootFileNameLen
= (UINT16
)(Length
- (UINT16
) ((UINTN
)BootFileNamePtr
- (UINTN
)TmpStr
) + 1);
331 if (BootFileNameLen
!= 0 || FileName
!= NULL
) {
333 // Remove trailing mode=octet if present and ignore. All other modes are
334 // invalid for netboot6, so reject them.
336 ModeStr
= AsciiStrStr (BootFileNamePtr
, ";mode=octet");
337 if (ModeStr
!= NULL
&& *(ModeStr
+ AsciiStrLen (";mode=octet")) == '\0') {
339 } else if (AsciiStrStr (BootFileNamePtr
, ";mode=") != NULL
) {
340 return EFI_INVALID_PARAMETER
;
344 // Extract boot file name from URL.
346 BootFileName
= (CHAR8
*) AllocateZeroPool (BootFileNameLen
);
347 if (BootFileName
== NULL
) {
349 return EFI_OUT_OF_RESOURCES
;
351 *FileName
= (UINT8
*) BootFileName
;
354 // Decode percent-encoding in boot file name.
356 while (*BootFileNamePtr
!= '\0') {
357 if (*BootFileNamePtr
== '%') {
358 TmpChar
= *(BootFileNamePtr
+ 3);
359 *(BootFileNamePtr
+ 3) = '\0';
360 *BootFileName
= (UINT8
) AsciiStrHexToUintn ((CHAR8
*)(BootFileNamePtr
+ 1));
362 *(BootFileNamePtr
+ 3) = TmpChar
;
363 BootFileNamePtr
+= 3;
365 *BootFileName
= *BootFileNamePtr
;
370 *BootFileName
= '\0';
380 Parse the Boot File Parameter option.
382 @param[in] BootFilePara The pointer to boot file parameter option data.
383 @param[out] BootFileSize The pointer to the parsed boot file size.
385 @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option.
386 @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option.
390 PxeBcExtractBootFileParam (
391 IN CHAR8
*BootFilePara
,
392 OUT UINT16
*BootFileSize
400 CopyMem (&Length
, BootFilePara
, sizeof (UINT16
));
401 Length
= NTOHS (Length
);
404 // The BootFile Size should be 1~5 byte ASCII strings
406 if (Length
< 1 || Length
> 5) {
407 return EFI_NOT_FOUND
;
411 // Extract the value of BootFile Size.
413 BootFilePara
= BootFilePara
+ sizeof (UINT16
);
415 for (Index
= 0; Index
< Length
; Index
++) {
416 if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit
, *(BootFilePara
+ Index
)))) {
417 return EFI_NOT_FOUND
;
420 Size
= (Size
+ Digit
) * 10;
424 if (Size
> PXEBC_DHCP6_MAX_BOOT_FILE_SIZE
) {
425 return EFI_NOT_FOUND
;
428 *BootFileSize
= (UINT16
) Size
;
434 Parse the cached DHCPv6 packet, including all the options.
436 @param[in] Cache6 The pointer to a cached DHCPv6 packet.
438 @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
439 @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.
443 PxeBcParseDhcp6Packet (
444 IN PXEBC_DHCP6_PACKET_CACHE
*Cache6
447 EFI_DHCP6_PACKET
*Offer
;
448 EFI_DHCP6_PACKET_OPTION
**Options
;
449 EFI_DHCP6_PACKET_OPTION
*Option
;
450 PXEBC_OFFER_TYPE OfferType
;
451 BOOLEAN IsProxyOffer
;
455 UINT32 EnterpriseNum
;
459 Offer
= &Cache6
->Packet
.Offer
;
460 Options
= Cache6
->OptList
;
462 ZeroMem (Cache6
->OptList
, sizeof (Cache6
->OptList
));
464 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Offer
->Dhcp6
.Option
);
466 Length
= GET_DHCP6_OPTION_SIZE (Offer
);
469 // OpLen and OpCode here are both stored in network order, since they are from original packet.
471 while (Offset
< Length
) {
473 if (NTOHS (Option
->OpCode
) == PXEBC_DHCP6_OPT_IA_NA
) {
474 Options
[PXEBC_DHCP6_IDX_IA_NA
] = Option
;
475 } else if (NTOHS (Option
->OpCode
) == PXEBC_DHCP6_OPT_BOOT_FILE_URL
) {
477 // The server sends this option to inform the client about an URL to a boot file.
479 Options
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] = Option
;
480 } else if (NTOHS (Option
->OpCode
) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM
) {
481 Options
[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM
] = Option
;
482 } else if (NTOHS (Option
->OpCode
) == PXEBC_DHCP6_OPT_VENDOR_CLASS
) {
483 Options
[PXEBC_DHCP6_IDX_VENDOR_CLASS
] = Option
;
486 Offset
+= (NTOHS (Option
->OpLen
) + 4);
487 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Offer
->Dhcp6
.Option
+ Offset
);
491 // The offer with assigned client address is NOT a proxy offer.
492 // An ia_na option, embeded with valid ia_addr option and a status_code of success.
494 Option
= Options
[PXEBC_DHCP6_IDX_IA_NA
];
495 if (Option
!= NULL
) {
496 Option
= PxeBcParseDhcp6Options (
498 NTOHS (Option
->OpLen
),
499 PXEBC_DHCP6_OPT_STATUS_CODE
501 if ((Option
!= NULL
&& Option
->Data
[0] == 0) || (Option
== NULL
)) {
502 IsProxyOffer
= FALSE
;
507 // The offer with "PXEClient" is a pxe offer.
509 Option
= Options
[PXEBC_DHCP6_IDX_VENDOR_CLASS
];
510 EnterpriseNum
= HTONL(PXEBC_DHCP6_ENTERPRISE_NUM
);
512 if (Option
!= NULL
&&
513 NTOHS(Option
->OpLen
) >= 13 &&
514 CompareMem (Option
->Data
, &EnterpriseNum
, sizeof (UINT32
)) == 0 &&
515 CompareMem (&Option
->Data
[6], DEFAULT_CLASS_ID_DATA
, 9) == 0) {
520 // Determine offer type of the dhcp6 packet.
524 // It's a binl offer only with PXEClient.
526 OfferType
= IsProxyOffer
? PxeOfferTypeProxyBinl
: PxeOfferTypeDhcpBinl
;
529 // It's a dhcp only offer, which is a pure dhcp6 offer packet.
531 OfferType
= PxeOfferTypeDhcpOnly
;
534 Cache6
->OfferType
= OfferType
;
541 Cache the DHCPv6 ack packet, and parse it on demand.
543 @param[in] Private The pointer to PxeBc private data.
544 @param[in] Ack The pointer to the DHCPv6 ack packet.
545 @param[in] Verified If TRUE, parse the ACK packet and store info into mode data.
550 IN PXEBC_PRIVATE_DATA
*Private
,
551 IN EFI_DHCP6_PACKET
*Ack
,
555 EFI_PXE_BASE_CODE_MODE
*Mode
;
557 Mode
= Private
->PxeBc
.Mode
;
559 PxeBcCacheDhcp6Packet (&Private
->DhcpAck
.Dhcp6
.Packet
.Ack
, Ack
);
563 // Parse the ack packet and store it into mode data if needed.
565 PxeBcParseDhcp6Packet (&Private
->DhcpAck
.Dhcp6
);
566 CopyMem (&Mode
->DhcpAck
.Dhcpv6
, &Ack
->Dhcp6
, Ack
->Length
);
567 Mode
->DhcpAckReceived
= TRUE
;
573 Cache the DHCPv6 proxy offer packet according to the received order.
575 @param[in] Private The pointer to PxeBc private data.
576 @param[in] OfferIndex The received order of offer packets.
580 PxeBcCopyDhcp6Proxy (
581 IN PXEBC_PRIVATE_DATA
*Private
,
585 EFI_PXE_BASE_CODE_MODE
*Mode
;
586 EFI_DHCP6_PACKET
*Offer
;
588 ASSERT (OfferIndex
< Private
->OfferNum
);
589 ASSERT (OfferIndex
< PXEBC_OFFER_MAX_NUM
);
591 Mode
= Private
->PxeBc
.Mode
;
592 Offer
= &Private
->OfferBuffer
[OfferIndex
].Dhcp6
.Packet
.Offer
;
595 // Cache the proxy offer packet and parse it.
597 PxeBcCacheDhcp6Packet (&Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
, Offer
);
598 PxeBcParseDhcp6Packet (&Private
->ProxyOffer
.Dhcp6
);
601 // Store this packet into mode data.
603 CopyMem (&Mode
->ProxyOffer
.Dhcpv6
, &Offer
->Dhcp6
, Offer
->Length
);
604 Mode
->ProxyOfferReceived
= TRUE
;
608 Seek the address of the first byte of the option header.
610 @param[in] Buf The pointer to the buffer.
611 @param[in] SeekLen The length to seek.
612 @param[in] OptType The option type.
614 @retval NULL If it failed to seek the option.
615 @retval others The position to the option.
619 PxeBcDhcp6SeekOption (
633 while (Cursor
< Buf
+ SeekLen
) {
634 OpCode
= ReadUnaligned16 ((UINT16
*) Cursor
);
635 if (OpCode
== HTONS (OptType
)) {
639 DataLen
= NTOHS (ReadUnaligned16 ((UINT16
*) (Cursor
+ 2)));
640 Cursor
+= (DataLen
+ 4);
648 Build and send out the request packet for the bootfile, and parse the reply.
650 @param[in] Private The pointer to PxeBc private data.
651 @param[in] Index PxeBc option boot item type.
653 @retval EFI_SUCCESS Successfully discovered the boot file.
654 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
655 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
656 @retval Others Failed to discover the boot file.
660 PxeBcRequestBootService (
661 IN PXEBC_PRIVATE_DATA
*Private
,
665 EFI_PXE_BASE_CODE_UDP_PORT SrcPort
;
666 EFI_PXE_BASE_CODE_UDP_PORT DestPort
;
667 EFI_PXE_BASE_CODE_MODE
*Mode
;
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
;
686 Request
= Private
->Dhcp6Request
;
687 ProxyOffer
= &Private
->OfferBuffer
[Index
].Dhcp6
.Packet
.Offer
;
688 SrcPort
= PXEBC_BS_DISCOVER_PORT
;
689 DestPort
= PXEBC_BS_DISCOVER_PORT
;
692 if (Request
== NULL
) {
693 return EFI_DEVICE_ERROR
;
696 Discover
= AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET
));
697 if (Discover
== NULL
) {
698 return EFI_OUT_OF_RESOURCES
;
702 // Build the request packet by the cached request packet before.
704 Discover
->TransactionId
= ProxyOffer
->Dhcp6
.Header
.TransactionId
;
705 Discover
->MessageType
= Request
->Dhcp6
.Header
.MessageType
;
706 RequestOpt
= Request
->Dhcp6
.Option
;
707 DiscoverOpt
= Discover
->DhcpOptions
;
708 DiscoverLen
= sizeof (EFI_DHCP6_HEADER
);
709 RequestLen
= DiscoverLen
;
712 // Find Server ID Option from ProxyOffer.
714 Option
= PxeBcDhcp6SeekOption (
715 ProxyOffer
->Dhcp6
.Option
,
716 ProxyOffer
->Length
- 4,
717 PXEBC_DHCP6_OPT_SERVER_ID
719 if (Option
== NULL
) {
720 return EFI_NOT_FOUND
;
724 // Add Server ID Option.
726 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) Option
)->OpLen
);
727 CopyMem (DiscoverOpt
, Option
, OpLen
+ 4);
728 DiscoverOpt
+= (OpLen
+ 4);
729 DiscoverLen
+= (OpLen
+ 4);
731 while (RequestLen
< Request
->Length
) {
732 OpCode
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpCode
);
733 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpLen
);
734 if (OpCode
!= EFI_DHCP6_IA_TYPE_NA
&&
735 OpCode
!= EFI_DHCP6_IA_TYPE_TA
&&
736 OpCode
!= PXEBC_DHCP6_OPT_SERVER_ID
739 // Copy all the options except IA option and Server ID
741 CopyMem (DiscoverOpt
, RequestOpt
, OpLen
+ 4);
742 DiscoverOpt
+= (OpLen
+ 4);
743 DiscoverLen
+= (OpLen
+ 4);
745 RequestOpt
+= (OpLen
+ 4);
746 RequestLen
+= (OpLen
+ 4);
750 // Update Elapsed option in the package
752 Option
= PxeBcDhcp6SeekOption (
753 Discover
->DhcpOptions
,
754 (UINT32
)(RequestLen
- 4),
755 PXEBC_DHCP6_OPT_ELAPSED_TIME
757 if (Option
!= NULL
) {
758 CalcElapsedTime (Private
);
759 WriteUnaligned16 ((UINT16
*)(Option
+ 4), HTONS((UINT16
) Private
->ElapsedTime
));
762 Status
= PxeBc
->UdpWrite (
776 if (EFI_ERROR (Status
)) {
781 // Cache the right PXE reply packet here, set valid flag later.
782 // Especially for PXE discover packet, store it into mode data here.
784 Reply
= &Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
;
785 ReadSize
= (UINTN
) Reply
->Size
;
788 // Start Udp6Read instance
790 Status
= Private
->Udp6Read
->Configure (Private
->Udp6Read
, &Private
->Udp6CfgData
);
791 if (EFI_ERROR (Status
)) {
795 Status
= PxeBc
->UdpRead (
797 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP
,
805 (VOID
*) &Reply
->Dhcp6
808 // Stop Udp6Read instance
810 Private
->Udp6Read
->Configure (Private
->Udp6Read
, NULL
);
812 if (EFI_ERROR (Status
)) {
819 Reply
->Length
= (UINT32
) ReadSize
;
826 Retry to request bootfile name by the BINL offer.
828 @param[in] Private The pointer to PxeBc private data.
829 @param[in] Index The received order of offer packets.
831 @retval EFI_SUCCESS Successfully retried a request for the bootfile name.
832 @retval EFI_DEVICE_ERROR Failed to retry the bootfile name.
836 PxeBcRetryDhcp6Binl (
837 IN PXEBC_PRIVATE_DATA
*Private
,
841 EFI_PXE_BASE_CODE_MODE
*Mode
;
842 PXEBC_DHCP6_PACKET_CACHE
*Offer
;
843 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
846 ASSERT (Index
< PXEBC_OFFER_MAX_NUM
);
847 ASSERT (Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
== PxeOfferTypeDhcpBinl
||
848 Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
== PxeOfferTypeProxyBinl
);
850 Mode
= Private
->PxeBc
.Mode
;
851 Private
->IsDoDiscover
= FALSE
;
852 Offer
= &Private
->OfferBuffer
[Index
].Dhcp6
;
853 if (Offer
->OfferType
== PxeOfferTypeDhcpBinl
) {
855 // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.
858 &Private
->ServerIp
.v6
,
859 &mAllDhcpRelayAndServersAddress
,
860 sizeof (EFI_IPv6_ADDRESS
)
863 ASSERT (Offer
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] != NULL
);
865 // Parse out the next server address from the last offer, and store it
867 Status
= PxeBcExtractBootFileUrl (
868 &Private
->BootFileName
,
869 &Private
->ServerIp
.v6
,
870 (CHAR8
*) (Offer
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
]->Data
),
871 NTOHS (Offer
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
]->OpLen
)
873 if (EFI_ERROR (Status
)) {
879 // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
881 Status
= PxeBcRequestBootService (Private
, Index
);
883 if (EFI_ERROR (Status
)) {
887 Cache6
= &Private
->ProxyOffer
.Dhcp6
;
888 Status
= PxeBcParseDhcp6Packet (Cache6
);
889 if (EFI_ERROR (Status
)) {
893 if (Cache6
->OfferType
!= PxeOfferTypeProxyPxe10
&&
894 Cache6
->OfferType
!= PxeOfferTypeProxyWfm11a
&&
895 Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] == NULL
) {
897 // This BINL ack doesn't have discovery option set or multicast option set
898 // or bootfile name specified.
900 return EFI_DEVICE_ERROR
;
903 Mode
->ProxyOfferReceived
= TRUE
;
905 &Mode
->ProxyOffer
.Dhcpv6
,
906 &Cache6
->Packet
.Offer
.Dhcp6
,
907 Cache6
->Packet
.Offer
.Length
915 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
917 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
918 @param[in] RcvdOffer The pointer to the received offer packet.
922 PxeBcCacheDhcp6Offer (
923 IN PXEBC_PRIVATE_DATA
*Private
,
924 IN EFI_DHCP6_PACKET
*RcvdOffer
927 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
928 EFI_DHCP6_PACKET
*Offer
;
929 PXEBC_OFFER_TYPE OfferType
;
931 Cache6
= &Private
->OfferBuffer
[Private
->OfferNum
].Dhcp6
;
932 Offer
= &Cache6
->Packet
.Offer
;
935 // Cache the content of DHCPv6 packet firstly.
937 PxeBcCacheDhcp6Packet (Offer
, RcvdOffer
);
940 // Validate the DHCPv6 packet, and parse the options and offer type.
942 if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6
))) {
947 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
949 OfferType
= Cache6
->OfferType
;
950 ASSERT (OfferType
< PxeOfferTypeMax
);
951 ASSERT (Private
->OfferCount
[OfferType
] < PXEBC_OFFER_MAX_NUM
);
953 if (IS_PROXY_OFFER (OfferType
)) {
955 // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
957 Private
->IsProxyRecved
= TRUE
;
959 if (OfferType
== PxeOfferTypeProxyBinl
) {
961 // Cache all proxy BINL offers.
963 Private
->OfferIndex
[OfferType
][Private
->OfferCount
[OfferType
]] = Private
->OfferNum
;
964 Private
->OfferCount
[OfferType
]++;
965 } else if (Private
->OfferCount
[OfferType
] > 0) {
967 // Only cache the first PXE10/WFM11a offer, and discard the others.
969 Private
->OfferIndex
[OfferType
][0] = Private
->OfferNum
;
970 Private
->OfferCount
[OfferType
] = 1;
976 // It's a DHCPv6 offer with yiaddr, and cache them all.
978 Private
->OfferIndex
[OfferType
][Private
->OfferCount
[OfferType
]] = Private
->OfferNum
;
979 Private
->OfferCount
[OfferType
]++;
987 Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
989 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
993 PxeBcSelectDhcp6Offer (
994 IN PXEBC_PRIVATE_DATA
*Private
999 PXEBC_OFFER_TYPE OfferType
;
1001 Private
->SelectIndex
= 0;
1003 if (Private
->IsOfferSorted
) {
1005 // Select offer by default policy.
1007 if (Private
->OfferCount
[PxeOfferTypeDhcpPxe10
] > 0) {
1009 // 1. DhcpPxe10 offer
1011 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpPxe10
][0] + 1;
1013 } else if (Private
->OfferCount
[PxeOfferTypeDhcpWfm11a
] > 0) {
1015 // 2. DhcpWfm11a offer
1017 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpWfm11a
][0] + 1;
1019 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1020 Private
->OfferCount
[PxeOfferTypeProxyPxe10
] > 0) {
1022 // 3. DhcpOnly offer and ProxyPxe10 offer.
1024 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1025 Private
->SelectProxyType
= PxeOfferTypeProxyPxe10
;
1027 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1028 Private
->OfferCount
[PxeOfferTypeProxyWfm11a
] > 0) {
1030 // 4. DhcpOnly offer and ProxyWfm11a offer.
1032 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1033 Private
->SelectProxyType
= PxeOfferTypeProxyWfm11a
;
1035 } else if (Private
->OfferCount
[PxeOfferTypeDhcpBinl
] > 0) {
1037 // 5. DhcpBinl offer.
1039 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpBinl
][0] + 1;
1041 } else if (Private
->OfferCount
[PxeOfferTypeDhcpOnly
] > 0 &&
1042 Private
->OfferCount
[PxeOfferTypeProxyBinl
] > 0) {
1044 // 6. DhcpOnly offer and ProxyBinl offer.
1046 Private
->SelectIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][0] + 1;
1047 Private
->SelectProxyType
= PxeOfferTypeProxyBinl
;
1051 // 7. DhcpOnly offer with bootfilename.
1053 for (Index
= 0; Index
< Private
->OfferCount
[PxeOfferTypeDhcpOnly
]; Index
++) {
1054 OfferIndex
= Private
->OfferIndex
[PxeOfferTypeDhcpOnly
][Index
];
1055 if (Private
->OfferBuffer
[OfferIndex
].Dhcp6
.OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] != NULL
) {
1056 Private
->SelectIndex
= OfferIndex
+ 1;
1063 // Select offer by received order.
1065 for (Index
= 0; Index
< Private
->OfferNum
; Index
++) {
1067 OfferType
= Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
;
1069 if (IS_PROXY_OFFER (OfferType
)) {
1071 // Skip proxy offers
1076 if (!Private
->IsProxyRecved
&&
1077 OfferType
== PxeOfferTypeDhcpOnly
&&
1078 Private
->OfferBuffer
[Index
].Dhcp6
.OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] == NULL
) {
1080 // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
1085 Private
->SelectIndex
= Index
+ 1;
1093 Handle the DHCPv6 offer packet.
1095 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1097 @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully.
1098 @retval EFI_NO_RESPONSE No response to the following request packet.
1102 PxeBcHandleDhcp6Offer (
1103 IN PXEBC_PRIVATE_DATA
*Private
1106 PXEBC_DHCP6_PACKET_CACHE
*Cache6
;
1108 PXEBC_OFFER_TYPE OfferType
;
1113 ASSERT (Private
->SelectIndex
> 0);
1114 SelectIndex
= (UINT32
) (Private
->SelectIndex
- 1);
1115 ASSERT (SelectIndex
< PXEBC_OFFER_MAX_NUM
);
1116 Cache6
= &Private
->OfferBuffer
[SelectIndex
].Dhcp6
;
1117 Status
= EFI_SUCCESS
;
1119 if (Cache6
->OfferType
== PxeOfferTypeDhcpBinl
) {
1121 // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
1123 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, SelectIndex
))) {
1124 Status
= EFI_NO_RESPONSE
;
1126 } else if (Cache6
->OfferType
== PxeOfferTypeDhcpOnly
) {
1128 if (Private
->IsProxyRecved
) {
1130 // DhcpOnly offer is selected, so need try to request bootfilename.
1133 if (Private
->IsOfferSorted
) {
1135 // The proxy offer should be determined if select by default policy.
1136 // IsOfferSorted means all offers are labeled by OfferIndex.
1138 ASSERT (Private
->OfferCount
[Private
->SelectProxyType
] > 0);
1140 if (Private
->SelectProxyType
== PxeOfferTypeProxyBinl
) {
1142 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1144 for (Index
= 0; Index
< Private
->OfferCount
[Private
->SelectProxyType
]; Index
++) {
1146 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][Index
];
1147 if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, ProxyIndex
))) {
1151 if (Index
== Private
->OfferCount
[Private
->SelectProxyType
]) {
1152 Status
= EFI_NO_RESPONSE
;
1156 // For other proxy offers (pxe10 or wfm11a), only one is buffered.
1158 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][0];
1162 // The proxy offer should not be determined if select by received order.
1164 Status
= EFI_NO_RESPONSE
;
1166 for (Index
= 0; Index
< Private
->OfferNum
; Index
++) {
1168 OfferType
= Private
->OfferBuffer
[Index
].Dhcp6
.OfferType
;
1170 if (!IS_PROXY_OFFER (OfferType
)) {
1172 // Skip non proxy dhcp offers.
1177 if (OfferType
== PxeOfferTypeProxyBinl
) {
1179 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1181 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private
, Index
))) {
1186 Private
->SelectProxyType
= OfferType
;
1188 Status
= EFI_SUCCESS
;
1193 if (!EFI_ERROR (Status
) && Private
->SelectProxyType
!= PxeOfferTypeProxyBinl
) {
1195 // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
1197 PxeBcCopyDhcp6Proxy (Private
, ProxyIndex
);
1201 // Othewise, the bootfilename must be included in DhcpOnly offer.
1203 ASSERT (Cache6
->OptList
[PXEBC_DHCP6_IDX_BOOT_FILE_URL
] != NULL
);
1207 if (!EFI_ERROR (Status
)) {
1209 // All PXE boot information is ready by now.
1211 PxeBcCopyDhcp6Ack (Private
, &Private
->DhcpAck
.Dhcp6
.Packet
.Ack
, TRUE
);
1212 Private
->PxeBc
.Mode
->DhcpDiscoverValid
= TRUE
;
1220 Unregister the address by Ip6Config protocol.
1222 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1226 PxeBcUnregisterIp6Address (
1227 IN PXEBC_PRIVATE_DATA
*Private
1230 if (Private
->Ip6Policy
!= PXEBC_IP6_POLICY_MAX
) {
1232 // PXE driver change the policy of IP6 driver, it's a chance to recover.
1233 // Keep the point and there is no enough requirements to do recovery.
1239 Check whether IP driver could route the message which will be sent to ServerIp address.
1241 This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid
1242 route is found in IP6 route table, the address will be filed in GatewayAddr and return.
1244 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1245 @param[in] TimeOutInSecond Timeout value in seconds.
1246 @param[out] GatewayAddr Pointer to store the gateway IP address.
1248 @retval EFI_SUCCESS Found a valid gateway address successfully.
1249 @retval EFI_TIMEOUT The operation is time out.
1250 @retval Other Unexpect error happened.
1254 PxeBcCheckRouteTable (
1255 IN PXEBC_PRIVATE_DATA
*Private
,
1256 IN UINTN TimeOutInSecond
,
1257 OUT EFI_IPv6_ADDRESS
*GatewayAddr
1261 EFI_IP6_PROTOCOL
*Ip6
;
1262 EFI_IP6_MODE_DATA Ip6ModeData
;
1264 EFI_EVENT TimeOutEvt
;
1266 BOOLEAN GatewayIsFound
;
1268 ASSERT (GatewayAddr
!= NULL
);
1269 ASSERT (Private
!= NULL
);
1272 GatewayIsFound
= FALSE
;
1275 ZeroMem (GatewayAddr
, sizeof (EFI_IPv6_ADDRESS
));
1278 Status
= Ip6
->GetModeData (Ip6
, &Ip6ModeData
, NULL
, NULL
);
1279 if (EFI_ERROR (Status
)) {
1284 // Find out the gateway address which can route the message whcih send to ServerIp.
1286 for (Index
= 0; Index
< Ip6ModeData
.RouteCount
; Index
++) {
1287 if (NetIp6IsNetEqual (&Private
->ServerIp
.v6
, &Ip6ModeData
.RouteTable
[Index
].Destination
, Ip6ModeData
.RouteTable
[Index
].PrefixLength
)) {
1288 IP6_COPY_ADDRESS (GatewayAddr
, &Ip6ModeData
.RouteTable
[Index
].Gateway
);
1289 GatewayIsFound
= TRUE
;
1294 if (Ip6ModeData
.AddressList
!= NULL
) {
1295 FreePool (Ip6ModeData
.AddressList
);
1297 if (Ip6ModeData
.GroupTable
!= NULL
) {
1298 FreePool (Ip6ModeData
.GroupTable
);
1300 if (Ip6ModeData
.RouteTable
!= NULL
) {
1301 FreePool (Ip6ModeData
.RouteTable
);
1303 if (Ip6ModeData
.NeighborCache
!= NULL
) {
1304 FreePool (Ip6ModeData
.NeighborCache
);
1306 if (Ip6ModeData
.PrefixTable
!= NULL
) {
1307 FreePool (Ip6ModeData
.PrefixTable
);
1309 if (Ip6ModeData
.IcmpTypeList
!= NULL
) {
1310 FreePool (Ip6ModeData
.IcmpTypeList
);
1313 if (GatewayIsFound
|| RetryCount
== TimeOutInSecond
) {
1320 // Delay 1 second then recheck it again.
1322 if (TimeOutEvt
== NULL
) {
1323 Status
= gBS
->CreateEvent (
1330 if (EFI_ERROR (Status
)) {
1335 Status
= gBS
->SetTimer (TimeOutEvt
, TimerRelative
, TICKS_PER_SECOND
);
1336 if (EFI_ERROR (Status
)) {
1339 while (EFI_ERROR (gBS
->CheckEvent (TimeOutEvt
))) {
1345 if (TimeOutEvt
!= NULL
) {
1346 gBS
->CloseEvent (TimeOutEvt
);
1349 if (GatewayIsFound
) {
1350 Status
= EFI_SUCCESS
;
1351 } else if (RetryCount
== TimeOutInSecond
) {
1352 Status
= EFI_TIMEOUT
;
1359 Register the ready station address and gateway by Ip6Config protocol.
1361 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1362 @param[in] Address The pointer to the ready address.
1364 @retval EFI_SUCCESS Registered the address succesfully.
1365 @retval Others Failed to register the address.
1369 PxeBcRegisterIp6Address (
1370 IN PXEBC_PRIVATE_DATA
*Private
,
1371 IN EFI_IPv6_ADDRESS
*Address
1374 EFI_IP6_PROTOCOL
*Ip6
;
1375 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1376 EFI_IP6_CONFIG_POLICY Policy
;
1377 EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr
;
1378 EFI_IPv6_ADDRESS GatewayAddr
;
1380 EFI_EVENT TimeOutEvt
;
1381 EFI_EVENT MappedEvt
;
1383 UINT64 DadTriggerTime
;
1384 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits
;
1386 Status
= EFI_SUCCESS
;
1389 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1390 Ip6Cfg
= Private
->Ip6Cfg
;
1393 ZeroMem (&CfgAddr
, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS
));
1394 CopyMem (&CfgAddr
.Address
, Address
, sizeof (EFI_IPv6_ADDRESS
));
1396 Status
= Ip6
->Configure (Ip6
, &Private
->Ip6CfgData
);
1397 if (EFI_ERROR (Status
)) {
1402 // Retrieve the gateway address from IP6 route table.
1404 Status
= PxeBcCheckRouteTable (Private
, PXEBC_IP6_ROUTE_TABLE_TIMEOUT
, &GatewayAddr
);
1405 if (EFI_ERROR (Status
)) {
1410 // There is no channel between IP6 and PXE driver about address setting,
1411 // so it has to set the new address by Ip6ConfigProtocol manually.
1413 Policy
= Ip6ConfigPolicyManual
;
1414 Status
= Ip6Cfg
->SetData (
1416 Ip6ConfigDataTypePolicy
,
1417 sizeof(EFI_IP6_CONFIG_POLICY
),
1420 if (EFI_ERROR (Status
)) {
1422 // There is no need to recover later.
1424 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1429 // Get Duplicate Address Detection Transmits count.
1431 DataSize
= sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS
);
1432 Status
= Ip6Cfg
->GetData (
1434 Ip6ConfigDataTypeDupAddrDetectTransmits
,
1438 if (EFI_ERROR (Status
)) {
1443 // Create a timer as setting address timeout event since DAD in IP6 driver.
1445 Status
= gBS
->CreateEvent (
1452 if (EFI_ERROR (Status
)) {
1457 // Create a notify event to set address flag when DAD if IP6 driver succeeded.
1459 Status
= gBS
->CreateEvent (
1463 &Private
->IsAddressOk
,
1466 if (EFI_ERROR (Status
)) {
1470 Status
= Ip6Cfg
->RegisterDataNotify (
1472 Ip6ConfigDataTypeManualAddress
,
1475 if (EFI_ERROR(Status
)) {
1479 Status
= Ip6Cfg
->SetData (
1481 Ip6ConfigDataTypeManualAddress
,
1482 sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS
),
1485 if (EFI_ERROR(Status
) && Status
!= EFI_NOT_READY
) {
1490 // Start the 5 secondes timer to wait for setting address.
1492 Status
= EFI_NO_MAPPING
;
1493 DadTriggerTime
= TICKS_PER_SECOND
* DadXmits
.DupAddrDetectTransmits
+ PXEBC_DAD_ADDITIONAL_DELAY
;
1494 gBS
->SetTimer (TimeOutEvt
, TimerRelative
, DadTriggerTime
);
1496 while (EFI_ERROR (gBS
->CheckEvent (TimeOutEvt
))) {
1498 if (Private
->IsAddressOk
) {
1499 Status
= EFI_SUCCESS
;
1505 // Set the default gateway address back if needed.
1507 if (!NetIp6IsUnspecifiedAddr (&GatewayAddr
)) {
1508 Status
= Ip6Cfg
->SetData (
1510 Ip6ConfigDataTypeGateway
,
1511 sizeof (EFI_IPv6_ADDRESS
),
1514 if (EFI_ERROR (Status
)) {
1520 if (MappedEvt
!= NULL
) {
1521 Ip6Cfg
->UnregisterDataNotify (
1523 Ip6ConfigDataTypeManualAddress
,
1526 gBS
->CloseEvent (MappedEvt
);
1528 if (TimeOutEvt
!= NULL
) {
1529 gBS
->CloseEvent (TimeOutEvt
);
1535 Set the IP6 policy to Automatic.
1537 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1539 @retval EFI_SUCCESS Switch the IP policy succesfully.
1540 @retval Others Unexpect error happened.
1545 IN PXEBC_PRIVATE_DATA
*Private
1548 EFI_IP6_CONFIG_POLICY Policy
;
1550 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1553 Ip6Cfg
= Private
->Ip6Cfg
;
1554 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1557 // Get and store the current policy of IP6 driver.
1559 Status
= Ip6Cfg
->GetData (
1561 Ip6ConfigDataTypePolicy
,
1565 if (EFI_ERROR (Status
)) {
1569 if (Private
->Ip6Policy
== Ip6ConfigPolicyManual
) {
1570 Policy
= Ip6ConfigPolicyAutomatic
;
1571 Status
= Ip6Cfg
->SetData (
1573 Ip6ConfigDataTypePolicy
,
1574 sizeof(EFI_IP6_CONFIG_POLICY
),
1577 if (EFI_ERROR (Status
)) {
1579 // There is no need to recover later.
1581 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1589 This function will register the station IP address and flush IP instance to start using the new IP address.
1591 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1593 @retval EFI_SUCCESS The new IP address has been configured successfully.
1594 @retval Others Failed to configure the address.
1598 PxeBcSetIp6Address (
1599 IN PXEBC_PRIVATE_DATA
*Private
1603 EFI_DHCP6_PROTOCOL
*Dhcp6
;
1605 Dhcp6
= Private
->Dhcp6
;
1607 CopyMem (&Private
->StationIp
.v6
, &Private
->TmpStationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1608 CopyMem (&Private
->PxeBc
.Mode
->StationIp
.v6
, &Private
->StationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1610 Status
= PxeBcRegisterIp6Address (Private
, &Private
->StationIp
.v6
);
1611 if (EFI_ERROR (Status
)) {
1612 Dhcp6
->Stop (Dhcp6
);
1616 Status
= PxeBcFlushStationIp (Private
, &Private
->StationIp
, NULL
);
1617 if (EFI_ERROR (Status
)) {
1618 PxeBcUnregisterIp6Address (Private
);
1619 Dhcp6
->Stop (Dhcp6
);
1623 AsciiPrint ("\n Station IP address is ");
1624 PxeBcShowIp6Addr (&Private
->StationIp
.v6
);
1630 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
1631 to intercept events that occurred in the configuration process.
1633 @param[in] This The pointer to the EFI DHCPv6 Protocol.
1634 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
1635 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
1636 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
1638 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
1639 @param[out] NewPacket The packet that is used to replace the Packet above.
1641 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
1642 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
1643 driver will continue to wait for more packets.
1644 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
1649 PxeBcDhcp6CallBack (
1650 IN EFI_DHCP6_PROTOCOL
*This
,
1652 IN EFI_DHCP6_STATE CurrentState
,
1653 IN EFI_DHCP6_EVENT Dhcp6Event
,
1654 IN EFI_DHCP6_PACKET
*Packet
,
1655 OUT EFI_DHCP6_PACKET
**NewPacket OPTIONAL
1658 PXEBC_PRIVATE_DATA
*Private
;
1659 EFI_PXE_BASE_CODE_MODE
*Mode
;
1660 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
*Callback
;
1661 EFI_DHCP6_PACKET
*SelectAd
;
1665 if ((Dhcp6Event
!= Dhcp6RcvdAdvertise
) &&
1666 (Dhcp6Event
!= Dhcp6SelectAdvertise
) &&
1667 (Dhcp6Event
!= Dhcp6SendSolicit
) &&
1668 (Dhcp6Event
!= Dhcp6SendRequest
) &&
1669 (Dhcp6Event
!= Dhcp6RcvdReply
)) {
1673 ASSERT (Packet
!= NULL
);
1675 Private
= (PXEBC_PRIVATE_DATA
*) Context
;
1676 Mode
= Private
->PxeBc
.Mode
;
1677 Callback
= Private
->PxeBcCallback
;
1680 // Callback to user when any traffic ocurred if has.
1682 if (Dhcp6Event
!= Dhcp6SelectAdvertise
&& Callback
!= NULL
) {
1683 Received
= (BOOLEAN
) (Dhcp6Event
== Dhcp6RcvdAdvertise
|| Dhcp6Event
== Dhcp6RcvdReply
);
1684 Status
= Callback
->Callback (
1689 (EFI_PXE_BASE_CODE_PACKET
*) &Packet
->Dhcp6
1691 if (Status
!= EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
) {
1696 Status
= EFI_SUCCESS
;
1698 switch (Dhcp6Event
) {
1700 case Dhcp6SendSolicit
:
1702 // Record the first Solicate msg time
1704 if (Private
->SolicitTimes
== 0) {
1705 CalcElapsedTime (Private
);
1706 Private
->SolicitTimes
++;
1709 // Cache the dhcp discover packet to mode data directly.
1711 CopyMem (&Mode
->DhcpDiscover
.Dhcpv4
, &Packet
->Dhcp6
, Packet
->Length
);
1714 case Dhcp6RcvdAdvertise
:
1715 Status
= EFI_NOT_READY
;
1716 if (Private
->OfferNum
< PXEBC_OFFER_MAX_NUM
) {
1718 // Cache the dhcp offers to OfferBuffer[] for select later, and record
1719 // the OfferIndex and OfferCount.
1721 PxeBcCacheDhcp6Offer (Private
, Packet
);
1725 case Dhcp6SendRequest
:
1727 // Store the request packet as seed packet for discover.
1729 if (Private
->Dhcp6Request
!= NULL
) {
1730 FreePool (Private
->Dhcp6Request
);
1732 Private
->Dhcp6Request
= AllocateZeroPool (Packet
->Size
);
1733 if (Private
->Dhcp6Request
!= NULL
) {
1734 CopyMem (Private
->Dhcp6Request
, Packet
, Packet
->Size
);
1738 case Dhcp6SelectAdvertise
:
1740 // Select offer by the default policy or by order, and record the SelectIndex
1741 // and SelectProxyType.
1743 PxeBcSelectDhcp6Offer (Private
);
1745 if (Private
->SelectIndex
== 0) {
1746 Status
= EFI_ABORTED
;
1748 ASSERT (NewPacket
!= NULL
);
1749 SelectAd
= &Private
->OfferBuffer
[Private
->SelectIndex
- 1].Dhcp6
.Packet
.Offer
;
1750 *NewPacket
= AllocateZeroPool (SelectAd
->Size
);
1751 ASSERT (*NewPacket
!= NULL
);
1752 CopyMem (*NewPacket
, SelectAd
, SelectAd
->Size
);
1756 case Dhcp6RcvdReply
:
1758 // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
1759 // without verification.
1761 ASSERT (Private
->SelectIndex
!= 0);
1762 PxeBcCopyDhcp6Ack (Private
, Packet
, FALSE
);
1774 Build and send out the request packet for the bootfile, and parse the reply.
1776 @param[in] Private The pointer to PxeBc private data.
1777 @param[in] Type PxeBc option boot item type.
1778 @param[in] Layer The pointer to option boot item layer.
1779 @param[in] UseBis Use BIS or not.
1780 @param[in] DestIp The pointer to the server address.
1782 @retval EFI_SUCCESS Successfully discovered the boot file.
1783 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1784 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
1785 @retval Others Failed to discover the boot file.
1789 PxeBcDhcp6Discover (
1790 IN PXEBC_PRIVATE_DATA
*Private
,
1794 IN EFI_IP_ADDRESS
*DestIp
1797 EFI_PXE_BASE_CODE_UDP_PORT SrcPort
;
1798 EFI_PXE_BASE_CODE_UDP_PORT DestPort
;
1799 EFI_PXE_BASE_CODE_MODE
*Mode
;
1800 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
1801 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*Discover
;
1803 EFI_DHCP6_PACKET
*Request
;
1805 EFI_DHCP6_PACKET
*Reply
;
1815 PxeBc
= &Private
->PxeBc
;
1817 Request
= Private
->Dhcp6Request
;
1818 SrcPort
= PXEBC_BS_DISCOVER_PORT
;
1819 DestPort
= PXEBC_BS_DISCOVER_PORT
;
1822 if (!UseBis
&& Layer
!= NULL
) {
1823 *Layer
&= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK
;
1826 if (Request
== NULL
) {
1827 return EFI_DEVICE_ERROR
;
1830 Discover
= AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET
));
1831 if (Discover
== NULL
) {
1832 return EFI_OUT_OF_RESOURCES
;
1836 // Build the discover packet by the cached request packet before.
1838 Xid
= NET_RANDOM (NetRandomInitSeed ());
1839 Discover
->TransactionId
= HTONL (Xid
);
1840 Discover
->MessageType
= Request
->Dhcp6
.Header
.MessageType
;
1841 RequestOpt
= Request
->Dhcp6
.Option
;
1842 DiscoverOpt
= Discover
->DhcpOptions
;
1843 DiscoverLen
= sizeof (EFI_DHCP6_HEADER
);
1844 RequestLen
= DiscoverLen
;
1846 while (RequestLen
< Request
->Length
) {
1847 OpCode
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpCode
);
1848 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpLen
);
1849 if (OpCode
!= EFI_DHCP6_IA_TYPE_NA
&&
1850 OpCode
!= EFI_DHCP6_IA_TYPE_TA
) {
1852 // Copy all the options except IA option.
1854 CopyMem (DiscoverOpt
, RequestOpt
, OpLen
+ 4);
1855 DiscoverOpt
+= (OpLen
+ 4);
1856 DiscoverLen
+= (OpLen
+ 4);
1858 RequestOpt
+= (OpLen
+ 4);
1859 RequestLen
+= (OpLen
+ 4);
1862 Status
= PxeBc
->UdpWrite (
1868 &Private
->StationIp
,
1875 if (EFI_ERROR (Status
)) {
1880 // Cache the right PXE reply packet here, set valid flag later.
1881 // Especially for PXE discover packet, store it into mode data here.
1883 if (Private
->IsDoDiscover
) {
1884 CopyMem (&Mode
->PxeDiscover
.Dhcpv6
, Discover
, DiscoverLen
);
1885 Reply
= &Private
->PxeReply
.Dhcp6
.Packet
.Ack
;
1887 Reply
= &Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
;
1889 ReadSize
= (UINTN
) Reply
->Size
;
1892 // Start Udp6Read instance
1894 Status
= Private
->Udp6Read
->Configure (Private
->Udp6Read
, &Private
->Udp6CfgData
);
1895 if (EFI_ERROR (Status
)) {
1899 Status
= PxeBc
->UdpRead (
1902 &Private
->StationIp
,
1909 (VOID
*) &Reply
->Dhcp6
1912 // Stop Udp6Read instance
1914 Private
->Udp6Read
->Configure (Private
->Udp6Read
, NULL
);
1915 if (EFI_ERROR (Status
)) {
1924 Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
1926 @param[in] Private The pointer to PxeBc private data.
1927 @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL
1929 @retval EFI_SUCCESS The S.A.R.R. process successfully finished.
1930 @retval Others Failed to finish the S.A.R.R. process.
1935 IN PXEBC_PRIVATE_DATA
*Private
,
1936 IN EFI_DHCP6_PROTOCOL
*Dhcp6
1939 EFI_PXE_BASE_CODE_MODE
*PxeMode
;
1940 EFI_DHCP6_CONFIG_DATA Config
;
1941 EFI_DHCP6_MODE_DATA Mode
;
1942 EFI_DHCP6_RETRANSMISSION
*Retransmit
;
1943 EFI_DHCP6_PACKET_OPTION
*OptList
[PXEBC_DHCP6_OPTION_MAX_NUM
];
1944 UINT8 Buffer
[PXEBC_DHCP6_OPTION_MAX_SIZE
];
1947 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1948 EFI_STATUS TimerStatus
;
1950 UINT64 GetMappingTimeOut
;
1952 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits
;
1954 Status
= EFI_SUCCESS
;
1955 PxeMode
= Private
->PxeBc
.Mode
;
1956 Ip6Cfg
= Private
->Ip6Cfg
;
1960 // Build option list for the request packet.
1962 OptCount
= PxeBcBuildDhcp6Options (Private
, OptList
, Buffer
);
1963 ASSERT (OptCount
> 0);
1965 Retransmit
= AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION
));
1966 if (Retransmit
== NULL
) {
1967 return EFI_OUT_OF_RESOURCES
;
1970 ZeroMem (&Mode
, sizeof (EFI_DHCP6_MODE_DATA
));
1971 ZeroMem (&Config
, sizeof (EFI_DHCP6_CONFIG_DATA
));
1973 Config
.OptionCount
= OptCount
;
1974 Config
.OptionList
= OptList
;
1975 Config
.Dhcp6Callback
= PxeBcDhcp6CallBack
;
1976 Config
.CallbackContext
= Private
;
1977 Config
.IaInfoEvent
= NULL
;
1978 Config
.RapidCommit
= FALSE
;
1979 Config
.ReconfigureAccept
= FALSE
;
1980 Config
.IaDescriptor
.IaId
= Private
->IaId
;
1981 Config
.IaDescriptor
.Type
= EFI_DHCP6_IA_TYPE_NA
;
1982 Config
.SolicitRetransmission
= Retransmit
;
1983 Retransmit
->Irt
= 4;
1984 Retransmit
->Mrc
= 4;
1985 Retransmit
->Mrt
= 32;
1986 Retransmit
->Mrd
= 60;
1989 // Configure the DHCPv6 instance for PXE boot.
1991 Status
= Dhcp6
->Configure (Dhcp6
, &Config
);
1992 FreePool (Retransmit
);
1993 if (EFI_ERROR (Status
)) {
1998 // Initialize the record fields for DHCPv6 offer in private data.
2000 Private
->IsProxyRecved
= FALSE
;
2001 Private
->OfferNum
= 0;
2002 Private
->SelectIndex
= 0;
2003 ZeroMem (Private
->OfferCount
, sizeof (Private
->OfferCount
));
2004 ZeroMem (Private
->OfferIndex
, sizeof (Private
->OfferIndex
));
2008 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
2010 Status
= Dhcp6
->Start (Dhcp6
);
2011 if (Status
== EFI_NO_MAPPING
) {
2013 // IP6 Linklocal address is not available for use, so stop current Dhcp process
2014 // and wait for duplicate address detection to finish.
2016 Dhcp6
->Stop (Dhcp6
);
2019 // Get Duplicate Address Detection Transmits count.
2021 DataSize
= sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS
);
2022 Status
= Ip6Cfg
->GetData (
2024 Ip6ConfigDataTypeDupAddrDetectTransmits
,
2028 if (EFI_ERROR (Status
)) {
2029 Dhcp6
->Configure (Dhcp6
, NULL
);
2033 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
2034 if (EFI_ERROR (Status
)) {
2035 Dhcp6
->Configure (Dhcp6
, NULL
);
2039 GetMappingTimeOut
= TICKS_PER_SECOND
* DadXmits
.DupAddrDetectTransmits
+ PXEBC_DAD_ADDITIONAL_DELAY
;
2040 Status
= gBS
->SetTimer (Timer
, TimerRelative
, GetMappingTimeOut
);
2041 if (EFI_ERROR (Status
)) {
2042 gBS
->CloseEvent (Timer
);
2043 Dhcp6
->Configure (Dhcp6
, NULL
);
2049 TimerStatus
= gBS
->CheckEvent (Timer
);
2050 if (!EFI_ERROR (TimerStatus
)) {
2051 Status
= Dhcp6
->Start (Dhcp6
);
2053 } while (TimerStatus
== EFI_NOT_READY
);
2055 gBS
->CloseEvent (Timer
);
2057 if (EFI_ERROR (Status
)) {
2058 if (Status
== EFI_ICMP_ERROR
) {
2059 PxeMode
->IcmpErrorReceived
= TRUE
;
2061 Dhcp6
->Configure (Dhcp6
, NULL
);
2066 // Get the acquired IPv6 address and store them.
2068 Status
= Dhcp6
->GetModeData (Dhcp6
, &Mode
, NULL
);
2069 if (EFI_ERROR (Status
)) {
2070 Dhcp6
->Stop (Dhcp6
);
2074 ASSERT (Mode
.Ia
->State
== Dhcp6Bound
);
2076 // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the
2077 // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when
2078 // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as
2079 // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery
2080 // to find a valid router address.
2082 CopyMem (&Private
->TmpStationIp
.v6
, &Mode
.Ia
->IaAddress
[0].IpAddress
, sizeof (EFI_IPv6_ADDRESS
));
2085 // Check the selected offer whether BINL retry is needed.
2087 Status
= PxeBcHandleDhcp6Offer (Private
);
2088 if (EFI_ERROR (Status
)) {
2089 Dhcp6
->Stop (Dhcp6
);