2 Functions implementation related with DHCPv6 for UefiPxeBc Driver.
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php.
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include "PxeBcImpl.h"
20 // Well-known multi-cast address defined in section-24.1 of rfc-3315
22 // ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
24 EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress
= {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
27 Parse out a DHCPv6 option by OptTag, and find the position in buffer.
29 @param[in] Buffer The pointer to the option buffer.
30 @param[in] Length Length of the option buffer.
31 @param[in] OptTag The required option tag.
33 @retval NULL Failed to parse the required option.
34 @retval Others The postion of the required option in buffer.
37 EFI_DHCP6_PACKET_OPTION
*
38 PxeBcParseDhcp6Options (
44 EFI_DHCP6_PACKET_OPTION
*Option
;
47 Option
= (EFI_DHCP6_PACKET_OPTION
*) Buffer
;
51 // OpLen and OpCode here are both stored in network order.
53 while (Offset
< Length
) {
55 if (NTOHS (Option
->OpCode
) == OptTag
) {
60 Offset
+= (NTOHS(Option
->OpLen
) + 4);
61 Option
= (EFI_DHCP6_PACKET_OPTION
*) (Buffer
+ Offset
);
69 Build the options buffer for the DHCPv6 request packet.
71 @param[in] Private The pointer to PxeBc private data.
72 @param[out] OptList The pointer to the option pointer array.
73 @param[in] Buffer The pointer to the buffer to contain the option list.
75 @return Index The count of the built-in options.
79 PxeBcBuildDhcp6Options (
80 IN PXEBC_PRIVATE_DATA
*Private
,
81 OUT EFI_DHCP6_PACKET_OPTION
**OptList
,
85 PXEBC_DHCP6_OPTION_ENTRY OptEnt
;
90 OptList
[0] = (EFI_DHCP6_PACKET_OPTION
*) Buffer
;
93 // Append client option request option
95 OptList
[Index
]->OpCode
= HTONS (DHCP6_OPT_ORO
);
96 OptList
[Index
]->OpLen
= HTONS (4);
97 OptEnt
.Oro
= (PXEBC_DHCP6_OPTION_ORO
*) OptList
[Index
]->Data
;
98 OptEnt
.Oro
->OpCode
[0] = HTONS(DHCP6_OPT_BOOT_FILE_URL
);
99 OptEnt
.Oro
->OpCode
[1] = HTONS(DHCP6_OPT_BOOT_FILE_PARAM
);
101 OptList
[Index
] = GET_NEXT_DHCP6_OPTION (OptList
[Index
- 1]);
104 // Append client network device interface option
106 OptList
[Index
]->OpCode
= HTONS (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 (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 (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
) == DHCP6_OPT_IA_NA
) {
475 Options
[PXEBC_DHCP6_IDX_IA_NA
] = Option
;
476 } else if (NTOHS (Option
->OpCode
) == 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
) == DHCP6_OPT_BOOT_FILE_PARAM
) {
482 Options
[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM
] = Option
;
483 } else if (NTOHS (Option
->OpCode
) == 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 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,
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
!= 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 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 MappedEvt
;
1382 EFI_IPv6_ADDRESS
*Ip6Addr
;
1385 Status
= EFI_SUCCESS
;
1388 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1389 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 // Create a notify event to set address flag when DAD if IP6 driver succeeded.
1431 Status
= gBS
->CreateEvent (
1435 &Private
->IsAddressOk
,
1438 if (EFI_ERROR (Status
)) {
1442 Private
->IsAddressOk
= FALSE
;
1443 Status
= Ip6Cfg
->RegisterDataNotify (
1445 Ip6ConfigDataTypeManualAddress
,
1448 if (EFI_ERROR(Status
)) {
1452 Status
= Ip6Cfg
->SetData (
1454 Ip6ConfigDataTypeManualAddress
,
1455 sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS
),
1458 if (EFI_ERROR(Status
) && Status
!= EFI_NOT_READY
) {
1460 } else if (Status
== EFI_NOT_READY
) {
1462 // Poll the network until the asynchronous process is finished.
1464 while (!Private
->IsAddressOk
) {
1468 // Check whether the IP6 address setting is successed.
1471 Status
= Ip6Cfg
->GetData (
1473 Ip6ConfigDataTypeManualAddress
,
1477 if (Status
!= EFI_BUFFER_TOO_SMALL
|| DataSize
== 0) {
1478 Status
= EFI_DEVICE_ERROR
;
1482 Ip6Addr
= AllocatePool (DataSize
);
1483 if (Ip6Addr
== NULL
) {
1484 return EFI_OUT_OF_RESOURCES
;
1486 Status
= Ip6Cfg
->GetData (
1488 Ip6ConfigDataTypeManualAddress
,
1492 if (EFI_ERROR (Status
)) {
1493 Status
= EFI_DEVICE_ERROR
;
1497 for (Index
= 0; Index
< DataSize
/ sizeof (EFI_IPv6_ADDRESS
); Index
++) {
1498 if (CompareMem (Ip6Addr
+ Index
, Address
, sizeof (EFI_IPv6_ADDRESS
)) == 0) {
1502 if (Index
== DataSize
/ sizeof (EFI_IPv6_ADDRESS
)) {
1503 Status
= EFI_ABORTED
;
1509 // Set the default gateway address back if needed.
1511 if (!NoGateway
&& !NetIp6IsUnspecifiedAddr (&GatewayAddr
)) {
1512 Status
= Ip6Cfg
->SetData (
1514 Ip6ConfigDataTypeGateway
,
1515 sizeof (EFI_IPv6_ADDRESS
),
1518 if (EFI_ERROR (Status
)) {
1524 if (MappedEvt
!= NULL
) {
1525 Ip6Cfg
->UnregisterDataNotify (
1527 Ip6ConfigDataTypeManualAddress
,
1530 gBS
->CloseEvent (MappedEvt
);
1532 if (Ip6Addr
!= NULL
) {
1539 Set the IP6 policy to Automatic.
1541 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1543 @retval EFI_SUCCESS Switch the IP policy succesfully.
1544 @retval Others Unexpect error happened.
1549 IN PXEBC_PRIVATE_DATA
*Private
1552 EFI_IP6_CONFIG_POLICY Policy
;
1554 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1557 Ip6Cfg
= Private
->Ip6Cfg
;
1558 DataSize
= sizeof (EFI_IP6_CONFIG_POLICY
);
1561 // Get and store the current policy of IP6 driver.
1563 Status
= Ip6Cfg
->GetData (
1565 Ip6ConfigDataTypePolicy
,
1569 if (EFI_ERROR (Status
)) {
1573 if (Private
->Ip6Policy
== Ip6ConfigPolicyManual
) {
1574 Policy
= Ip6ConfigPolicyAutomatic
;
1575 Status
= Ip6Cfg
->SetData (
1577 Ip6ConfigDataTypePolicy
,
1578 sizeof(EFI_IP6_CONFIG_POLICY
),
1581 if (EFI_ERROR (Status
)) {
1583 // There is no need to recover later.
1585 Private
->Ip6Policy
= PXEBC_IP6_POLICY_MAX
;
1593 This function will register the station IP address and flush IP instance to start using the new IP address.
1595 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1597 @retval EFI_SUCCESS The new IP address has been configured successfully.
1598 @retval Others Failed to configure the address.
1602 PxeBcSetIp6Address (
1603 IN PXEBC_PRIVATE_DATA
*Private
1607 EFI_DHCP6_PROTOCOL
*Dhcp6
;
1609 Dhcp6
= Private
->Dhcp6
;
1611 CopyMem (&Private
->StationIp
.v6
, &Private
->TmpStationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1612 CopyMem (&Private
->PxeBc
.Mode
->StationIp
.v6
, &Private
->StationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
1614 Status
= PxeBcRegisterIp6Address (Private
, &Private
->StationIp
.v6
);
1615 if (EFI_ERROR (Status
)) {
1616 Dhcp6
->Stop (Dhcp6
);
1620 Status
= PxeBcFlushStationIp (Private
, &Private
->StationIp
, NULL
);
1621 if (EFI_ERROR (Status
)) {
1622 PxeBcUnregisterIp6Address (Private
);
1623 Dhcp6
->Stop (Dhcp6
);
1627 AsciiPrint ("\n Station IP address is ");
1628 PxeBcShowIp6Addr (&Private
->StationIp
.v6
);
1634 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
1635 to intercept events that occurred in the configuration process.
1637 @param[in] This The pointer to the EFI DHCPv6 Protocol.
1638 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
1639 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
1640 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
1642 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
1643 @param[out] NewPacket The packet that is used to replace the Packet above.
1645 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
1646 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
1647 driver will continue to wait for more packets.
1648 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
1653 PxeBcDhcp6CallBack (
1654 IN EFI_DHCP6_PROTOCOL
*This
,
1656 IN EFI_DHCP6_STATE CurrentState
,
1657 IN EFI_DHCP6_EVENT Dhcp6Event
,
1658 IN EFI_DHCP6_PACKET
*Packet
,
1659 OUT EFI_DHCP6_PACKET
**NewPacket OPTIONAL
1662 PXEBC_PRIVATE_DATA
*Private
;
1663 EFI_PXE_BASE_CODE_MODE
*Mode
;
1664 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL
*Callback
;
1665 EFI_DHCP6_PACKET
*SelectAd
;
1669 if ((Dhcp6Event
!= Dhcp6RcvdAdvertise
) &&
1670 (Dhcp6Event
!= Dhcp6SelectAdvertise
) &&
1671 (Dhcp6Event
!= Dhcp6SendSolicit
) &&
1672 (Dhcp6Event
!= Dhcp6SendRequest
) &&
1673 (Dhcp6Event
!= Dhcp6RcvdReply
)) {
1677 ASSERT (Packet
!= NULL
);
1679 Private
= (PXEBC_PRIVATE_DATA
*) Context
;
1680 Mode
= Private
->PxeBc
.Mode
;
1681 Callback
= Private
->PxeBcCallback
;
1684 // Callback to user when any traffic ocurred if has.
1686 if (Dhcp6Event
!= Dhcp6SelectAdvertise
&& Callback
!= NULL
) {
1687 Received
= (BOOLEAN
) (Dhcp6Event
== Dhcp6RcvdAdvertise
|| Dhcp6Event
== Dhcp6RcvdReply
);
1688 Status
= Callback
->Callback (
1693 (EFI_PXE_BASE_CODE_PACKET
*) &Packet
->Dhcp6
1695 if (Status
!= EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE
) {
1700 Status
= EFI_SUCCESS
;
1702 switch (Dhcp6Event
) {
1704 case Dhcp6SendSolicit
:
1706 // Record the first Solicate msg time
1708 if (Private
->SolicitTimes
== 0) {
1709 CalcElapsedTime (Private
);
1710 Private
->SolicitTimes
++;
1713 // Cache the dhcp discover packet to mode data directly.
1715 CopyMem (&Mode
->DhcpDiscover
.Dhcpv4
, &Packet
->Dhcp6
, Packet
->Length
);
1718 case Dhcp6RcvdAdvertise
:
1719 Status
= EFI_NOT_READY
;
1720 if (Private
->OfferNum
< PXEBC_OFFER_MAX_NUM
) {
1722 // Cache the dhcp offers to OfferBuffer[] for select later, and record
1723 // the OfferIndex and OfferCount.
1725 PxeBcCacheDhcp6Offer (Private
, Packet
);
1729 case Dhcp6SendRequest
:
1731 // Store the request packet as seed packet for discover.
1733 if (Private
->Dhcp6Request
!= NULL
) {
1734 FreePool (Private
->Dhcp6Request
);
1736 Private
->Dhcp6Request
= AllocateZeroPool (Packet
->Size
);
1737 if (Private
->Dhcp6Request
!= NULL
) {
1738 CopyMem (Private
->Dhcp6Request
, Packet
, Packet
->Size
);
1742 case Dhcp6SelectAdvertise
:
1744 // Select offer by the default policy or by order, and record the SelectIndex
1745 // and SelectProxyType.
1747 PxeBcSelectDhcp6Offer (Private
);
1749 if (Private
->SelectIndex
== 0) {
1750 Status
= EFI_ABORTED
;
1752 ASSERT (NewPacket
!= NULL
);
1753 SelectAd
= &Private
->OfferBuffer
[Private
->SelectIndex
- 1].Dhcp6
.Packet
.Offer
;
1754 *NewPacket
= AllocateZeroPool (SelectAd
->Size
);
1755 ASSERT (*NewPacket
!= NULL
);
1756 CopyMem (*NewPacket
, SelectAd
, SelectAd
->Size
);
1760 case Dhcp6RcvdReply
:
1762 // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
1763 // without verification.
1765 ASSERT (Private
->SelectIndex
!= 0);
1766 PxeBcCopyDhcp6Ack (Private
, Packet
, FALSE
);
1778 Build and send out the request packet for the bootfile, and parse the reply.
1780 @param[in] Private The pointer to PxeBc private data.
1781 @param[in] Type PxeBc option boot item type.
1782 @param[in] Layer The pointer to option boot item layer.
1783 @param[in] UseBis Use BIS or not.
1784 @param[in] DestIp The pointer to the server address.
1786 @retval EFI_SUCCESS Successfully discovered the boot file.
1787 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1788 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
1789 @retval Others Failed to discover the boot file.
1793 PxeBcDhcp6Discover (
1794 IN PXEBC_PRIVATE_DATA
*Private
,
1798 IN EFI_IP_ADDRESS
*DestIp
1801 EFI_PXE_BASE_CODE_UDP_PORT SrcPort
;
1802 EFI_PXE_BASE_CODE_UDP_PORT DestPort
;
1803 EFI_PXE_BASE_CODE_MODE
*Mode
;
1804 EFI_PXE_BASE_CODE_PROTOCOL
*PxeBc
;
1805 EFI_PXE_BASE_CODE_DHCPV6_PACKET
*Discover
;
1807 EFI_DHCP6_PACKET
*Request
;
1809 EFI_DHCP6_PACKET
*Reply
;
1818 PxeBc
= &Private
->PxeBc
;
1820 Request
= Private
->Dhcp6Request
;
1821 SrcPort
= PXEBC_BS_DISCOVER_PORT
;
1822 DestPort
= PXEBC_BS_DISCOVER_PORT
;
1824 if (!UseBis
&& Layer
!= NULL
) {
1825 *Layer
&= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK
;
1828 if (Request
== NULL
) {
1829 return EFI_DEVICE_ERROR
;
1832 Discover
= AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET
));
1833 if (Discover
== NULL
) {
1834 return EFI_OUT_OF_RESOURCES
;
1838 // Build the discover packet by the cached request packet before.
1840 Xid
= NET_RANDOM (NetRandomInitSeed ());
1841 Discover
->TransactionId
= HTONL (Xid
);
1842 Discover
->MessageType
= Request
->Dhcp6
.Header
.MessageType
;
1843 RequestOpt
= Request
->Dhcp6
.Option
;
1844 DiscoverOpt
= Discover
->DhcpOptions
;
1845 DiscoverLen
= sizeof (EFI_DHCP6_HEADER
);
1846 RequestLen
= DiscoverLen
;
1848 while (RequestLen
< Request
->Length
) {
1849 OpCode
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpCode
);
1850 OpLen
= NTOHS (((EFI_DHCP6_PACKET_OPTION
*) RequestOpt
)->OpLen
);
1851 if (OpCode
!= EFI_DHCP6_IA_TYPE_NA
&&
1852 OpCode
!= EFI_DHCP6_IA_TYPE_TA
) {
1854 // Copy all the options except IA option.
1856 CopyMem (DiscoverOpt
, RequestOpt
, OpLen
+ 4);
1857 DiscoverOpt
+= (OpLen
+ 4);
1858 DiscoverLen
+= (OpLen
+ 4);
1860 RequestOpt
+= (OpLen
+ 4);
1861 RequestLen
+= (OpLen
+ 4);
1864 Status
= PxeBc
->UdpWrite (
1870 &Private
->StationIp
,
1877 if (EFI_ERROR (Status
)) {
1882 // Cache the right PXE reply packet here, set valid flag later.
1883 // Especially for PXE discover packet, store it into mode data here.
1885 if (Private
->IsDoDiscover
) {
1886 CopyMem (&Mode
->PxeDiscover
.Dhcpv6
, Discover
, DiscoverLen
);
1887 Reply
= &Private
->PxeReply
.Dhcp6
.Packet
.Ack
;
1889 Reply
= &Private
->ProxyOffer
.Dhcp6
.Packet
.Offer
;
1891 ReadSize
= (UINTN
) Reply
->Size
;
1894 // Start Udp6Read instance
1896 Status
= Private
->Udp6Read
->Configure (Private
->Udp6Read
, &Private
->Udp6CfgData
);
1897 if (EFI_ERROR (Status
)) {
1901 Status
= PxeBc
->UdpRead (
1903 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP
,
1911 (VOID
*) &Reply
->Dhcp6
1914 // Stop Udp6Read instance
1916 Private
->Udp6Read
->Configure (Private
->Udp6Read
, NULL
);
1917 if (EFI_ERROR (Status
)) {
1926 Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
1928 @param[in] Private The pointer to PxeBc private data.
1929 @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL
1931 @retval EFI_SUCCESS The S.A.R.R. process successfully finished.
1932 @retval Others Failed to finish the S.A.R.R. process.
1937 IN PXEBC_PRIVATE_DATA
*Private
,
1938 IN EFI_DHCP6_PROTOCOL
*Dhcp6
1941 EFI_PXE_BASE_CODE_MODE
*PxeMode
;
1942 EFI_DHCP6_CONFIG_DATA Config
;
1943 EFI_DHCP6_MODE_DATA Mode
;
1944 EFI_DHCP6_RETRANSMISSION
*Retransmit
;
1945 EFI_DHCP6_PACKET_OPTION
*OptList
[PXEBC_DHCP6_OPTION_MAX_NUM
];
1946 UINT8 Buffer
[PXEBC_DHCP6_OPTION_MAX_SIZE
];
1949 EFI_IP6_CONFIG_PROTOCOL
*Ip6Cfg
;
1950 EFI_STATUS TimerStatus
;
1952 UINT64 GetMappingTimeOut
;
1954 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits
;
1956 Status
= EFI_SUCCESS
;
1957 PxeMode
= Private
->PxeBc
.Mode
;
1958 Ip6Cfg
= Private
->Ip6Cfg
;
1962 // Build option list for the request packet.
1964 OptCount
= PxeBcBuildDhcp6Options (Private
, OptList
, Buffer
);
1965 ASSERT (OptCount
> 0);
1967 Retransmit
= AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION
));
1968 if (Retransmit
== NULL
) {
1969 return EFI_OUT_OF_RESOURCES
;
1972 ZeroMem (&Mode
, sizeof (EFI_DHCP6_MODE_DATA
));
1973 ZeroMem (&Config
, sizeof (EFI_DHCP6_CONFIG_DATA
));
1975 Config
.OptionCount
= OptCount
;
1976 Config
.OptionList
= OptList
;
1977 Config
.Dhcp6Callback
= PxeBcDhcp6CallBack
;
1978 Config
.CallbackContext
= Private
;
1979 Config
.IaInfoEvent
= NULL
;
1980 Config
.RapidCommit
= FALSE
;
1981 Config
.ReconfigureAccept
= FALSE
;
1982 Config
.IaDescriptor
.IaId
= Private
->IaId
;
1983 Config
.IaDescriptor
.Type
= EFI_DHCP6_IA_TYPE_NA
;
1984 Config
.SolicitRetransmission
= Retransmit
;
1985 Retransmit
->Irt
= 4;
1986 Retransmit
->Mrc
= 4;
1987 Retransmit
->Mrt
= 32;
1988 Retransmit
->Mrd
= 60;
1991 // Configure the DHCPv6 instance for PXE boot.
1993 Status
= Dhcp6
->Configure (Dhcp6
, &Config
);
1994 FreePool (Retransmit
);
1995 if (EFI_ERROR (Status
)) {
2000 // Initialize the record fields for DHCPv6 offer in private data.
2002 Private
->IsProxyRecved
= FALSE
;
2003 Private
->OfferNum
= 0;
2004 Private
->SelectIndex
= 0;
2005 ZeroMem (Private
->OfferCount
, sizeof (Private
->OfferCount
));
2006 ZeroMem (Private
->OfferIndex
, sizeof (Private
->OfferIndex
));
2010 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
2012 Status
= Dhcp6
->Start (Dhcp6
);
2013 if (Status
== EFI_NO_MAPPING
) {
2015 // IP6 Linklocal address is not available for use, so stop current Dhcp process
2016 // and wait for duplicate address detection to finish.
2018 Dhcp6
->Stop (Dhcp6
);
2021 // Get Duplicate Address Detection Transmits count.
2023 DataSize
= sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS
);
2024 Status
= Ip6Cfg
->GetData (
2026 Ip6ConfigDataTypeDupAddrDetectTransmits
,
2030 if (EFI_ERROR (Status
)) {
2031 Dhcp6
->Configure (Dhcp6
, NULL
);
2035 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
2036 if (EFI_ERROR (Status
)) {
2037 Dhcp6
->Configure (Dhcp6
, NULL
);
2041 GetMappingTimeOut
= TICKS_PER_SECOND
* DadXmits
.DupAddrDetectTransmits
+ PXEBC_DAD_ADDITIONAL_DELAY
;
2042 Status
= gBS
->SetTimer (Timer
, TimerRelative
, GetMappingTimeOut
);
2043 if (EFI_ERROR (Status
)) {
2044 gBS
->CloseEvent (Timer
);
2045 Dhcp6
->Configure (Dhcp6
, NULL
);
2051 TimerStatus
= gBS
->CheckEvent (Timer
);
2052 if (!EFI_ERROR (TimerStatus
)) {
2053 Status
= Dhcp6
->Start (Dhcp6
);
2055 } while (TimerStatus
== EFI_NOT_READY
);
2057 gBS
->CloseEvent (Timer
);
2059 if (EFI_ERROR (Status
)) {
2060 if (Status
== EFI_ICMP_ERROR
) {
2061 PxeMode
->IcmpErrorReceived
= TRUE
;
2063 Dhcp6
->Configure (Dhcp6
, NULL
);
2068 // Get the acquired IPv6 address and store them.
2070 Status
= Dhcp6
->GetModeData (Dhcp6
, &Mode
, NULL
);
2071 if (EFI_ERROR (Status
)) {
2072 Dhcp6
->Stop (Dhcp6
);
2076 ASSERT ((Mode
.Ia
!= NULL
) && (Mode
.Ia
->State
== Dhcp6Bound
));
2078 // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the
2079 // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when
2080 // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as
2081 // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery
2082 // to find a valid router address.
2084 CopyMem (&Private
->TmpStationIp
.v6
, &Mode
.Ia
->IaAddress
[0].IpAddress
, sizeof (EFI_IPv6_ADDRESS
));
2085 if (Mode
.ClientId
!= NULL
) {
2086 FreePool (Mode
.ClientId
);
2088 if (Mode
.Ia
!= NULL
) {
2092 // Check the selected offer whether BINL retry is needed.
2094 Status
= PxeBcHandleDhcp6Offer (Private
);
2095 if (EFI_ERROR (Status
)) {
2096 Dhcp6
->Stop (Dhcp6
);