VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c@ 105670

Last change on this file since 105670 was 105670, checked in by vboxsync, 4 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 71.6 KB
Line 
1/** @file
2 Functions implementation related with DHCPv6 for UefiPxeBc Driver.
3
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10**/
11
12#include "PxeBcImpl.h"
13
14//
15// Well-known multi-cast address defined in section-24.1 of rfc-3315
16//
17// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
18//
19EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {
20 { 0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2 }
21};
22
23/**
24 Parse out a DHCPv6 option by OptTag, and find the position in buffer.
25
26 @param[in] Buffer The pointer to the option buffer.
27 @param[in] Length Length of the option buffer.
28 @param[in] OptTag The required option tag.
29
30 @retval NULL Failed to parse the required option.
31 @retval Others The position of the required option in buffer.
32
33**/
34EFI_DHCP6_PACKET_OPTION *
35PxeBcParseDhcp6Options (
36 IN UINT8 *Buffer,
37 IN UINT32 Length,
38 IN UINT16 OptTag
39 )
40{
41 EFI_DHCP6_PACKET_OPTION *Option;
42 UINT32 Offset;
43
44 Option = (EFI_DHCP6_PACKET_OPTION *)Buffer;
45 Offset = 0;
46
47 //
48 // OpLen and OpCode here are both stored in network order.
49 //
50 while (Offset < Length) {
51 if (NTOHS (Option->OpCode) == OptTag) {
52 return Option;
53 }
54
55 Offset += (NTOHS (Option->OpLen) + 4);
56 Option = (EFI_DHCP6_PACKET_OPTION *)(Buffer + Offset);
57 }
58
59 return NULL;
60}
61
62/**
63 Build the options buffer for the DHCPv6 request packet.
64
65 @param[in] Private The pointer to PxeBc private data.
66 @param[out] OptList The pointer to the option pointer array.
67 @param[in] Buffer The pointer to the buffer to contain the option list.
68
69 @return Index The count of the built-in options.
70
71**/
72UINT32
73PxeBcBuildDhcp6Options (
74 IN PXEBC_PRIVATE_DATA *Private,
75 OUT EFI_DHCP6_PACKET_OPTION **OptList,
76 IN UINT8 *Buffer
77 )
78{
79 PXEBC_DHCP6_OPTION_ENTRY OptEnt;
80 UINT32 Index;
81 UINT16 Value;
82
83 Index = 0;
84 OptList[0] = (EFI_DHCP6_PACKET_OPTION *)Buffer;
85
86 //
87 // Append client option request option
88 //
89 OptList[Index]->OpCode = HTONS (DHCP6_OPT_ORO);
90 OptList[Index]->OpLen = HTONS (8);
91 OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *)OptList[Index]->Data;
92 OptEnt.Oro->OpCode[0] = HTONS (DHCP6_OPT_BOOT_FILE_URL);
93 OptEnt.Oro->OpCode[1] = HTONS (DHCP6_OPT_BOOT_FILE_PARAM);
94 OptEnt.Oro->OpCode[2] = HTONS (DHCP6_OPT_DNS_SERVERS);
95 OptEnt.Oro->OpCode[3] = HTONS (DHCP6_OPT_VENDOR_CLASS);
96 Index++;
97 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
98
99 //
100 // Append client network device interface option
101 //
102 OptList[Index]->OpCode = HTONS (DHCP6_OPT_UNDI);
103 OptList[Index]->OpLen = HTONS ((UINT16)3);
104 OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *)OptList[Index]->Data;
105
106 if (Private->Nii != NULL) {
107 OptEnt.Undi->Type = Private->Nii->Type;
108 OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
109 OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
110 } else {
111 OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
112 OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
113 OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
114 }
115
116 Index++;
117 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
118
119 //
120 // Append client system architecture option
121 //
122 OptList[Index]->OpCode = HTONS (DHCP6_OPT_ARCH);
123 OptList[Index]->OpLen = HTONS ((UINT16)sizeof (PXEBC_DHCP6_OPTION_ARCH));
124 OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *)OptList[Index]->Data;
125 Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
126 CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
127 Index++;
128 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
129
130 //
131 // Append vendor class option to store the PXE class identifier.
132 //
133 OptList[Index]->OpCode = HTONS (DHCP6_OPT_VENDOR_CLASS);
134 OptList[Index]->OpLen = HTONS ((UINT16)sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));
135 OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *)OptList[Index]->Data;
136 OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);
137 OptEnt.VendorClass->ClassLen = HTONS ((UINT16)sizeof (PXEBC_CLASS_ID));
138 CopyMem (
139 &OptEnt.VendorClass->ClassId,
140 DEFAULT_CLASS_ID_DATA,
141 sizeof (PXEBC_CLASS_ID)
142 );
143 PxeBcUintnToAscDecWithFormat (
144 EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,
145 OptEnt.VendorClass->ClassId.ArchitectureType,
146 sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)
147 );
148
149 if (Private->Nii != NULL) {
150 CopyMem (
151 OptEnt.VendorClass->ClassId.InterfaceName,
152 Private->Nii->StringId,
153 sizeof (OptEnt.VendorClass->ClassId.InterfaceName)
154 );
155 PxeBcUintnToAscDecWithFormat (
156 Private->Nii->MajorVer,
157 OptEnt.VendorClass->ClassId.UndiMajor,
158 sizeof (OptEnt.VendorClass->ClassId.UndiMajor)
159 );
160 PxeBcUintnToAscDecWithFormat (
161 Private->Nii->MinorVer,
162 OptEnt.VendorClass->ClassId.UndiMinor,
163 sizeof (OptEnt.VendorClass->ClassId.UndiMinor)
164 );
165 }
166
167 Index++;
168
169 return Index;
170}
171
172/**
173 Cache the DHCPv6 packet.
174
175 @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.
176 @param[in] Src The pointer to the DHCPv6 packet to be cached.
177
178 @retval EFI_SUCCESS Packet is copied.
179 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
180
181**/
182EFI_STATUS
183PxeBcCacheDhcp6Packet (
184 IN EFI_DHCP6_PACKET *Dst,
185 IN EFI_DHCP6_PACKET *Src
186 )
187{
188 if (Dst->Size < Src->Length) {
189 return EFI_BUFFER_TOO_SMALL;
190 }
191
192 CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
193 Dst->Length = Src->Length;
194
195 return EFI_SUCCESS;
196}
197
198/**
199 Retrieve the boot server address using the EFI_DNS6_PROTOCOL.
200
201 @param[in] Private Pointer to PxeBc private data.
202 @param[in] HostName Pointer to buffer containing hostname.
203 @param[out] IpAddress On output, pointer to buffer containing IPv6 address.
204
205 @retval EFI_SUCCESS Operation succeeded.
206 @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
207 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
208 @retval Others Other errors as indicated.
209
210**/
211EFI_STATUS
212PxeBcDns6 (
213 IN PXEBC_PRIVATE_DATA *Private,
214 IN CHAR16 *HostName,
215 OUT EFI_IPv6_ADDRESS *IpAddress
216 )
217{
218 EFI_STATUS Status;
219 EFI_DNS6_PROTOCOL *Dns6;
220 EFI_DNS6_CONFIG_DATA Dns6ConfigData;
221 EFI_DNS6_COMPLETION_TOKEN Token;
222 EFI_HANDLE Dns6Handle;
223 EFI_IPv6_ADDRESS *DnsServerList;
224 BOOLEAN IsDone;
225
226 Dns6 = NULL;
227 Dns6Handle = NULL;
228 DnsServerList = Private->DnsServer;
229 ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));
230
231 //
232 // Create a DNSv6 child instance and get the protocol.
233 //
234 Status = NetLibCreateServiceChild (
235 Private->Controller,
236 Private->Image,
237 &gEfiDns6ServiceBindingProtocolGuid,
238 &Dns6Handle
239 );
240 if (EFI_ERROR (Status)) {
241 goto Exit;
242 }
243
244 Status = gBS->OpenProtocol (
245 Dns6Handle,
246 &gEfiDns6ProtocolGuid,
247 (VOID **)&Dns6,
248 Private->Image,
249 Private->Controller,
250 EFI_OPEN_PROTOCOL_BY_DRIVER
251 );
252 if (EFI_ERROR (Status)) {
253 goto Exit;
254 }
255
256 //
257 // Configure DNS6 instance for the DNS server address and protocol.
258 //
259 ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));
260 Dns6ConfigData.DnsServerCount = 1;
261 Dns6ConfigData.DnsServerList = DnsServerList;
262 Dns6ConfigData.EnableDnsCache = TRUE;
263 Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP;
264 IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &Private->TmpStationIp.v6);
265 Status = Dns6->Configure (
266 Dns6,
267 &Dns6ConfigData
268 );
269 if (EFI_ERROR (Status)) {
270 goto Exit;
271 }
272
273 Token.Status = EFI_NOT_READY;
274 IsDone = FALSE;
275 //
276 // Create event to set the IsDone flag when name resolution is finished.
277 //
278 Status = gBS->CreateEvent (
279 EVT_NOTIFY_SIGNAL,
280 TPL_NOTIFY,
281 PxeBcCommonNotify,
282 &IsDone,
283 &Token.Event
284 );
285 if (EFI_ERROR (Status)) {
286 goto Exit;
287 }
288
289 //
290 // Start asynchronous name resolution.
291 //
292 Status = Dns6->HostNameToIp (Dns6, HostName, &Token);
293 if (EFI_ERROR (Status)) {
294 goto Exit;
295 }
296
297 while (!IsDone) {
298 Dns6->Poll (Dns6);
299 }
300
301 //
302 // Name resolution is done, check result.
303 //
304 Status = Token.Status;
305 if (!EFI_ERROR (Status)) {
306 if (Token.RspData.H2AData == NULL) {
307 Status = EFI_DEVICE_ERROR;
308 goto Exit;
309 }
310
311 if ((Token.RspData.H2AData->IpCount == 0) || (Token.RspData.H2AData->IpList == NULL)) {
312 Status = EFI_DEVICE_ERROR;
313 goto Exit;
314 }
315
316 //
317 // We just return the first IPv6 address from DNS protocol.
318 //
319 IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);
320 Status = EFI_SUCCESS;
321 }
322
323Exit:
324 FreePool (HostName);
325
326 if (Token.Event != NULL) {
327 gBS->CloseEvent (Token.Event);
328 }
329
330 if (Token.RspData.H2AData != NULL) {
331 if (Token.RspData.H2AData->IpList != NULL) {
332 FreePool (Token.RspData.H2AData->IpList);
333 }
334
335 FreePool (Token.RspData.H2AData);
336 }
337
338 if (Dns6 != NULL) {
339 Dns6->Configure (Dns6, NULL);
340
341 gBS->CloseProtocol (
342 Dns6Handle,
343 &gEfiDns6ProtocolGuid,
344 Private->Image,
345 Private->Controller
346 );
347 }
348
349 if (Dns6Handle != NULL) {
350 NetLibDestroyServiceChild (
351 Private->Controller,
352 Private->Image,
353 &gEfiDns6ServiceBindingProtocolGuid,
354 Dns6Handle
355 );
356 }
357
358 if (DnsServerList != NULL) {
359 FreePool (DnsServerList);
360 }
361
362 return Status;
363}
364
365/**
366 Parse the Boot File URL option.
367
368 @param[in] Private Pointer to PxeBc private data.
369 @param[out] FileName The pointer to the boot file name.
370 @param[in, out] SrvAddr The pointer to the boot server address.
371 @param[in] BootFile The pointer to the boot file URL option data.
372 @param[in] Length The length of the boot file URL option data.
373
374 @retval EFI_ABORTED User cancel operation.
375 @retval EFI_SUCCESS Selected the boot menu successfully.
376 @retval EFI_NOT_READY Read the input key from the keyboard has not finish.
377
378**/
379EFI_STATUS
380PxeBcExtractBootFileUrl (
381 IN PXEBC_PRIVATE_DATA *Private,
382 OUT UINT8 **FileName,
383 IN OUT EFI_IPv6_ADDRESS *SrvAddr,
384 IN CHAR8 *BootFile,
385 IN UINT16 Length
386 )
387{
388 UINT16 PrefixLen;
389 CHAR8 *BootFileNamePtr;
390 CHAR8 *BootFileName;
391 UINT16 BootFileNameLen;
392 CHAR8 *TmpStr;
393 CHAR8 TmpChar;
394 CHAR8 *ServerAddressOption;
395 CHAR8 *ServerAddress;
396 CHAR8 *ModeStr;
397 CHAR16 *HostName;
398 BOOLEAN IpExpressedUrl;
399 UINTN Len;
400 EFI_STATUS Status;
401
402 IpExpressedUrl = TRUE;
403 //
404 // The format of the Boot File URL option is:
405 //
406 // 0 1 2 3
407 // 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
408 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409 // | OPT_BOOTFILE_URL | option-len |
410 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411 // | |
412 // . bootfile-url (variable length) .
413 // | |
414 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
415 //
416
417 //
418 // Based upon RFC 5970 and UEFI 2.6, bootfile-url format can be
419 // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME or tftp://domain_name/BOOTFILE_NAME
420 // As an example where the BOOTFILE_NAME is the EFI loader and
421 // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.
422 //
423 PrefixLen = (UINT16)AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);
424
425 if ((Length <= PrefixLen) ||
426 (CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0))
427 {
428 return EFI_NOT_FOUND;
429 }
430
431 BootFile = BootFile + PrefixLen;
432 Length = (UINT16)(Length - PrefixLen);
433
434 TmpStr = (CHAR8 *)AllocateZeroPool (Length + 1);
435 if (TmpStr == NULL) {
436 return EFI_OUT_OF_RESOURCES;
437 }
438
439 CopyMem (TmpStr, BootFile, Length);
440 TmpStr[Length] = '\0';
441
442 //
443 // Get the part of SERVER_ADDRESS string.
444 //
445 ServerAddressOption = TmpStr;
446 if (*ServerAddressOption == PXEBC_ADDR_START_DELIMITER) {
447 ServerAddressOption++;
448 ServerAddress = ServerAddressOption;
449 while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {
450 ServerAddress++;
451 }
452
453 if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {
454 FreePool (TmpStr);
455 return EFI_INVALID_PARAMETER;
456 }
457
458 *ServerAddress = '\0';
459
460 //
461 // Convert the string of server address to Ipv6 address format and store it.
462 //
463 Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);
464 if (EFI_ERROR (Status)) {
465 FreePool (TmpStr);
466 return Status;
467 }
468 } else {
469 IpExpressedUrl = FALSE;
470 ServerAddress = ServerAddressOption;
471 while (*ServerAddress != '\0' && *ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {
472 ServerAddress++;
473 }
474
475 if (*ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {
476 FreePool (TmpStr);
477 return EFI_INVALID_PARAMETER;
478 }
479
480 *ServerAddress = '\0';
481
482 Len = AsciiStrSize (ServerAddressOption);
483 HostName = AllocateZeroPool (Len * sizeof (CHAR16));
484 if (HostName == NULL) {
485 FreePool (TmpStr);
486 return EFI_OUT_OF_RESOURCES;
487 }
488
489 AsciiStrToUnicodeStrS (
490 ServerAddressOption,
491 HostName,
492 Len
493 );
494
495 //
496 // Perform DNS resolution.
497 //
498 Status = PxeBcDns6 (Private, HostName, SrvAddr);
499 if (EFI_ERROR (Status)) {
500 FreePool (TmpStr);
501 return Status;
502 }
503 }
504
505 //
506 // Get the part of BOOTFILE_NAME string.
507 //
508 BootFileNamePtr = (CHAR8 *)((UINTN)ServerAddress + 1);
509 if (IpExpressedUrl) {
510 if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {
511 FreePool (TmpStr);
512 return EFI_INVALID_PARAMETER;
513 }
514
515 ++BootFileNamePtr;
516 }
517
518 BootFileNameLen = (UINT16)(Length - (UINT16)((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);
519 if ((BootFileNameLen != 0) || (FileName != NULL)) {
520 //
521 // Remove trailing mode=octet if present and ignore. All other modes are
522 // invalid for netboot6, so reject them.
523 //
524 ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet");
525 if ((ModeStr != NULL) && (*(ModeStr + AsciiStrLen (";mode=octet")) == '\0')) {
526 *ModeStr = '\0';
527 } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) {
528 FreePool (TmpStr);
529 return EFI_INVALID_PARAMETER;
530 }
531
532 //
533 // Extract boot file name from URL.
534 //
535 BootFileName = (CHAR8 *)AllocateZeroPool (BootFileNameLen);
536 if (BootFileName == NULL) {
537 FreePool (TmpStr);
538 return EFI_OUT_OF_RESOURCES;
539 }
540
541 *FileName = (UINT8 *)BootFileName;
542
543 //
544 // Decode percent-encoding in boot file name.
545 //
546 while (*BootFileNamePtr != '\0') {
547 if (*BootFileNamePtr == '%') {
548 TmpChar = *(BootFileNamePtr+ 3);
549 *(BootFileNamePtr+ 3) = '\0';
550 *BootFileName = (UINT8)AsciiStrHexToUintn ((CHAR8 *)(BootFileNamePtr + 1));
551 BootFileName++;
552 *(BootFileNamePtr+ 3) = TmpChar;
553 BootFileNamePtr += 3;
554 } else {
555 *BootFileName = *BootFileNamePtr;
556 BootFileName++;
557 BootFileNamePtr++;
558 }
559 }
560
561 *BootFileName = '\0';
562 }
563
564 FreePool (TmpStr);
565
566 return EFI_SUCCESS;
567}
568
569/**
570 Parse the Boot File Parameter option.
571
572 @param[in] BootFilePara The pointer to boot file parameter option data.
573 @param[out] BootFileSize The pointer to the parsed boot file size.
574
575 @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option.
576 @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option.
577
578**/
579EFI_STATUS
580PxeBcExtractBootFileParam (
581 IN CHAR8 *BootFilePara,
582 OUT UINT16 *BootFileSize
583 )
584{
585 UINT16 Length;
586 UINT8 Index;
587 UINT8 Digit;
588 UINT32 Size;
589
590 CopyMem (&Length, BootFilePara, sizeof (UINT16));
591 Length = NTOHS (Length);
592
593 //
594 // The BootFile Size should be 1~5 byte ASCII strings
595 //
596 if ((Length < 1) || (Length > 5)) {
597 return EFI_NOT_FOUND;
598 }
599
600 //
601 // Extract the value of BootFile Size.
602 //
603 BootFilePara = BootFilePara + sizeof (UINT16);
604 Size = 0;
605 for (Index = 0; Index < Length; Index++) {
606 if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {
607 return EFI_NOT_FOUND;
608 }
609
610 Size = (Size + Digit) * 10;
611 }
612
613 Size = Size / 10;
614 if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {
615 return EFI_NOT_FOUND;
616 }
617
618 *BootFileSize = (UINT16)Size;
619 return EFI_SUCCESS;
620}
621
622/**
623 Parse the cached DHCPv6 packet, including all the options.
624
625 @param[in] Cache6 The pointer to a cached DHCPv6 packet.
626
627 @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
628 @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.
629
630**/
631EFI_STATUS
632PxeBcParseDhcp6Packet (
633 IN PXEBC_DHCP6_PACKET_CACHE *Cache6
634 )
635{
636 EFI_DHCP6_PACKET *Offer;
637 EFI_DHCP6_PACKET_OPTION **Options;
638 EFI_DHCP6_PACKET_OPTION *Option;
639 PXEBC_OFFER_TYPE OfferType;
640 BOOLEAN IsProxyOffer;
641 BOOLEAN IsPxeOffer;
642 UINT32 Offset;
643 UINT32 Length;
644 UINT32 EnterpriseNum;
645
646 IsProxyOffer = TRUE;
647 IsPxeOffer = FALSE;
648 Offer = &Cache6->Packet.Offer;
649 Options = Cache6->OptList;
650
651 ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));
652
653 Option = (EFI_DHCP6_PACKET_OPTION *)(Offer->Dhcp6.Option);
654 Offset = 0;
655 Length = GET_DHCP6_OPTION_SIZE (Offer);
656
657 //
658 // OpLen and OpCode here are both stored in network order, since they are from original packet.
659 //
660 while (Offset < Length) {
661 if (NTOHS (Option->OpCode) == DHCP6_OPT_IA_NA) {
662 Options[PXEBC_DHCP6_IDX_IA_NA] = Option;
663 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_URL) {
664 //
665 // The server sends this option to inform the client about an URL to a boot file.
666 //
667 Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;
668 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_PARAM) {
669 Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
670 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {
671 Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;
672 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) {
673 Options[PXEBC_DHCP6_IDX_DNS_SERVER] = Option;
674 }
675
676 Offset += (NTOHS (Option->OpLen) + 4);
677 Option = (EFI_DHCP6_PACKET_OPTION *)(Offer->Dhcp6.Option + Offset);
678 }
679
680 //
681 // The offer with assigned client address is NOT a proxy offer.
682 // An ia_na option, embedded with valid ia_addr option and a status_code of success.
683 //
684 Option = Options[PXEBC_DHCP6_IDX_IA_NA];
685 if (Option != NULL) {
686 Option = PxeBcParseDhcp6Options (
687 Option->Data + 12,
688 NTOHS (Option->OpLen),
689 DHCP6_OPT_STATUS_CODE
690 );
691 if (((Option != NULL) && (Option->Data[0] == 0)) || (Option == NULL)) {
692 IsProxyOffer = FALSE;
693 }
694 }
695
696 //
697 // The offer with "PXEClient" is a pxe offer.
698 //
699 Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];
700 EnterpriseNum = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);
701
702 if ((Option != NULL) &&
703 (NTOHS (Option->OpLen) >= 13) &&
704 (CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0) &&
705 (CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0))
706 {
707 IsPxeOffer = TRUE;
708 }
709
710 //
711 // Determine offer type of the dhcp6 packet.
712 //
713 if (IsPxeOffer) {
714 //
715 // It's a binl offer only with PXEClient.
716 //
717 OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
718 } else {
719 //
720 // It's a dhcp only offer, which is a pure dhcp6 offer packet.
721 //
722 OfferType = PxeOfferTypeDhcpOnly;
723 }
724
725 Cache6->OfferType = OfferType;
726
727 return EFI_SUCCESS;
728}
729
730/**
731 Cache the DHCPv6 ack packet, and parse it on demand.
732
733 @param[in] Private The pointer to PxeBc private data.
734 @param[in] Ack The pointer to the DHCPv6 ack packet.
735 @param[in] Verified If TRUE, parse the ACK packet and store info into mode data.
736
737 @retval EFI_SUCCESS Cache and parse the packet successfully.
738 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
739
740**/
741EFI_STATUS
742PxeBcCopyDhcp6Ack (
743 IN PXEBC_PRIVATE_DATA *Private,
744 IN EFI_DHCP6_PACKET *Ack,
745 IN BOOLEAN Verified
746 )
747{
748 EFI_PXE_BASE_CODE_MODE *Mode;
749 EFI_STATUS Status;
750
751 Mode = Private->PxeBc.Mode;
752
753 Status = PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);
754 if (EFI_ERROR (Status)) {
755 return Status;
756 }
757
758 if (Verified) {
759 //
760 // Parse the ack packet and store it into mode data if needed.
761 //
762 PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);
763 CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);
764 Mode->DhcpAckReceived = TRUE;
765 }
766
767 return EFI_SUCCESS;
768}
769
770/**
771 Cache the DHCPv6 proxy offer packet according to the received order.
772
773 @param[in] Private The pointer to PxeBc private data.
774 @param[in] OfferIndex The received order of offer packets.
775
776 @retval EFI_SUCCESS Cache and parse the packet successfully.
777 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
778
779**/
780EFI_STATUS
781PxeBcCopyDhcp6Proxy (
782 IN PXEBC_PRIVATE_DATA *Private,
783 IN UINT32 OfferIndex
784 )
785{
786 EFI_PXE_BASE_CODE_MODE *Mode;
787 EFI_DHCP6_PACKET *Offer;
788 EFI_STATUS Status;
789
790 ASSERT (OfferIndex < Private->OfferNum);
791 ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
792
793 Mode = Private->PxeBc.Mode;
794 Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;
795
796 //
797 // Cache the proxy offer packet and parse it.
798 //
799 Status = PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);
800 if (EFI_ERROR (Status)) {
801 return Status;
802 }
803
804 PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);
805
806 //
807 // Store this packet into mode data.
808 //
809 CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);
810 Mode->ProxyOfferReceived = TRUE;
811
812 return EFI_SUCCESS;
813}
814
815/**
816 Seek the address of the first byte of the option header.
817
818 @param[in] Buf The pointer to the buffer.
819 @param[in] SeekLen The length to seek.
820 @param[in] OptType The option type.
821
822 @retval NULL If it failed to seek the option.
823 @retval others The position to the option.
824
825**/
826UINT8 *
827PxeBcDhcp6SeekOption (
828 IN UINT8 *Buf,
829 IN UINT32 SeekLen,
830 IN UINT16 OptType
831 )
832{
833 UINT8 *Cursor;
834 UINT8 *Option;
835 UINT16 DataLen;
836 UINT16 OpCode;
837
838 Option = NULL;
839 Cursor = Buf;
840
841 while (Cursor < Buf + SeekLen) {
842 OpCode = ReadUnaligned16 ((UINT16 *)Cursor);
843 if (OpCode == HTONS (OptType)) {
844 Option = Cursor;
845 break;
846 }
847
848 DataLen = NTOHS (ReadUnaligned16 ((UINT16 *)(Cursor + 2)));
849 Cursor += (DataLen + 4);
850 }
851
852 return Option;
853}
854
855/**
856 Build and send out the request packet for the bootfile, and parse the reply.
857
858 @param[in] Private The pointer to PxeBc private data.
859 @param[in] Index PxeBc option boot item type.
860
861 @retval EFI_SUCCESS Successfully discovered the boot file.
862 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
863 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
864 @retval Others Failed to discover the boot file.
865
866**/
867EFI_STATUS
868PxeBcRequestBootService (
869 IN PXEBC_PRIVATE_DATA *Private,
870 IN UINT32 Index
871 )
872{
873 EFI_PXE_BASE_CODE_UDP_PORT SrcPort;
874 EFI_PXE_BASE_CODE_UDP_PORT DestPort;
875 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
876 EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover;
877 UINTN DiscoverLen;
878 EFI_DHCP6_PACKET *Request;
879 UINTN RequestLen;
880 EFI_DHCP6_PACKET *Reply;
881 UINT8 *RequestOpt;
882 UINT8 *DiscoverOpt;
883 UINTN ReadSize;
884 UINT16 OpFlags;
885 UINT16 OpCode;
886 UINT16 OpLen;
887 EFI_STATUS Status;
888 EFI_DHCP6_PACKET *IndexOffer;
889 UINT8 *Option;
890 UINTN DiscoverLenNeeded;
891
892 PxeBc = &Private->PxeBc;
893 Request = Private->Dhcp6Request;
894 IndexOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer;
895 SrcPort = PXEBC_BS_DISCOVER_PORT;
896 DestPort = PXEBC_BS_DISCOVER_PORT;
897 OpFlags = 0;
898
899 if (Request == NULL) {
900 return EFI_DEVICE_ERROR;
901 }
902
903 DiscoverLenNeeded = sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET);
904 Discover = AllocateZeroPool (DiscoverLenNeeded);
905 if (Discover == NULL) {
906 return EFI_OUT_OF_RESOURCES;
907 }
908
909 //
910 // Build the request packet by the cached request packet before.
911 //
912 Discover->TransactionId = IndexOffer->Dhcp6.Header.TransactionId;
913 Discover->MessageType = Request->Dhcp6.Header.MessageType;
914 RequestOpt = Request->Dhcp6.Option;
915 DiscoverOpt = Discover->DhcpOptions;
916 DiscoverLen = sizeof (EFI_DHCP6_HEADER);
917 RequestLen = DiscoverLen;
918
919 //
920 // Find Server ID Option from ProxyOffer.
921 //
922 if (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl) {
923 Option = PxeBcDhcp6SeekOption (
924 IndexOffer->Dhcp6.Option,
925 IndexOffer->Length - 4,
926 DHCP6_OPT_SERVER_ID
927 );
928 if (Option == NULL) {
929 Status = EFI_NOT_FOUND;
930 goto ON_ERROR;
931 }
932
933 //
934 // Add Server ID Option.
935 //
936 OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *)Option)->OpLen);
937
938 //
939 // Check that the minimum and maximum requirements are met
940 //
941 if ((OpLen < PXEBC_MIN_SIZE_OF_DUID) || (OpLen > PXEBC_MAX_SIZE_OF_DUID)) {
942 Status = EFI_INVALID_PARAMETER;
943 goto ON_ERROR;
944 }
945
946 //
947 // Check that the option length is valid.
948 //
949 if ((DiscoverLen + OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN) > DiscoverLenNeeded) {
950 Status = EFI_OUT_OF_RESOURCES;
951 goto ON_ERROR;
952 }
953
954 CopyMem (DiscoverOpt, Option, OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
955 DiscoverOpt += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
956 DiscoverLen += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
957 }
958
959 while (RequestLen < Request->Length) {
960 OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *)RequestOpt)->OpCode);
961 OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *)RequestOpt)->OpLen);
962 if ((OpCode != EFI_DHCP6_IA_TYPE_NA) &&
963 (OpCode != EFI_DHCP6_IA_TYPE_TA) &&
964 (OpCode != DHCP6_OPT_SERVER_ID)
965 )
966 {
967 //
968 // Check that the option length is valid.
969 //
970 if (DiscoverLen + OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN > DiscoverLenNeeded) {
971 Status = EFI_OUT_OF_RESOURCES;
972 goto ON_ERROR;
973 }
974
975 //
976 // Copy all the options except IA option and Server ID
977 //
978 CopyMem (DiscoverOpt, RequestOpt, OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
979 DiscoverOpt += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
980 DiscoverLen += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
981 }
982
983 RequestOpt += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
984 RequestLen += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
985 }
986
987 //
988 // Update Elapsed option in the package
989 //
990 Option = PxeBcDhcp6SeekOption (
991 Discover->DhcpOptions,
992 (UINT32)(RequestLen - 4),
993 DHCP6_OPT_ELAPSED_TIME
994 );
995 if (Option != NULL) {
996 CalcElapsedTime (Private);
997 WriteUnaligned16 ((UINT16 *)(Option + 4), HTONS ((UINT16)Private->ElapsedTime));
998 }
999
1000 Status = PxeBc->UdpWrite (
1001 PxeBc,
1002 OpFlags,
1003 &Private->ServerIp,
1004 &DestPort,
1005 NULL,
1006 &Private->StationIp,
1007 &SrcPort,
1008 NULL,
1009 NULL,
1010 &DiscoverLen,
1011 (VOID *)Discover
1012 );
1013
1014 if (EFI_ERROR (Status)) {
1015 goto ON_ERROR;
1016 }
1017
1018 //
1019 // Cache the right PXE reply packet here, set valid flag later.
1020 // Especially for PXE discover packet, store it into mode data here.
1021 //
1022 Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
1023 ReadSize = (UINTN)Reply->Size;
1024
1025 //
1026 // Start Udp6Read instance
1027 //
1028 Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
1029 if (EFI_ERROR (Status)) {
1030 goto ON_ERROR;
1031 }
1032
1033 Status = PxeBc->UdpRead (
1034 PxeBc,
1035 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
1036 NULL,
1037 &SrcPort,
1038 &Private->ServerIp,
1039 &DestPort,
1040 NULL,
1041 NULL,
1042 &ReadSize,
1043 (VOID *)&Reply->Dhcp6
1044 );
1045 //
1046 // Stop Udp6Read instance
1047 //
1048 Private->Udp6Read->Configure (Private->Udp6Read, NULL);
1049
1050 if (EFI_ERROR (Status)) {
1051 goto ON_ERROR;
1052 }
1053
1054 //
1055 // Update length
1056 //
1057 Reply->Length = (UINT32)ReadSize;
1058
1059 return EFI_SUCCESS;
1060
1061ON_ERROR:
1062 if (Discover != NULL) {
1063 FreePool (Discover);
1064 }
1065
1066 return Status;
1067}
1068
1069/**
1070 Retry to request bootfile name by the BINL offer.
1071
1072 @param[in] Private The pointer to PxeBc private data.
1073 @param[in] Index The received order of offer packets.
1074
1075 @retval EFI_SUCCESS Successfully retried a request for the bootfile name.
1076 @retval EFI_DEVICE_ERROR Failed to retry the bootfile name.
1077
1078**/
1079EFI_STATUS
1080PxeBcRetryDhcp6Binl (
1081 IN PXEBC_PRIVATE_DATA *Private,
1082 IN UINT32 Index
1083 )
1084{
1085 EFI_PXE_BASE_CODE_MODE *Mode;
1086 PXEBC_DHCP6_PACKET_CACHE *Offer;
1087 PXEBC_DHCP6_PACKET_CACHE *Cache6;
1088 EFI_STATUS Status;
1089
1090 ASSERT (Index < PXEBC_OFFER_MAX_NUM);
1091 ASSERT (
1092 Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||
1093 Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl
1094 );
1095
1096 Mode = Private->PxeBc.Mode;
1097 Private->IsDoDiscover = FALSE;
1098 Offer = &Private->OfferBuffer[Index].Dhcp6;
1099 if (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
1100 //
1101 // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.
1102 //
1103 CopyMem (
1104 &Private->ServerIp.v6,
1105 &mAllDhcpRelayAndServersAddress,
1106 sizeof (EFI_IPv6_ADDRESS)
1107 );
1108 } else {
1109 ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
1110 //
1111 // Parse out the next server address from the last offer, and store it
1112 //
1113 Status = PxeBcExtractBootFileUrl (
1114 Private,
1115 &Private->BootFileName,
1116 &Private->ServerIp.v6,
1117 (CHAR8 *)(Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
1118 NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
1119 );
1120 if (EFI_ERROR (Status)) {
1121 return Status;
1122 }
1123 }
1124
1125 //
1126 // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
1127 //
1128 Status = PxeBcRequestBootService (Private, Index);
1129
1130 if (EFI_ERROR (Status)) {
1131 return Status;
1132 }
1133
1134 Cache6 = &Private->ProxyOffer.Dhcp6;
1135 Status = PxeBcParseDhcp6Packet (Cache6);
1136 if (EFI_ERROR (Status)) {
1137 return Status;
1138 }
1139
1140 if ((Cache6->OfferType != PxeOfferTypeProxyPxe10) &&
1141 (Cache6->OfferType != PxeOfferTypeProxyWfm11a) &&
1142 (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL))
1143 {
1144 //
1145 // This BINL ack doesn't have discovery option set or multicast option set
1146 // or bootfile name specified.
1147 //
1148 return EFI_DEVICE_ERROR;
1149 }
1150
1151 Mode->ProxyOfferReceived = TRUE;
1152 CopyMem (
1153 &Mode->ProxyOffer.Dhcpv6,
1154 &Cache6->Packet.Offer.Dhcp6,
1155 Cache6->Packet.Offer.Length
1156 );
1157
1158 return EFI_SUCCESS;
1159}
1160
1161/**
1162 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
1163
1164 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1165 @param[in] RcvdOffer The pointer to the received offer packet.
1166
1167 @retval EFI_SUCCESS Cache and parse the packet successfully.
1168 @retval Others Operation failed.
1169**/
1170EFI_STATUS
1171PxeBcCacheDhcp6Offer (
1172 IN PXEBC_PRIVATE_DATA *Private,
1173 IN EFI_DHCP6_PACKET *RcvdOffer
1174 )
1175{
1176 PXEBC_DHCP6_PACKET_CACHE *Cache6;
1177 EFI_DHCP6_PACKET *Offer;
1178 PXEBC_OFFER_TYPE OfferType;
1179 EFI_STATUS Status;
1180
1181 Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
1182 Offer = &Cache6->Packet.Offer;
1183
1184 //
1185 // Cache the content of DHCPv6 packet firstly.
1186 //
1187 Status = PxeBcCacheDhcp6Packet (Offer, RcvdOffer);
1188 if (EFI_ERROR (Status)) {
1189 return Status;
1190 }
1191
1192 //
1193 // Validate the DHCPv6 packet, and parse the options and offer type.
1194 //
1195 if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {
1196 return EFI_ABORTED;
1197 }
1198
1199 //
1200 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
1201 //
1202 OfferType = Cache6->OfferType;
1203 ASSERT (OfferType < PxeOfferTypeMax);
1204 ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
1205
1206 if (IS_PROXY_OFFER (OfferType)) {
1207 //
1208 // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
1209 //
1210 Private->IsProxyRecved = TRUE;
1211
1212 if (OfferType == PxeOfferTypeProxyBinl) {
1213 //
1214 // Cache all proxy BINL offers.
1215 //
1216 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
1217 Private->OfferCount[OfferType]++;
1218 } else if (((OfferType == PxeOfferTypeProxyPxe10) || (OfferType == PxeOfferTypeProxyWfm11a)) &&
1219 (Private->OfferCount[OfferType] < 1))
1220 {
1221 //
1222 // Only cache the first PXE10/WFM11a offer, and discard the others.
1223 //
1224 Private->OfferIndex[OfferType][0] = Private->OfferNum;
1225 Private->OfferCount[OfferType] = 1;
1226 } else {
1227 return EFI_ABORTED;
1228 }
1229 } else {
1230 //
1231 // It's a DHCPv6 offer with yiaddr, and cache them all.
1232 //
1233 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
1234 Private->OfferCount[OfferType]++;
1235 }
1236
1237 Private->OfferNum++;
1238
1239 return EFI_SUCCESS;
1240}
1241
1242/**
1243 Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
1244
1245 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1246
1247**/
1248VOID
1249PxeBcSelectDhcp6Offer (
1250 IN PXEBC_PRIVATE_DATA *Private
1251 )
1252{
1253 UINT32 Index;
1254 UINT32 OfferIndex;
1255 PXEBC_OFFER_TYPE OfferType;
1256
1257 Private->SelectIndex = 0;
1258
1259 if (Private->IsOfferSorted) {
1260 //
1261 // Select offer by default policy.
1262 //
1263 if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
1264 //
1265 // 1. DhcpPxe10 offer
1266 //
1267 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
1268 } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
1269 //
1270 // 2. DhcpWfm11a offer
1271 //
1272 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
1273 } else if ((Private->OfferCount[PxeOfferTypeDhcpOnly] > 0) &&
1274 (Private->OfferCount[PxeOfferTypeProxyPxe10] > 0))
1275 {
1276 //
1277 // 3. DhcpOnly offer and ProxyPxe10 offer.
1278 //
1279 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1280 Private->SelectProxyType = PxeOfferTypeProxyPxe10;
1281 } else if ((Private->OfferCount[PxeOfferTypeDhcpOnly] > 0) &&
1282 (Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0))
1283 {
1284 //
1285 // 4. DhcpOnly offer and ProxyWfm11a offer.
1286 //
1287 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1288 Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
1289 } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
1290 //
1291 // 5. DhcpBinl offer.
1292 //
1293 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
1294 } else if ((Private->OfferCount[PxeOfferTypeDhcpOnly] > 0) &&
1295 (Private->OfferCount[PxeOfferTypeProxyBinl] > 0))
1296 {
1297 //
1298 // 6. DhcpOnly offer and ProxyBinl offer.
1299 //
1300 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1301 Private->SelectProxyType = PxeOfferTypeProxyBinl;
1302 } else {
1303 //
1304 // 7. DhcpOnly offer with bootfilename.
1305 //
1306 for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {
1307 OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];
1308 if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {
1309 Private->SelectIndex = OfferIndex + 1;
1310 break;
1311 }
1312 }
1313 }
1314 } else {
1315 //
1316 // Select offer by received order.
1317 //
1318 for (Index = 0; Index < Private->OfferNum; Index++) {
1319 OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
1320
1321 if (IS_PROXY_OFFER (OfferType)) {
1322 //
1323 // Skip proxy offers
1324 //
1325 continue;
1326 }
1327
1328 if (!Private->IsProxyRecved &&
1329 (OfferType == PxeOfferTypeDhcpOnly) &&
1330 (Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL))
1331 {
1332 //
1333 // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
1334 //
1335 continue;
1336 }
1337
1338 Private->SelectIndex = Index + 1;
1339 break;
1340 }
1341 }
1342}
1343
1344/**
1345 Cache the DHCPv6 DNS Server addresses
1346
1347 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1348 @param[in] Cache6 The pointer to PXEBC_DHCP6_PACKET_CACHE.
1349
1350 @retval EFI_SUCCESS Cache the DHCPv6 DNS Server address successfully.
1351 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1352 @retval EFI_DEVICE_ERROR The DNS Server Address Length provided by a untrusted
1353 option is not a multiple of 16 bytes (sizeof (EFI_IPv6_ADDRESS)).
1354**/
1355EFI_STATUS
1356PxeBcCacheDnsServerAddresses (
1357 IN PXEBC_PRIVATE_DATA *Private,
1358 IN PXEBC_DHCP6_PACKET_CACHE *Cache6
1359 )
1360{
1361 UINT16 DnsServerLen;
1362
1363 DnsServerLen = NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->OpLen);
1364 //
1365 // Make sure that the number is nonzero
1366 //
1367 if (DnsServerLen == 0) {
1368 return EFI_DEVICE_ERROR;
1369 }
1370
1371 //
1372 // Make sure the DnsServerlen is a multiple of EFI_IPv6_ADDRESS (16)
1373 //
1374 if (DnsServerLen % sizeof (EFI_IPv6_ADDRESS) != 0) {
1375 return EFI_DEVICE_ERROR;
1376 }
1377
1378 //
1379 // This code is currently written to only support a single DNS Server instead
1380 // of multiple such as is spec defined (RFC3646, Section 3). The proper behavior
1381 // would be to allocate the full space requested, CopyMem all of the data,
1382 // and then add a DnsServerCount field to Private and update additional code
1383 // that depends on this.
1384 //
1385 // To support multiple DNS servers the `AllocationSize` would need to be changed to DnsServerLen
1386 //
1387 // This is tracked in https://bugzilla.tianocore.org/show_bug.cgi?id=1886
1388 //
1389 Private->DnsServer = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS));
1390 if (Private->DnsServer == NULL) {
1391 return EFI_OUT_OF_RESOURCES;
1392 }
1393
1394 //
1395 // Intentionally only copy over the first server address.
1396 // To support multiple DNS servers, the `Length` would need to be changed to DnsServerLen
1397 //
1398 CopyMem (Private->DnsServer, Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->Data, sizeof (EFI_IPv6_ADDRESS));
1399
1400 return EFI_SUCCESS;
1401}
1402
1403/**
1404 Handle the DHCPv6 offer packet.
1405
1406 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1407
1408 @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully.
1409 @retval EFI_NO_RESPONSE No response to the following request packet.
1410 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1411 @retval EFI_BUFFER_TOO_SMALL Can't cache the offer pacet.
1412
1413**/
1414EFI_STATUS
1415PxeBcHandleDhcp6Offer (
1416 IN PXEBC_PRIVATE_DATA *Private
1417 )
1418{
1419 PXEBC_DHCP6_PACKET_CACHE *Cache6;
1420 EFI_STATUS Status;
1421 PXEBC_OFFER_TYPE OfferType;
1422 UINT32 ProxyIndex;
1423 UINT32 SelectIndex;
1424 UINT32 Index;
1425
1426 ASSERT (Private != NULL);
1427 ASSERT (Private->SelectIndex > 0);
1428 SelectIndex = (UINT32)(Private->SelectIndex - 1);
1429 ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);
1430 Cache6 = &Private->OfferBuffer[SelectIndex].Dhcp6;
1431 Status = EFI_SUCCESS;
1432
1433 //
1434 // First try to cache DNS server addresses if DHCP6 offer provides.
1435 //
1436 if (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER] != NULL) {
1437 Status = PxeBcCacheDnsServerAddresses (Private, Cache6);
1438 if (EFI_ERROR (Status)) {
1439 return Status;
1440 }
1441 }
1442
1443 if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {
1444 //
1445 // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
1446 //
1447 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {
1448 Status = EFI_NO_RESPONSE;
1449 }
1450 } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {
1451 if (Private->IsProxyRecved) {
1452 //
1453 // DhcpOnly offer is selected, so need try to request bootfilename.
1454 //
1455 ProxyIndex = 0;
1456 if (Private->IsOfferSorted) {
1457 //
1458 // The proxy offer should be determined if select by default policy.
1459 // IsOfferSorted means all offers are labeled by OfferIndex.
1460 //
1461 ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
1462
1463 if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
1464 //
1465 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1466 //
1467 for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
1468 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
1469 if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {
1470 break;
1471 }
1472 }
1473
1474 if (Index == Private->OfferCount[Private->SelectProxyType]) {
1475 Status = EFI_NO_RESPONSE;
1476 }
1477 } else {
1478 //
1479 // For other proxy offers (pxe10 or wfm11a), only one is buffered.
1480 //
1481 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
1482 }
1483 } else {
1484 //
1485 // The proxy offer should not be determined if select by received order.
1486 //
1487 Status = EFI_NO_RESPONSE;
1488
1489 for (Index = 0; Index < Private->OfferNum; Index++) {
1490 OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
1491
1492 if (!IS_PROXY_OFFER (OfferType)) {
1493 //
1494 // Skip non proxy dhcp offers.
1495 //
1496 continue;
1497 }
1498
1499 if (OfferType == PxeOfferTypeProxyBinl) {
1500 //
1501 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1502 //
1503 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {
1504 continue;
1505 }
1506 }
1507
1508 Private->SelectProxyType = OfferType;
1509 ProxyIndex = Index;
1510 Status = EFI_SUCCESS;
1511 break;
1512 }
1513 }
1514
1515 if (!EFI_ERROR (Status) && (Private->SelectProxyType != PxeOfferTypeProxyBinl)) {
1516 //
1517 // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
1518 //
1519 Status = PxeBcCopyDhcp6Proxy (Private, ProxyIndex);
1520 }
1521 } else {
1522 //
1523 // Otherwise, the bootfilename must be included in DhcpOnly offer.
1524 //
1525 ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
1526 }
1527 }
1528
1529 if (!EFI_ERROR (Status)) {
1530 //
1531 // All PXE boot information is ready by now.
1532 //
1533 Status = PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);
1534 Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;
1535 }
1536
1537 return Status;
1538}
1539
1540/**
1541 Unregister the address by Ip6Config protocol.
1542
1543 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1544
1545**/
1546VOID
1547PxeBcUnregisterIp6Address (
1548 IN PXEBC_PRIVATE_DATA *Private
1549 )
1550{
1551 if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {
1552 //
1553 // PXE driver change the policy of IP6 driver, it's a chance to recover.
1554 // Keep the point and there is no enough requirements to do recovery.
1555 //
1556 }
1557}
1558
1559/**
1560 Check whether IP driver could route the message which will be sent to ServerIp address.
1561
1562 This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid
1563 route is found in IP6 route table, the address will be filed in GatewayAddr and return.
1564
1565 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1566 @param[in] TimeOutInSecond Timeout value in seconds.
1567 @param[out] GatewayAddr Pointer to store the gateway IP address.
1568
1569 @retval EFI_SUCCESS Found a valid gateway address successfully.
1570 @retval EFI_TIMEOUT The operation is time out.
1571 @retval Other Unexpected error happened.
1572
1573**/
1574EFI_STATUS
1575PxeBcCheckRouteTable (
1576 IN PXEBC_PRIVATE_DATA *Private,
1577 IN UINTN TimeOutInSecond,
1578 OUT EFI_IPv6_ADDRESS *GatewayAddr
1579 )
1580{
1581 EFI_STATUS Status;
1582 EFI_IP6_PROTOCOL *Ip6;
1583 EFI_IP6_MODE_DATA Ip6ModeData;
1584 UINTN Index;
1585 EFI_EVENT TimeOutEvt;
1586 UINTN RetryCount;
1587 BOOLEAN GatewayIsFound;
1588
1589 ASSERT (GatewayAddr != NULL);
1590 ASSERT (Private != NULL);
1591
1592 Ip6 = Private->Ip6;
1593 GatewayIsFound = FALSE;
1594 RetryCount = 0;
1595 TimeOutEvt = NULL;
1596 ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));
1597
1598 while (TRUE) {
1599 Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);
1600 if (EFI_ERROR (Status)) {
1601 goto ON_EXIT;
1602 }
1603
1604 //
1605 // Find out the gateway address which can route the message which send to ServerIp.
1606 //
1607 for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {
1608 if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {
1609 IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);
1610 GatewayIsFound = TRUE;
1611 break;
1612 }
1613 }
1614
1615 if (Ip6ModeData.AddressList != NULL) {
1616 FreePool (Ip6ModeData.AddressList);
1617 }
1618
1619 if (Ip6ModeData.GroupTable != NULL) {
1620 FreePool (Ip6ModeData.GroupTable);
1621 }
1622
1623 if (Ip6ModeData.RouteTable != NULL) {
1624 FreePool (Ip6ModeData.RouteTable);
1625 }
1626
1627 if (Ip6ModeData.NeighborCache != NULL) {
1628 FreePool (Ip6ModeData.NeighborCache);
1629 }
1630
1631 if (Ip6ModeData.PrefixTable != NULL) {
1632 FreePool (Ip6ModeData.PrefixTable);
1633 }
1634
1635 if (Ip6ModeData.IcmpTypeList != NULL) {
1636 FreePool (Ip6ModeData.IcmpTypeList);
1637 }
1638
1639 if (GatewayIsFound || (RetryCount == TimeOutInSecond)) {
1640 break;
1641 }
1642
1643 RetryCount++;
1644
1645 //
1646 // Delay 1 second then recheck it again.
1647 //
1648 if (TimeOutEvt == NULL) {
1649 Status = gBS->CreateEvent (
1650 EVT_TIMER,
1651 TPL_CALLBACK,
1652 NULL,
1653 NULL,
1654 &TimeOutEvt
1655 );
1656 if (EFI_ERROR (Status)) {
1657 goto ON_EXIT;
1658 }
1659 }
1660
1661 Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);
1662 if (EFI_ERROR (Status)) {
1663 goto ON_EXIT;
1664 }
1665
1666 while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
1667 Ip6->Poll (Ip6);
1668 }
1669 }
1670
1671ON_EXIT:
1672 if (TimeOutEvt != NULL) {
1673 gBS->CloseEvent (TimeOutEvt);
1674 }
1675
1676 if (GatewayIsFound) {
1677 Status = EFI_SUCCESS;
1678 } else if (RetryCount == TimeOutInSecond) {
1679 Status = EFI_TIMEOUT;
1680 }
1681
1682 return Status;
1683}
1684
1685/**
1686 Register the ready station address and gateway by Ip6Config protocol.
1687
1688 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1689 @param[in] Address The pointer to the ready address.
1690
1691 @retval EFI_SUCCESS Registered the address successfully.
1692 @retval Others Failed to register the address.
1693
1694**/
1695EFI_STATUS
1696PxeBcRegisterIp6Address (
1697 IN PXEBC_PRIVATE_DATA *Private,
1698 IN EFI_IPv6_ADDRESS *Address
1699 )
1700{
1701 EFI_IP6_PROTOCOL *Ip6;
1702 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
1703 EFI_IP6_CONFIG_POLICY Policy;
1704 EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr;
1705 EFI_IPv6_ADDRESS GatewayAddr;
1706 UINTN DataSize;
1707 EFI_EVENT MappedEvt;
1708 EFI_STATUS Status;
1709 BOOLEAN NoGateway;
1710 EFI_IPv6_ADDRESS *Ip6Addr;
1711 UINTN Index;
1712
1713 Status = EFI_SUCCESS;
1714 MappedEvt = NULL;
1715 Ip6Addr = NULL;
1716 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
1717 Ip6Cfg = Private->Ip6Cfg;
1718 Ip6 = Private->Ip6;
1719 NoGateway = FALSE;
1720
1721 ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
1722 CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));
1723
1724 Status = Ip6->Configure (Ip6, &Private->Ip6CfgData);
1725 if (EFI_ERROR (Status)) {
1726 goto ON_EXIT;
1727 }
1728
1729 //
1730 // Retrieve the gateway address from IP6 route table.
1731 //
1732 Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);
1733 if (EFI_ERROR (Status)) {
1734 NoGateway = TRUE;
1735 }
1736
1737 //
1738 // There is no channel between IP6 and PXE driver about address setting,
1739 // so it has to set the new address by Ip6ConfigProtocol manually.
1740 //
1741 Policy = Ip6ConfigPolicyManual;
1742 Status = Ip6Cfg->SetData (
1743 Ip6Cfg,
1744 Ip6ConfigDataTypePolicy,
1745 sizeof (EFI_IP6_CONFIG_POLICY),
1746 &Policy
1747 );
1748 if (EFI_ERROR (Status)) {
1749 //
1750 // There is no need to recover later.
1751 //
1752 Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
1753 goto ON_EXIT;
1754 }
1755
1756 //
1757 // Create a notify event to set address flag when DAD if IP6 driver succeeded.
1758 //
1759 Status = gBS->CreateEvent (
1760 EVT_NOTIFY_SIGNAL,
1761 TPL_NOTIFY,
1762 PxeBcCommonNotify,
1763 &Private->IsAddressOk,
1764 &MappedEvt
1765 );
1766 if (EFI_ERROR (Status)) {
1767 goto ON_EXIT;
1768 }
1769
1770 Private->IsAddressOk = FALSE;
1771 Status = Ip6Cfg->RegisterDataNotify (
1772 Ip6Cfg,
1773 Ip6ConfigDataTypeManualAddress,
1774 MappedEvt
1775 );
1776 if (EFI_ERROR (Status)) {
1777 goto ON_EXIT;
1778 }
1779
1780 Status = Ip6Cfg->SetData (
1781 Ip6Cfg,
1782 Ip6ConfigDataTypeManualAddress,
1783 sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS),
1784 &CfgAddr
1785 );
1786 if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
1787 goto ON_EXIT;
1788 } else if (Status == EFI_NOT_READY) {
1789 //
1790 // Poll the network until the asynchronous process is finished.
1791 //
1792 while (!Private->IsAddressOk) {
1793 Ip6->Poll (Ip6);
1794 }
1795
1796 //
1797 // Check whether the IP6 address setting is successed.
1798 //
1799 DataSize = 0;
1800 Status = Ip6Cfg->GetData (
1801 Ip6Cfg,
1802 Ip6ConfigDataTypeManualAddress,
1803 &DataSize,
1804 NULL
1805 );
1806 if ((Status != EFI_BUFFER_TOO_SMALL) || (DataSize == 0)) {
1807 Status = EFI_DEVICE_ERROR;
1808 goto ON_EXIT;
1809 }
1810
1811 Ip6Addr = AllocatePool (DataSize);
1812 if (Ip6Addr == NULL) {
1813 return EFI_OUT_OF_RESOURCES;
1814 }
1815
1816 Status = Ip6Cfg->GetData (
1817 Ip6Cfg,
1818 Ip6ConfigDataTypeManualAddress,
1819 &DataSize,
1820 (VOID *)Ip6Addr
1821 );
1822 if (EFI_ERROR (Status)) {
1823 Status = EFI_DEVICE_ERROR;
1824 goto ON_EXIT;
1825 }
1826
1827 for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) {
1828 if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) {
1829 break;
1830 }
1831 }
1832
1833 if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {
1834 Status = EFI_ABORTED;
1835 goto ON_EXIT;
1836 }
1837 }
1838
1839 //
1840 // Set the default gateway address back if needed.
1841 //
1842 if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) {
1843 Status = Ip6Cfg->SetData (
1844 Ip6Cfg,
1845 Ip6ConfigDataTypeGateway,
1846 sizeof (EFI_IPv6_ADDRESS),
1847 &GatewayAddr
1848 );
1849 if (EFI_ERROR (Status)) {
1850 goto ON_EXIT;
1851 }
1852 }
1853
1854ON_EXIT:
1855 if (MappedEvt != NULL) {
1856 Ip6Cfg->UnregisterDataNotify (
1857 Ip6Cfg,
1858 Ip6ConfigDataTypeManualAddress,
1859 MappedEvt
1860 );
1861 gBS->CloseEvent (MappedEvt);
1862 }
1863
1864 if (Ip6Addr != NULL) {
1865 FreePool (Ip6Addr);
1866 }
1867
1868 return Status;
1869}
1870
1871/**
1872 Set the IP6 policy to Automatic.
1873
1874 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1875
1876 @retval EFI_SUCCESS Switch the IP policy successfully.
1877 @retval Others Unexpected error happened.
1878
1879**/
1880EFI_STATUS
1881PxeBcSetIp6Policy (
1882 IN PXEBC_PRIVATE_DATA *Private
1883 )
1884{
1885 EFI_IP6_CONFIG_POLICY Policy;
1886 EFI_STATUS Status;
1887 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
1888 UINTN DataSize;
1889
1890 Ip6Cfg = Private->Ip6Cfg;
1891 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
1892
1893 //
1894 // Get and store the current policy of IP6 driver.
1895 //
1896 Status = Ip6Cfg->GetData (
1897 Ip6Cfg,
1898 Ip6ConfigDataTypePolicy,
1899 &DataSize,
1900 &Private->Ip6Policy
1901 );
1902 if (EFI_ERROR (Status)) {
1903 return Status;
1904 }
1905
1906 if (Private->Ip6Policy == Ip6ConfigPolicyManual) {
1907 Policy = Ip6ConfigPolicyAutomatic;
1908 Status = Ip6Cfg->SetData (
1909 Ip6Cfg,
1910 Ip6ConfigDataTypePolicy,
1911 sizeof (EFI_IP6_CONFIG_POLICY),
1912 &Policy
1913 );
1914 if (EFI_ERROR (Status)) {
1915 //
1916 // There is no need to recover later.
1917 //
1918 Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
1919 }
1920 }
1921
1922 return Status;
1923}
1924
1925/**
1926 This function will register the station IP address and flush IP instance to start using the new IP address.
1927
1928 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1929
1930 @retval EFI_SUCCESS The new IP address has been configured successfully.
1931 @retval Others Failed to configure the address.
1932
1933**/
1934EFI_STATUS
1935PxeBcSetIp6Address (
1936 IN PXEBC_PRIVATE_DATA *Private
1937 )
1938{
1939 EFI_STATUS Status;
1940 EFI_DHCP6_PROTOCOL *Dhcp6;
1941
1942 Dhcp6 = Private->Dhcp6;
1943
1944 CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS));
1945 CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
1946
1947 Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);
1948 if (EFI_ERROR (Status)) {
1949 Dhcp6->Stop (Dhcp6);
1950 return Status;
1951 }
1952
1953 Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL);
1954 if (EFI_ERROR (Status)) {
1955 PxeBcUnregisterIp6Address (Private);
1956 Dhcp6->Stop (Dhcp6);
1957 return Status;
1958 }
1959
1960 AsciiPrint ("\n Station IP address is ");
1961 PxeBcShowIp6Addr (&Private->StationIp.v6);
1962
1963 return EFI_SUCCESS;
1964}
1965
1966/**
1967 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
1968 to intercept events that occurred in the configuration process.
1969
1970 @param[in] This The pointer to the EFI DHCPv6 Protocol.
1971 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
1972 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
1973 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
1974 state transition.
1975 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
1976 @param[out] NewPacket The packet that is used to replace the Packet above.
1977
1978 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
1979 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
1980 driver will continue to wait for more packets.
1981 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
1982
1983**/
1984EFI_STATUS
1985EFIAPI
1986PxeBcDhcp6CallBack (
1987 IN EFI_DHCP6_PROTOCOL *This,
1988 IN VOID *Context,
1989 IN EFI_DHCP6_STATE CurrentState,
1990 IN EFI_DHCP6_EVENT Dhcp6Event,
1991 IN EFI_DHCP6_PACKET *Packet,
1992 OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL
1993 )
1994{
1995 PXEBC_PRIVATE_DATA *Private;
1996 EFI_PXE_BASE_CODE_MODE *Mode;
1997 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
1998 EFI_DHCP6_PACKET *SelectAd;
1999 EFI_STATUS Status;
2000 BOOLEAN Received;
2001
2002 if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&
2003 (Dhcp6Event != Dhcp6SelectAdvertise) &&
2004 (Dhcp6Event != Dhcp6SendSolicit) &&
2005 (Dhcp6Event != Dhcp6SendRequest) &&
2006 (Dhcp6Event != Dhcp6RcvdReply))
2007 {
2008 return EFI_SUCCESS;
2009 }
2010
2011 ASSERT (Packet != NULL);
2012
2013 Private = (PXEBC_PRIVATE_DATA *)Context;
2014 Mode = Private->PxeBc.Mode;
2015 Callback = Private->PxeBcCallback;
2016
2017 //
2018 // Callback to user when any traffic occurred if has.
2019 //
2020 if ((Dhcp6Event != Dhcp6SelectAdvertise) && (Callback != NULL)) {
2021 Received = (BOOLEAN)(Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);
2022 Status = Callback->Callback (
2023 Callback,
2024 Private->Function,
2025 Received,
2026 Packet->Length,
2027 (EFI_PXE_BASE_CODE_PACKET *)&Packet->Dhcp6
2028 );
2029 if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
2030 return EFI_ABORTED;
2031 }
2032 }
2033
2034 Status = EFI_SUCCESS;
2035
2036 switch (Dhcp6Event) {
2037 case Dhcp6SendSolicit:
2038 if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
2039 //
2040 // If the to be sent packet exceeds the maximum length, abort the DHCP process.
2041 //
2042 Status = EFI_ABORTED;
2043 break;
2044 }
2045
2046 //
2047 // Record the first Solicate msg time
2048 //
2049 if (Private->SolicitTimes == 0) {
2050 CalcElapsedTime (Private);
2051 Private->SolicitTimes++;
2052 }
2053
2054 //
2055 // Cache the dhcp discover packet to mode data directly.
2056 //
2057 CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);
2058 break;
2059
2060 case Dhcp6RcvdAdvertise:
2061 Status = EFI_NOT_READY;
2062 if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
2063 //
2064 // Ignore the incoming packets which exceed the maximum length.
2065 //
2066 break;
2067 }
2068
2069 if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
2070 //
2071 // Cache the dhcp offers to OfferBuffer[] for select later, and record
2072 // the OfferIndex and OfferCount.
2073 //
2074 PxeBcCacheDhcp6Offer (Private, Packet);
2075 }
2076
2077 break;
2078
2079 case Dhcp6SendRequest:
2080 if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
2081 //
2082 // If the to be sent packet exceeds the maximum length, abort the DHCP process.
2083 //
2084 Status = EFI_ABORTED;
2085 break;
2086 }
2087
2088 //
2089 // Store the request packet as seed packet for discover.
2090 //
2091 if (Private->Dhcp6Request != NULL) {
2092 FreePool (Private->Dhcp6Request);
2093 }
2094
2095 Private->Dhcp6Request = AllocateZeroPool (Packet->Size);
2096 if (Private->Dhcp6Request != NULL) {
2097 CopyMem (Private->Dhcp6Request, Packet, Packet->Size);
2098 }
2099
2100 break;
2101
2102 case Dhcp6SelectAdvertise:
2103 //
2104 // Select offer by the default policy or by order, and record the SelectIndex
2105 // and SelectProxyType.
2106 //
2107 PxeBcSelectDhcp6Offer (Private);
2108
2109 if (Private->SelectIndex == 0) {
2110 Status = EFI_ABORTED;
2111 } else {
2112 ASSERT (NewPacket != NULL);
2113 SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;
2114 *NewPacket = AllocateZeroPool (SelectAd->Size);
2115 ASSERT (*NewPacket != NULL);
2116 if (*NewPacket == NULL) {
2117 return EFI_ABORTED;
2118 }
2119
2120 CopyMem (*NewPacket, SelectAd, SelectAd->Size);
2121 }
2122
2123 break;
2124
2125 case Dhcp6RcvdReply:
2126 //
2127 // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
2128 // without verification.
2129 //
2130 ASSERT (Private->SelectIndex != 0);
2131 Status = PxeBcCopyDhcp6Ack (Private, Packet, FALSE);
2132 if (EFI_ERROR (Status)) {
2133 Status = EFI_ABORTED;
2134 }
2135
2136 break;
2137
2138 default:
2139 ASSERT (0);
2140 }
2141
2142 return Status;
2143}
2144
2145/**
2146 Build and send out the request packet for the bootfile, and parse the reply.
2147
2148 @param[in] Private The pointer to PxeBc private data.
2149 @param[in] Type PxeBc option boot item type.
2150 @param[in] Layer The pointer to option boot item layer.
2151 @param[in] UseBis Use BIS or not.
2152 @param[in] DestIp The pointer to the server address.
2153
2154 @retval EFI_SUCCESS Successfully discovered the boot file.
2155 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
2156 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
2157 @retval Others Failed to discover the boot file.
2158
2159**/
2160EFI_STATUS
2161PxeBcDhcp6Discover (
2162 IN PXEBC_PRIVATE_DATA *Private,
2163 IN UINT16 Type,
2164 IN UINT16 *Layer,
2165 IN BOOLEAN UseBis,
2166 IN EFI_IP_ADDRESS *DestIp
2167 )
2168{
2169 EFI_PXE_BASE_CODE_UDP_PORT SrcPort;
2170 EFI_PXE_BASE_CODE_UDP_PORT DestPort;
2171 EFI_PXE_BASE_CODE_MODE *Mode;
2172 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
2173 EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover;
2174 UINTN DiscoverLen;
2175 EFI_DHCP6_PACKET *Request;
2176 UINTN RequestLen;
2177 EFI_DHCP6_PACKET *Reply;
2178 UINT8 *RequestOpt;
2179 UINT8 *DiscoverOpt;
2180 UINTN ReadSize;
2181 UINT16 OpCode;
2182 UINT16 OpLen;
2183 UINT32 Random;
2184 EFI_STATUS Status;
2185 UINTN DiscoverLenNeeded;
2186
2187 PxeBc = &Private->PxeBc;
2188 Mode = PxeBc->Mode;
2189 Request = Private->Dhcp6Request;
2190 SrcPort = PXEBC_BS_DISCOVER_PORT;
2191 DestPort = PXEBC_BS_DISCOVER_PORT;
2192
2193 if (!UseBis && (Layer != NULL)) {
2194 *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
2195 }
2196
2197 if (Request == NULL) {
2198 return EFI_DEVICE_ERROR;
2199 }
2200
2201 Status = PseudoRandomU32 (&Random);
2202 if (EFI_ERROR (Status)) {
2203 DEBUG ((DEBUG_ERROR, "%a failed to generate random number: %r\n", __func__, Status));
2204 return Status;
2205 }
2206
2207 DiscoverLenNeeded = sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET);
2208 Discover = AllocateZeroPool (DiscoverLenNeeded);
2209 if (Discover == NULL) {
2210 return EFI_OUT_OF_RESOURCES;
2211 }
2212
2213 //
2214 // Build the discover packet by the cached request packet before.
2215 //
2216 Discover->TransactionId = HTONL (Random);
2217 Discover->MessageType = Request->Dhcp6.Header.MessageType;
2218 RequestOpt = Request->Dhcp6.Option;
2219 DiscoverOpt = Discover->DhcpOptions;
2220 DiscoverLen = sizeof (EFI_DHCP6_HEADER);
2221 RequestLen = DiscoverLen;
2222
2223 //
2224 // The request packet is generated by the UEFI network stack. In the DHCP4 DORA and DHCP6 SARR sequence,
2225 // the first (discover in DHCP4 and solicit in DHCP6) and third (request in both DHCP4 and DHCP6) are
2226 // generated by the DHCP client (the UEFI network stack in this case). By the time this function executes,
2227 // the DHCP sequence already has been executed once (see UEFI Specification Figures 24.2 and 24.3), with
2228 // Private->Dhcp6Request being a cached copy of the DHCP6 request packet that UEFI network stack previously
2229 // generated and sent.
2230 //
2231 // Therefore while this code looks like it could overflow, in practice it's not possible.
2232 //
2233 while (RequestLen < Request->Length) {
2234 OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *)RequestOpt)->OpCode);
2235 OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *)RequestOpt)->OpLen);
2236 if ((OpCode != EFI_DHCP6_IA_TYPE_NA) &&
2237 (OpCode != EFI_DHCP6_IA_TYPE_TA))
2238 {
2239 if (DiscoverLen + OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN > DiscoverLenNeeded) {
2240 Status = EFI_OUT_OF_RESOURCES;
2241 goto ON_ERROR;
2242 }
2243
2244 //
2245 // Copy all the options except IA option.
2246 //
2247 CopyMem (DiscoverOpt, RequestOpt, OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
2248 DiscoverOpt += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
2249 DiscoverLen += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
2250 }
2251
2252 RequestOpt += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
2253 RequestLen += (OpLen + PXEBC_COMBINED_SIZE_OF_OPT_CODE_AND_LEN);
2254 }
2255
2256 Status = PxeBc->UdpWrite (
2257 PxeBc,
2258 0,
2259 &Private->ServerIp,
2260 &DestPort,
2261 NULL,
2262 &Private->StationIp,
2263 &SrcPort,
2264 NULL,
2265 NULL,
2266 &DiscoverLen,
2267 (VOID *)Discover
2268 );
2269 if (EFI_ERROR (Status)) {
2270 goto ON_ERROR;
2271 }
2272
2273 //
2274 // Cache the right PXE reply packet here, set valid flag later.
2275 // Especially for PXE discover packet, store it into mode data here.
2276 //
2277 if (Private->IsDoDiscover) {
2278 CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);
2279 Reply = &Private->PxeReply.Dhcp6.Packet.Ack;
2280 } else {
2281 Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
2282 }
2283
2284 ReadSize = (UINTN)Reply->Size;
2285
2286 //
2287 // Start Udp6Read instance
2288 //
2289 Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
2290 if (EFI_ERROR (Status)) {
2291 goto ON_ERROR;
2292 }
2293
2294 Status = PxeBc->UdpRead (
2295 PxeBc,
2296 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
2297 NULL,
2298 &SrcPort,
2299 &Private->ServerIp,
2300 &DestPort,
2301 NULL,
2302 NULL,
2303 &ReadSize,
2304 (VOID *)&Reply->Dhcp6
2305 );
2306 //
2307 // Stop Udp6Read instance
2308 //
2309 Private->Udp6Read->Configure (Private->Udp6Read, NULL);
2310 if (EFI_ERROR (Status)) {
2311 goto ON_ERROR;
2312 }
2313
2314 return EFI_SUCCESS;
2315
2316ON_ERROR:
2317 if (Discover != NULL) {
2318 FreePool (Discover);
2319 }
2320
2321 return Status;
2322}
2323
2324/**
2325 Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
2326
2327 @param[in] Private The pointer to PxeBc private data.
2328 @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL
2329
2330 @retval EFI_SUCCESS The S.A.R.R. process successfully finished.
2331 @retval Others Failed to finish the S.A.R.R. process.
2332
2333**/
2334EFI_STATUS
2335PxeBcDhcp6Sarr (
2336 IN PXEBC_PRIVATE_DATA *Private,
2337 IN EFI_DHCP6_PROTOCOL *Dhcp6
2338 )
2339{
2340 EFI_PXE_BASE_CODE_MODE *PxeMode;
2341 EFI_DHCP6_CONFIG_DATA Config;
2342 EFI_DHCP6_MODE_DATA Mode;
2343 EFI_DHCP6_RETRANSMISSION *Retransmit;
2344 EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];
2345 UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];
2346 UINT32 OptCount;
2347 EFI_STATUS Status;
2348 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
2349 EFI_STATUS TimerStatus;
2350 EFI_EVENT Timer;
2351 UINT64 GetMappingTimeOut;
2352 UINTN DataSize;
2353 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;
2354
2355 Status = EFI_SUCCESS;
2356 PxeMode = Private->PxeBc.Mode;
2357 Ip6Cfg = Private->Ip6Cfg;
2358 Timer = NULL;
2359
2360 //
2361 // Build option list for the request packet.
2362 //
2363 OptCount = PxeBcBuildDhcp6Options (Private, OptList, Buffer);
2364 ASSERT (OptCount > 0);
2365
2366 Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
2367 if (Retransmit == NULL) {
2368 return EFI_OUT_OF_RESOURCES;
2369 }
2370
2371 ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));
2372 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
2373
2374 Config.OptionCount = OptCount;
2375 Config.OptionList = OptList;
2376 Config.Dhcp6Callback = PxeBcDhcp6CallBack;
2377 Config.CallbackContext = Private;
2378 Config.IaInfoEvent = NULL;
2379 Config.RapidCommit = FALSE;
2380 Config.ReconfigureAccept = FALSE;
2381 Config.IaDescriptor.IaId = Private->IaId;
2382 Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;
2383 Config.SolicitRetransmission = Retransmit;
2384 Retransmit->Irt = 4;
2385 Retransmit->Mrc = 4;
2386 Retransmit->Mrt = 32;
2387 Retransmit->Mrd = 60;
2388
2389 //
2390 // Configure the DHCPv6 instance for PXE boot.
2391 //
2392 Status = Dhcp6->Configure (Dhcp6, &Config);
2393 FreePool (Retransmit);
2394 if (EFI_ERROR (Status)) {
2395 return Status;
2396 }
2397
2398 //
2399 // Initialize the record fields for DHCPv6 offer in private data.
2400 //
2401 Private->IsProxyRecved = FALSE;
2402 Private->OfferNum = 0;
2403 Private->SelectIndex = 0;
2404 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
2405 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
2406
2407 //
2408 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
2409 //
2410 Status = Dhcp6->Start (Dhcp6);
2411 if (Status == EFI_NO_MAPPING) {
2412 //
2413 // IP6 Linklocal address is not available for use, so stop current Dhcp process
2414 // and wait for duplicate address detection to finish.
2415 //
2416 Dhcp6->Stop (Dhcp6);
2417
2418 //
2419 // Get Duplicate Address Detection Transmits count.
2420 //
2421 DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
2422 Status = Ip6Cfg->GetData (
2423 Ip6Cfg,
2424 Ip6ConfigDataTypeDupAddrDetectTransmits,
2425 &DataSize,
2426 &DadXmits
2427 );
2428 if (EFI_ERROR (Status)) {
2429 Dhcp6->Configure (Dhcp6, NULL);
2430 return Status;
2431 }
2432
2433 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
2434 if (EFI_ERROR (Status)) {
2435 Dhcp6->Configure (Dhcp6, NULL);
2436 return Status;
2437 }
2438
2439 GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;
2440 Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);
2441 if (EFI_ERROR (Status)) {
2442 gBS->CloseEvent (Timer);
2443 Dhcp6->Configure (Dhcp6, NULL);
2444 return Status;
2445 }
2446
2447 do {
2448 TimerStatus = gBS->CheckEvent (Timer);
2449 if (!EFI_ERROR (TimerStatus)) {
2450 Status = Dhcp6->Start (Dhcp6);
2451 }
2452 } while (TimerStatus == EFI_NOT_READY);
2453
2454 gBS->CloseEvent (Timer);
2455 }
2456
2457 if (EFI_ERROR (Status)) {
2458 if (Status == EFI_ICMP_ERROR) {
2459 PxeMode->IcmpErrorReceived = TRUE;
2460 }
2461
2462 Dhcp6->Configure (Dhcp6, NULL);
2463 return Status;
2464 }
2465
2466 //
2467 // Get the acquired IPv6 address and store them.
2468 //
2469 Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);
2470 if (EFI_ERROR (Status)) {
2471 Dhcp6->Stop (Dhcp6);
2472 return Status;
2473 }
2474
2475 ASSERT ((Mode.Ia != NULL) && (Mode.Ia->State == Dhcp6Bound));
2476 //
2477 // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the
2478 // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when
2479 // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as
2480 // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery
2481 // to find a valid router address.
2482 //
2483 CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));
2484 if (Mode.ClientId != NULL) {
2485 FreePool (Mode.ClientId);
2486 }
2487
2488 if (Mode.Ia != NULL) {
2489 FreePool (Mode.Ia);
2490 }
2491
2492 //
2493 // Check the selected offer whether BINL retry is needed.
2494 //
2495 Status = PxeBcHandleDhcp6Offer (Private);
2496 if (EFI_ERROR (Status)) {
2497 Dhcp6->Stop (Dhcp6);
2498 return Status;
2499 }
2500
2501 return EFI_SUCCESS;
2502}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette