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 - 2015, Intel Corporation. All rights reserved.<BR>
6
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.
11
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.
14
15 **/
16
17 #include "PxeBcImpl.h"
18
19 //
20 // Well-known multi-cast address defined in section-24.1 of rfc-3315
21 //
22 // ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
23 //
24 EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
25
26 /**
27 Parse out a DHCPv6 option by OptTag, and find the position in buffer.
28
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.
32
33 @retval NULL Failed to parse the required option.
34 @retval Others The postion of the required option in buffer.
35
36 **/
37 EFI_DHCP6_PACKET_OPTION *
PxeBcParseDhcp6Options(IN UINT8 * Buffer,IN UINT32 Length,IN UINT16 OptTag)38 PxeBcParseDhcp6Options (
39 IN UINT8 *Buffer,
40 IN UINT32 Length,
41 IN UINT16 OptTag
42 )
43 {
44 EFI_DHCP6_PACKET_OPTION *Option;
45 UINT32 Offset;
46
47 Option = (EFI_DHCP6_PACKET_OPTION *) Buffer;
48 Offset = 0;
49
50 //
51 // OpLen and OpCode here are both stored in network order.
52 //
53 while (Offset < Length) {
54
55 if (NTOHS (Option->OpCode) == OptTag) {
56
57 return Option;
58 }
59
60 Offset += (NTOHS(Option->OpLen) + 4);
61 Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);
62 }
63
64 return NULL;
65 }
66
67
68 /**
69 Build the options buffer for the DHCPv6 request packet.
70
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.
74
75 @return Index The count of the built-in options.
76
77 **/
78 UINT32
PxeBcBuildDhcp6Options(IN PXEBC_PRIVATE_DATA * Private,OUT EFI_DHCP6_PACKET_OPTION ** OptList,IN UINT8 * Buffer)79 PxeBcBuildDhcp6Options (
80 IN PXEBC_PRIVATE_DATA *Private,
81 OUT EFI_DHCP6_PACKET_OPTION **OptList,
82 IN UINT8 *Buffer
83 )
84 {
85 PXEBC_DHCP6_OPTION_ENTRY OptEnt;
86 UINT32 Index;
87 UINT16 Value;
88
89 Index = 0;
90 OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer;
91
92 //
93 // Append client option request option
94 //
95 OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ORO);
96 OptList[Index]->OpLen = HTONS (4);
97 OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;
98 OptEnt.Oro->OpCode[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL);
99 OptEnt.Oro->OpCode[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM);
100 Index++;
101 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
102
103 //
104 // Append client network device interface option
105 //
106 OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_UNDI);
107 OptList[Index]->OpLen = HTONS ((UINT16)3);
108 OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;
109
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;
114 } else {
115 OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
116 OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
117 OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
118 }
119
120 Index++;
121 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
122
123 //
124 // Append client system architecture option
125 //
126 OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ARCH);
127 OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));
128 OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;
129 Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
130 CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
131 Index++;
132 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
133
134 //
135 // Append vendor class option to store the PXE class identifier.
136 //
137 OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS);
138 OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));
139 OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;
140 OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);
141 OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));
142 CopyMem (
143 &OptEnt.VendorClass->ClassId,
144 DEFAULT_CLASS_ID_DATA,
145 sizeof (PXEBC_CLASS_ID)
146 );
147 PxeBcUintnToAscDecWithFormat (
148 EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,
149 OptEnt.VendorClass->ClassId.ArchitectureType,
150 sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)
151 );
152
153 if (Private->Nii != NULL) {
154 CopyMem (
155 OptEnt.VendorClass->ClassId.InterfaceName,
156 Private->Nii->StringId,
157 sizeof (OptEnt.VendorClass->ClassId.InterfaceName)
158 );
159 PxeBcUintnToAscDecWithFormat (
160 Private->Nii->MajorVer,
161 OptEnt.VendorClass->ClassId.UndiMajor,
162 sizeof (OptEnt.VendorClass->ClassId.UndiMajor)
163 );
164 PxeBcUintnToAscDecWithFormat (
165 Private->Nii->MinorVer,
166 OptEnt.VendorClass->ClassId.UndiMinor,
167 sizeof (OptEnt.VendorClass->ClassId.UndiMinor)
168 );
169 }
170
171 Index++;
172
173 return Index;
174 }
175
176
177 /**
178 Cache the DHCPv6 packet.
179
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.
182
183 **/
184 VOID
PxeBcCacheDhcp6Packet(IN EFI_DHCP6_PACKET * Dst,IN EFI_DHCP6_PACKET * Src)185 PxeBcCacheDhcp6Packet (
186 IN EFI_DHCP6_PACKET *Dst,
187 IN EFI_DHCP6_PACKET *Src
188 )
189 {
190 ASSERT (Dst->Size >= Src->Length);
191
192 CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
193 Dst->Length = Src->Length;
194 }
195
196
197 /**
198 Free all the nodes in the list for boot file.
199
200 @param[in] Head The pointer to the head of list.
201
202 **/
203 VOID
PxeBcFreeBootFileOption(IN LIST_ENTRY * Head)204 PxeBcFreeBootFileOption (
205 IN LIST_ENTRY *Head
206 )
207 {
208 LIST_ENTRY *Entry;
209 LIST_ENTRY *NextEntry;
210 PXEBC_DHCP6_OPTION_NODE *Node;
211
212 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {
213 Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);
214 RemoveEntryList (Entry);
215 FreePool (Node);
216 }
217 }
218
219
220 /**
221 Parse the Boot File URL option.
222
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.
227
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.
231
232 **/
233 EFI_STATUS
PxeBcExtractBootFileUrl(OUT UINT8 ** FileName,IN OUT EFI_IPv6_ADDRESS * SrvAddr,IN CHAR8 * BootFile,IN UINT16 Length)234 PxeBcExtractBootFileUrl (
235 OUT UINT8 **FileName,
236 IN OUT EFI_IPv6_ADDRESS *SrvAddr,
237 IN CHAR8 *BootFile,
238 IN UINT16 Length
239 )
240 {
241 UINT16 PrefixLen;
242 CHAR8 *BootFileNamePtr;
243 CHAR8 *BootFileName;
244 UINT16 BootFileNameLen;
245 CHAR8 *TmpStr;
246 CHAR8 TmpChar;
247 CHAR8 *ServerAddressOption;
248 CHAR8 *ServerAddress;
249 CHAR8 *ModeStr;
250 EFI_STATUS Status;
251
252 //
253 // The format of the Boot File URL option is:
254 //
255 // 0 1 2 3
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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
260 // | |
261 // . bootfile-url (variable length) .
262 // | |
263 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
264 //
265
266 //
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.
271 //
272 PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);
273
274 if (Length <= PrefixLen ||
275 CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {
276 return EFI_NOT_FOUND;
277 }
278
279 BootFile = BootFile + PrefixLen;
280 Length = (UINT16) (Length - PrefixLen);
281
282 TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);
283 if (TmpStr == NULL) {
284 return EFI_OUT_OF_RESOURCES;
285 }
286
287 CopyMem (TmpStr, BootFile, Length);
288 TmpStr[Length] = '\0';
289
290 //
291 // Get the part of SERVER_ADDRESS string.
292 //
293 ServerAddressOption = TmpStr;
294 if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) {
295 FreePool (TmpStr);
296 return EFI_INVALID_PARAMETER;
297 }
298
299 ServerAddressOption ++;
300 ServerAddress = ServerAddressOption;
301 while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {
302 ServerAddress++;
303 }
304
305 if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {
306 FreePool (TmpStr);
307 return EFI_INVALID_PARAMETER;
308 }
309
310 *ServerAddress = '\0';
311
312 //
313 // Convert the string of server address to Ipv6 address format and store it.
314 //
315 Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);
316 if (EFI_ERROR (Status)) {
317 FreePool (TmpStr);
318 return Status;
319 }
320
321 //
322 // Get the part of BOOTFILE_NAME string.
323 //
324 BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);
325 if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {
326 FreePool (TmpStr);
327 return EFI_INVALID_PARAMETER;
328 }
329
330 ++BootFileNamePtr;
331 BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);
332 if (BootFileNameLen != 0 || FileName != NULL) {
333 //
334 // Remove trailing mode=octet if present and ignore. All other modes are
335 // invalid for netboot6, so reject them.
336 //
337 ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet");
338 if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') {
339 *ModeStr = '\0';
340 } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) {
341 return EFI_INVALID_PARAMETER;
342 }
343
344 //
345 // Extract boot file name from URL.
346 //
347 BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen);
348 if (BootFileName == NULL) {
349 FreePool (TmpStr);
350 return EFI_OUT_OF_RESOURCES;
351 }
352 *FileName = (UINT8*) BootFileName;
353
354 //
355 // Decode percent-encoding in boot file name.
356 //
357 while (*BootFileNamePtr != '\0') {
358 if (*BootFileNamePtr == '%') {
359 TmpChar = *(BootFileNamePtr+ 3);
360 *(BootFileNamePtr+ 3) = '\0';
361 *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1));
362 BootFileName++;
363 *(BootFileNamePtr+ 3) = TmpChar;
364 BootFileNamePtr += 3;
365 } else {
366 *BootFileName = *BootFileNamePtr;
367 BootFileName++;
368 BootFileNamePtr++;
369 }
370 }
371 *BootFileName = '\0';
372 }
373
374 FreePool (TmpStr);
375
376 return EFI_SUCCESS;
377 }
378
379
380 /**
381 Parse the Boot File Parameter option.
382
383 @param[in] BootFilePara The pointer to boot file parameter option data.
384 @param[out] BootFileSize The pointer to the parsed boot file size.
385
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.
388
389 **/
390 EFI_STATUS
PxeBcExtractBootFileParam(IN CHAR8 * BootFilePara,OUT UINT16 * BootFileSize)391 PxeBcExtractBootFileParam (
392 IN CHAR8 *BootFilePara,
393 OUT UINT16 *BootFileSize
394 )
395 {
396 UINT16 Length;
397 UINT8 Index;
398 UINT8 Digit;
399 UINT32 Size;
400
401 CopyMem (&Length, BootFilePara, sizeof (UINT16));
402 Length = NTOHS (Length);
403
404 //
405 // The BootFile Size should be 1~5 byte ASCII strings
406 //
407 if (Length < 1 || Length > 5) {
408 return EFI_NOT_FOUND;
409 }
410
411 //
412 // Extract the value of BootFile Size.
413 //
414 BootFilePara = BootFilePara + sizeof (UINT16);
415 Size = 0;
416 for (Index = 0; Index < Length; Index++) {
417 if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {
418 return EFI_NOT_FOUND;
419 }
420
421 Size = (Size + Digit) * 10;
422 }
423
424 Size = Size / 10;
425 if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {
426 return EFI_NOT_FOUND;
427 }
428
429 *BootFileSize = (UINT16) Size;
430 return EFI_SUCCESS;
431 }
432
433
434 /**
435 Parse the cached DHCPv6 packet, including all the options.
436
437 @param[in] Cache6 The pointer to a cached DHCPv6 packet.
438
439 @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
440 @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.
441
442 **/
443 EFI_STATUS
PxeBcParseDhcp6Packet(IN PXEBC_DHCP6_PACKET_CACHE * Cache6)444 PxeBcParseDhcp6Packet (
445 IN PXEBC_DHCP6_PACKET_CACHE *Cache6
446 )
447 {
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;
453 BOOLEAN IsPxeOffer;
454 UINT32 Offset;
455 UINT32 Length;
456 UINT32 EnterpriseNum;
457
458 IsProxyOffer = TRUE;
459 IsPxeOffer = FALSE;
460 Offer = &Cache6->Packet.Offer;
461 Options = Cache6->OptList;
462
463 ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));
464
465 Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);
466 Offset = 0;
467 Length = GET_DHCP6_OPTION_SIZE (Offer);
468
469 //
470 // OpLen and OpCode here are both stored in network order, since they are from original packet.
471 //
472 while (Offset < Length) {
473
474 if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) {
475 Options[PXEBC_DHCP6_IDX_IA_NA] = Option;
476 } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) {
477 //
478 // The server sends this option to inform the client about an URL to a boot file.
479 //
480 Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;
481 } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) {
482 Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
483 } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) {
484 Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;
485 }
486
487 Offset += (NTOHS (Option->OpLen) + 4);
488 Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);
489 }
490
491 //
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.
494 //
495 Option = Options[PXEBC_DHCP6_IDX_IA_NA];
496 if (Option != NULL) {
497 Option = PxeBcParseDhcp6Options (
498 Option->Data + 12,
499 NTOHS (Option->OpLen),
500 PXEBC_DHCP6_OPT_STATUS_CODE
501 );
502 if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {
503 IsProxyOffer = FALSE;
504 }
505 }
506
507 //
508 // The offer with "PXEClient" is a pxe offer.
509 //
510 Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];
511 EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM);
512
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) {
517 IsPxeOffer = TRUE;
518 }
519
520 //
521 // Determine offer type of the dhcp6 packet.
522 //
523 if (IsPxeOffer) {
524 //
525 // It's a binl offer only with PXEClient.
526 //
527 OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
528 } else {
529 //
530 // It's a dhcp only offer, which is a pure dhcp6 offer packet.
531 //
532 OfferType = PxeOfferTypeDhcpOnly;
533 }
534
535 Cache6->OfferType = OfferType;
536
537 return EFI_SUCCESS;
538 }
539
540
541 /**
542 Cache the DHCPv6 ack packet, and parse it on demand.
543
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.
547
548 **/
549 VOID
PxeBcCopyDhcp6Ack(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP6_PACKET * Ack,IN BOOLEAN Verified)550 PxeBcCopyDhcp6Ack (
551 IN PXEBC_PRIVATE_DATA *Private,
552 IN EFI_DHCP6_PACKET *Ack,
553 IN BOOLEAN Verified
554 )
555 {
556 EFI_PXE_BASE_CODE_MODE *Mode;
557
558 Mode = Private->PxeBc.Mode;
559
560 PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);
561
562 if (Verified) {
563 //
564 // Parse the ack packet and store it into mode data if needed.
565 //
566 PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);
567 CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);
568 Mode->DhcpAckReceived = TRUE;
569 }
570 }
571
572
573 /**
574 Cache the DHCPv6 proxy offer packet according to the received order.
575
576 @param[in] Private The pointer to PxeBc private data.
577 @param[in] OfferIndex The received order of offer packets.
578
579 **/
580 VOID
PxeBcCopyDhcp6Proxy(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 OfferIndex)581 PxeBcCopyDhcp6Proxy (
582 IN PXEBC_PRIVATE_DATA *Private,
583 IN UINT32 OfferIndex
584 )
585 {
586 EFI_PXE_BASE_CODE_MODE *Mode;
587 EFI_DHCP6_PACKET *Offer;
588
589 ASSERT (OfferIndex < Private->OfferNum);
590 ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
591
592 Mode = Private->PxeBc.Mode;
593 Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;
594
595 //
596 // Cache the proxy offer packet and parse it.
597 //
598 PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);
599 PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);
600
601 //
602 // Store this packet into mode data.
603 //
604 CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);
605 Mode->ProxyOfferReceived = TRUE;
606 }
607
608 /**
609 Seek the address of the first byte of the option header.
610
611 @param[in] Buf The pointer to the buffer.
612 @param[in] SeekLen The length to seek.
613 @param[in] OptType The option type.
614
615 @retval NULL If it failed to seek the option.
616 @retval others The position to the option.
617
618 **/
619 UINT8 *
PxeBcDhcp6SeekOption(IN UINT8 * Buf,IN UINT32 SeekLen,IN UINT16 OptType)620 PxeBcDhcp6SeekOption (
621 IN UINT8 *Buf,
622 IN UINT32 SeekLen,
623 IN UINT16 OptType
624 )
625 {
626 UINT8 *Cursor;
627 UINT8 *Option;
628 UINT16 DataLen;
629 UINT16 OpCode;
630
631 Option = NULL;
632 Cursor = Buf;
633
634 while (Cursor < Buf + SeekLen) {
635 OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
636 if (OpCode == HTONS (OptType)) {
637 Option = Cursor;
638 break;
639 }
640 DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
641 Cursor += (DataLen + 4);
642 }
643
644 return Option;
645 }
646
647
648 /**
649 Build and send out the request packet for the bootfile, and parse the reply.
650
651 @param[in] Private The pointer to PxeBc private data.
652 @param[in] Index PxeBc option boot item type.
653
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.
658
659 **/
660 EFI_STATUS
PxeBcRequestBootService(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 Index)661 PxeBcRequestBootService (
662 IN PXEBC_PRIVATE_DATA *Private,
663 IN UINT32 Index
664 )
665 {
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;
670 UINTN DiscoverLen;
671 EFI_DHCP6_PACKET *Request;
672 UINTN RequestLen;
673 EFI_DHCP6_PACKET *Reply;
674 UINT8 *RequestOpt;
675 UINT8 *DiscoverOpt;
676 UINTN ReadSize;
677 UINT16 OpFlags;
678 UINT16 OpCode;
679 UINT16 OpLen;
680 EFI_STATUS Status;
681 EFI_DHCP6_PACKET *ProxyOffer;
682 UINT8 *Option;
683
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;
689 OpFlags = 0;
690
691 if (Request == NULL) {
692 return EFI_DEVICE_ERROR;
693 }
694
695 Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
696 if (Discover == NULL) {
697 return EFI_OUT_OF_RESOURCES;
698 }
699
700 //
701 // Build the request packet by the cached request packet before.
702 //
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;
709
710 //
711 // Find Server ID Option from ProxyOffer.
712 //
713 Option = PxeBcDhcp6SeekOption (
714 ProxyOffer->Dhcp6.Option,
715 ProxyOffer->Length - 4,
716 PXEBC_DHCP6_OPT_SERVER_ID
717 );
718 if (Option == NULL) {
719 return EFI_NOT_FOUND;
720 }
721
722 //
723 // Add Server ID Option.
724 //
725 OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen);
726 CopyMem (DiscoverOpt, Option, OpLen + 4);
727 DiscoverOpt += (OpLen + 4);
728 DiscoverLen += (OpLen + 4);
729
730 while (RequestLen < Request->Length) {
731 OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);
732 OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);
733 if (OpCode != EFI_DHCP6_IA_TYPE_NA &&
734 OpCode != EFI_DHCP6_IA_TYPE_TA &&
735 OpCode != PXEBC_DHCP6_OPT_SERVER_ID
736 ) {
737 //
738 // Copy all the options except IA option and Server ID
739 //
740 CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
741 DiscoverOpt += (OpLen + 4);
742 DiscoverLen += (OpLen + 4);
743 }
744 RequestOpt += (OpLen + 4);
745 RequestLen += (OpLen + 4);
746 }
747
748 //
749 // Update Elapsed option in the package
750 //
751 Option = PxeBcDhcp6SeekOption (
752 Discover->DhcpOptions,
753 (UINT32)(RequestLen - 4),
754 PXEBC_DHCP6_OPT_ELAPSED_TIME
755 );
756 if (Option != NULL) {
757 CalcElapsedTime (Private);
758 WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime));
759 }
760
761 Status = PxeBc->UdpWrite (
762 PxeBc,
763 OpFlags,
764 &Private->ServerIp,
765 &DestPort,
766 NULL,
767 &Private->StationIp,
768 &SrcPort,
769 NULL,
770 NULL,
771 &DiscoverLen,
772 (VOID *) Discover
773 );
774
775 if (EFI_ERROR (Status)) {
776 return Status;
777 }
778
779 //
780 // Cache the right PXE reply packet here, set valid flag later.
781 // Especially for PXE discover packet, store it into mode data here.
782 //
783 Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
784 ReadSize = (UINTN) Reply->Size;
785
786 //
787 // Start Udp6Read instance
788 //
789 Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
790 if (EFI_ERROR (Status)) {
791 return Status;
792 }
793
794 Status = PxeBc->UdpRead (
795 PxeBc,
796 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
797 NULL,
798 &SrcPort,
799 &Private->ServerIp,
800 &DestPort,
801 NULL,
802 NULL,
803 &ReadSize,
804 (VOID *) &Reply->Dhcp6
805 );
806 //
807 // Stop Udp6Read instance
808 //
809 Private->Udp6Read->Configure (Private->Udp6Read, NULL);
810
811 if (EFI_ERROR (Status)) {
812 return Status;
813 }
814
815 //
816 // Update length
817 //
818 Reply->Length = (UINT32) ReadSize;
819
820 return EFI_SUCCESS;
821 }
822
823
824 /**
825 Retry to request bootfile name by the BINL offer.
826
827 @param[in] Private The pointer to PxeBc private data.
828 @param[in] Index The received order of offer packets.
829
830 @retval EFI_SUCCESS Successfully retried a request for the bootfile name.
831 @retval EFI_DEVICE_ERROR Failed to retry the bootfile name.
832
833 **/
834 EFI_STATUS
PxeBcRetryDhcp6Binl(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 Index)835 PxeBcRetryDhcp6Binl (
836 IN PXEBC_PRIVATE_DATA *Private,
837 IN UINT32 Index
838 )
839 {
840 EFI_PXE_BASE_CODE_MODE *Mode;
841 PXEBC_DHCP6_PACKET_CACHE *Offer;
842 PXEBC_DHCP6_PACKET_CACHE *Cache6;
843 EFI_STATUS Status;
844
845 ASSERT (Index < PXEBC_OFFER_MAX_NUM);
846 ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||
847 Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);
848
849 Mode = Private->PxeBc.Mode;
850 Private->IsDoDiscover = FALSE;
851 Offer = &Private->OfferBuffer[Index].Dhcp6;
852 if (Offer->OfferType == PxeOfferTypeDhcpBinl) {
853 //
854 // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.
855 //
856 CopyMem (
857 &Private->ServerIp.v6,
858 &mAllDhcpRelayAndServersAddress,
859 sizeof (EFI_IPv6_ADDRESS)
860 );
861 } else {
862 ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
863 //
864 // Parse out the next server address from the last offer, and store it
865 //
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)
871 );
872 if (EFI_ERROR (Status)) {
873 return Status;
874 }
875 }
876
877 //
878 // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
879 //
880 Status = PxeBcRequestBootService (Private, Index);
881
882 if (EFI_ERROR (Status)) {
883 return Status;
884 }
885
886 Cache6 = &Private->ProxyOffer.Dhcp6;
887 Status = PxeBcParseDhcp6Packet (Cache6);
888 if (EFI_ERROR (Status)) {
889 return Status;
890 }
891
892 if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&
893 Cache6->OfferType != PxeOfferTypeProxyWfm11a &&
894 Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
895 //
896 // This BINL ack doesn't have discovery option set or multicast option set
897 // or bootfile name specified.
898 //
899 return EFI_DEVICE_ERROR;
900 }
901
902 Mode->ProxyOfferReceived = TRUE;
903 CopyMem (
904 &Mode->ProxyOffer.Dhcpv6,
905 &Cache6->Packet.Offer.Dhcp6,
906 Cache6->Packet.Offer.Length
907 );
908
909 return EFI_SUCCESS;
910 }
911
912
913 /**
914 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
915
916 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
917 @param[in] RcvdOffer The pointer to the received offer packet.
918
919 **/
920 VOID
PxeBcCacheDhcp6Offer(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP6_PACKET * RcvdOffer)921 PxeBcCacheDhcp6Offer (
922 IN PXEBC_PRIVATE_DATA *Private,
923 IN EFI_DHCP6_PACKET *RcvdOffer
924 )
925 {
926 PXEBC_DHCP6_PACKET_CACHE *Cache6;
927 EFI_DHCP6_PACKET *Offer;
928 PXEBC_OFFER_TYPE OfferType;
929
930 Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
931 Offer = &Cache6->Packet.Offer;
932
933 //
934 // Cache the content of DHCPv6 packet firstly.
935 //
936 PxeBcCacheDhcp6Packet (Offer, RcvdOffer);
937
938 //
939 // Validate the DHCPv6 packet, and parse the options and offer type.
940 //
941 if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {
942 return ;
943 }
944
945 //
946 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
947 //
948 OfferType = Cache6->OfferType;
949 ASSERT (OfferType < PxeOfferTypeMax);
950 ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
951
952 if (IS_PROXY_OFFER (OfferType)) {
953 //
954 // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
955 //
956 Private->IsProxyRecved = TRUE;
957
958 if (OfferType == PxeOfferTypeProxyBinl) {
959 //
960 // Cache all proxy BINL offers.
961 //
962 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
963 Private->OfferCount[OfferType]++;
964 } else if (Private->OfferCount[OfferType] > 0) {
965 //
966 // Only cache the first PXE10/WFM11a offer, and discard the others.
967 //
968 Private->OfferIndex[OfferType][0] = Private->OfferNum;
969 Private->OfferCount[OfferType] = 1;
970 } else {
971 return;
972 }
973 } else {
974 //
975 // It's a DHCPv6 offer with yiaddr, and cache them all.
976 //
977 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
978 Private->OfferCount[OfferType]++;
979 }
980
981 Private->OfferNum++;
982 }
983
984
985 /**
986 Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
987
988 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
989
990 **/
991 VOID
PxeBcSelectDhcp6Offer(IN PXEBC_PRIVATE_DATA * Private)992 PxeBcSelectDhcp6Offer (
993 IN PXEBC_PRIVATE_DATA *Private
994 )
995 {
996 UINT32 Index;
997 UINT32 OfferIndex;
998 PXEBC_OFFER_TYPE OfferType;
999
1000 Private->SelectIndex = 0;
1001
1002 if (Private->IsOfferSorted) {
1003 //
1004 // Select offer by default policy.
1005 //
1006 if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
1007 //
1008 // 1. DhcpPxe10 offer
1009 //
1010 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
1011
1012 } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
1013 //
1014 // 2. DhcpWfm11a offer
1015 //
1016 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
1017
1018 } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
1019 Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {
1020 //
1021 // 3. DhcpOnly offer and ProxyPxe10 offer.
1022 //
1023 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1024 Private->SelectProxyType = PxeOfferTypeProxyPxe10;
1025
1026 } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
1027 Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {
1028 //
1029 // 4. DhcpOnly offer and ProxyWfm11a offer.
1030 //
1031 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1032 Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
1033
1034 } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
1035 //
1036 // 5. DhcpBinl offer.
1037 //
1038 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
1039
1040 } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
1041 Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {
1042 //
1043 // 6. DhcpOnly offer and ProxyBinl offer.
1044 //
1045 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
1046 Private->SelectProxyType = PxeOfferTypeProxyBinl;
1047
1048 } else {
1049 //
1050 // 7. DhcpOnly offer with bootfilename.
1051 //
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;
1056 break;
1057 }
1058 }
1059 }
1060 } else {
1061 //
1062 // Select offer by received order.
1063 //
1064 for (Index = 0; Index < Private->OfferNum; Index++) {
1065
1066 OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
1067
1068 if (IS_PROXY_OFFER (OfferType)) {
1069 //
1070 // Skip proxy offers
1071 //
1072 continue;
1073 }
1074
1075 if (!Private->IsProxyRecved &&
1076 OfferType == PxeOfferTypeDhcpOnly &&
1077 Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
1078 //
1079 // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
1080 //
1081 continue;
1082 }
1083
1084 Private->SelectIndex = Index + 1;
1085 break;
1086 }
1087 }
1088 }
1089
1090
1091 /**
1092 Handle the DHCPv6 offer packet.
1093
1094 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1095
1096 @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully.
1097 @retval EFI_NO_RESPONSE No response to the following request packet.
1098
1099 **/
1100 EFI_STATUS
PxeBcHandleDhcp6Offer(IN PXEBC_PRIVATE_DATA * Private)1101 PxeBcHandleDhcp6Offer (
1102 IN PXEBC_PRIVATE_DATA *Private
1103 )
1104 {
1105 PXEBC_DHCP6_PACKET_CACHE *Cache6;
1106 EFI_STATUS Status;
1107 PXEBC_OFFER_TYPE OfferType;
1108 UINT32 ProxyIndex;
1109 UINT32 SelectIndex;
1110 UINT32 Index;
1111
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;
1117
1118 if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {
1119 //
1120 // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
1121 //
1122 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {
1123 Status = EFI_NO_RESPONSE;
1124 }
1125 } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {
1126
1127 if (Private->IsProxyRecved) {
1128 //
1129 // DhcpOnly offer is selected, so need try to request bootfilename.
1130 //
1131 ProxyIndex = 0;
1132 if (Private->IsOfferSorted) {
1133 //
1134 // The proxy offer should be determined if select by default policy.
1135 // IsOfferSorted means all offers are labeled by OfferIndex.
1136 //
1137 ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
1138
1139 if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
1140 //
1141 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1142 //
1143 for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
1144
1145 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
1146 if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {
1147 break;
1148 }
1149 }
1150 if (Index == Private->OfferCount[Private->SelectProxyType]) {
1151 Status = EFI_NO_RESPONSE;
1152 }
1153 } else {
1154 //
1155 // For other proxy offers (pxe10 or wfm11a), only one is buffered.
1156 //
1157 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
1158 }
1159 } else {
1160 //
1161 // The proxy offer should not be determined if select by received order.
1162 //
1163 Status = EFI_NO_RESPONSE;
1164
1165 for (Index = 0; Index < Private->OfferNum; Index++) {
1166
1167 OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
1168
1169 if (!IS_PROXY_OFFER (OfferType)) {
1170 //
1171 // Skip non proxy dhcp offers.
1172 //
1173 continue;
1174 }
1175
1176 if (OfferType == PxeOfferTypeProxyBinl) {
1177 //
1178 // Try all the cached ProxyBinl offer one by one to request bootfilename.
1179 //
1180 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {
1181 continue;
1182 }
1183 }
1184
1185 Private->SelectProxyType = OfferType;
1186 ProxyIndex = Index;
1187 Status = EFI_SUCCESS;
1188 break;
1189 }
1190 }
1191
1192 if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {
1193 //
1194 // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
1195 //
1196 PxeBcCopyDhcp6Proxy (Private, ProxyIndex);
1197 }
1198 } else {
1199 //
1200 // Othewise, the bootfilename must be included in DhcpOnly offer.
1201 //
1202 ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
1203 }
1204 }
1205
1206 if (!EFI_ERROR (Status)) {
1207 //
1208 // All PXE boot information is ready by now.
1209 //
1210 PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);
1211 Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;
1212 }
1213
1214 return Status;
1215 }
1216
1217
1218 /**
1219 Unregister the address by Ip6Config protocol.
1220
1221 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1222
1223 **/
1224 VOID
PxeBcUnregisterIp6Address(IN PXEBC_PRIVATE_DATA * Private)1225 PxeBcUnregisterIp6Address (
1226 IN PXEBC_PRIVATE_DATA *Private
1227 )
1228 {
1229 if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {
1230 //
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.
1233 //
1234 }
1235 }
1236
1237 /**
1238 Check whether IP driver could route the message which will be sent to ServerIp address.
1239
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.
1242
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.
1246
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.
1250
1251 **/
1252 EFI_STATUS
PxeBcCheckRouteTable(IN PXEBC_PRIVATE_DATA * Private,IN UINTN TimeOutInSecond,OUT EFI_IPv6_ADDRESS * GatewayAddr)1253 PxeBcCheckRouteTable (
1254 IN PXEBC_PRIVATE_DATA *Private,
1255 IN UINTN TimeOutInSecond,
1256 OUT EFI_IPv6_ADDRESS *GatewayAddr
1257 )
1258 {
1259 EFI_STATUS Status;
1260 EFI_IP6_PROTOCOL *Ip6;
1261 EFI_IP6_MODE_DATA Ip6ModeData;
1262 UINTN Index;
1263 EFI_EVENT TimeOutEvt;
1264 UINTN RetryCount;
1265 BOOLEAN GatewayIsFound;
1266
1267 ASSERT (GatewayAddr != NULL);
1268 ASSERT (Private != NULL);
1269
1270 Ip6 = Private->Ip6;
1271 GatewayIsFound = FALSE;
1272 RetryCount = 0;
1273 TimeOutEvt = NULL;
1274 ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));
1275
1276 while (TRUE) {
1277 Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);
1278 if (EFI_ERROR (Status)) {
1279 goto ON_EXIT;
1280 }
1281
1282 //
1283 // Find out the gateway address which can route the message which send to ServerIp.
1284 //
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;
1289 break;
1290 }
1291 }
1292
1293 if (Ip6ModeData.AddressList != NULL) {
1294 FreePool (Ip6ModeData.AddressList);
1295 }
1296 if (Ip6ModeData.GroupTable != NULL) {
1297 FreePool (Ip6ModeData.GroupTable);
1298 }
1299 if (Ip6ModeData.RouteTable != NULL) {
1300 FreePool (Ip6ModeData.RouteTable);
1301 }
1302 if (Ip6ModeData.NeighborCache != NULL) {
1303 FreePool (Ip6ModeData.NeighborCache);
1304 }
1305 if (Ip6ModeData.PrefixTable != NULL) {
1306 FreePool (Ip6ModeData.PrefixTable);
1307 }
1308 if (Ip6ModeData.IcmpTypeList != NULL) {
1309 FreePool (Ip6ModeData.IcmpTypeList);
1310 }
1311
1312 if (GatewayIsFound || RetryCount == TimeOutInSecond) {
1313 break;
1314 }
1315
1316 RetryCount++;
1317
1318 //
1319 // Delay 1 second then recheck it again.
1320 //
1321 if (TimeOutEvt == NULL) {
1322 Status = gBS->CreateEvent (
1323 EVT_TIMER,
1324 TPL_CALLBACK,
1325 NULL,
1326 NULL,
1327 &TimeOutEvt
1328 );
1329 if (EFI_ERROR (Status)) {
1330 goto ON_EXIT;
1331 }
1332 }
1333
1334 Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);
1335 if (EFI_ERROR (Status)) {
1336 goto ON_EXIT;
1337 }
1338 while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
1339 Ip6->Poll (Ip6);
1340 }
1341 }
1342
1343 ON_EXIT:
1344 if (TimeOutEvt != NULL) {
1345 gBS->CloseEvent (TimeOutEvt);
1346 }
1347
1348 if (GatewayIsFound) {
1349 Status = EFI_SUCCESS;
1350 } else if (RetryCount == TimeOutInSecond) {
1351 Status = EFI_TIMEOUT;
1352 }
1353
1354 return Status;
1355 }
1356
1357 /**
1358 Register the ready station address and gateway by Ip6Config protocol.
1359
1360 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1361 @param[in] Address The pointer to the ready address.
1362
1363 @retval EFI_SUCCESS Registered the address succesfully.
1364 @retval Others Failed to register the address.
1365
1366 **/
1367 EFI_STATUS
PxeBcRegisterIp6Address(IN PXEBC_PRIVATE_DATA * Private,IN EFI_IPv6_ADDRESS * Address)1368 PxeBcRegisterIp6Address (
1369 IN PXEBC_PRIVATE_DATA *Private,
1370 IN EFI_IPv6_ADDRESS *Address
1371 )
1372 {
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;
1378 UINTN DataSize;
1379 EFI_EVENT MappedEvt;
1380 EFI_STATUS Status;
1381 BOOLEAN NoGateway;
1382 EFI_IPv6_ADDRESS *Ip6Addr;
1383 UINTN Index;
1384
1385 Status = EFI_SUCCESS;
1386 MappedEvt = NULL;
1387 Ip6Addr = NULL;
1388 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
1389 Ip6Cfg = Private->Ip6Cfg;
1390 Ip6 = Private->Ip6;
1391 NoGateway = FALSE;
1392
1393 ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
1394 CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));
1395
1396 Status = Ip6->Configure (Ip6, &Private->Ip6CfgData);
1397 if (EFI_ERROR (Status)) {
1398 goto ON_EXIT;
1399 }
1400
1401 //
1402 // Retrieve the gateway address from IP6 route table.
1403 //
1404 Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);
1405 if (EFI_ERROR (Status)) {
1406 NoGateway = TRUE;
1407 }
1408
1409 //
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.
1412 //
1413 Policy = Ip6ConfigPolicyManual;
1414 Status = Ip6Cfg->SetData (
1415 Ip6Cfg,
1416 Ip6ConfigDataTypePolicy,
1417 sizeof(EFI_IP6_CONFIG_POLICY),
1418 &Policy
1419 );
1420 if (EFI_ERROR (Status)) {
1421 //
1422 // There is no need to recover later.
1423 //
1424 Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
1425 goto ON_EXIT;
1426 }
1427
1428 //
1429 // Create a notify event to set address flag when DAD if IP6 driver succeeded.
1430 //
1431 Status = gBS->CreateEvent (
1432 EVT_NOTIFY_SIGNAL,
1433 TPL_NOTIFY,
1434 PxeBcCommonNotify,
1435 &Private->IsAddressOk,
1436 &MappedEvt
1437 );
1438 if (EFI_ERROR (Status)) {
1439 goto ON_EXIT;
1440 }
1441
1442 Private->IsAddressOk = FALSE;
1443 Status = Ip6Cfg->RegisterDataNotify (
1444 Ip6Cfg,
1445 Ip6ConfigDataTypeManualAddress,
1446 MappedEvt
1447 );
1448 if (EFI_ERROR(Status)) {
1449 goto ON_EXIT;
1450 }
1451
1452 Status = Ip6Cfg->SetData (
1453 Ip6Cfg,
1454 Ip6ConfigDataTypeManualAddress,
1455 sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),
1456 &CfgAddr
1457 );
1458 if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {
1459 goto ON_EXIT;
1460 } else if (Status == EFI_NOT_READY) {
1461 //
1462 // Poll the network until the asynchronous process is finished.
1463 //
1464 while (!Private->IsAddressOk) {
1465 Ip6->Poll (Ip6);
1466 }
1467 //
1468 // Check whether the IP6 address setting is successed.
1469 //
1470 DataSize = 0;
1471 Status = Ip6Cfg->GetData (
1472 Ip6Cfg,
1473 Ip6ConfigDataTypeManualAddress,
1474 &DataSize,
1475 NULL
1476 );
1477 if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {
1478 Status = EFI_DEVICE_ERROR;
1479 goto ON_EXIT;
1480 }
1481
1482 Ip6Addr = AllocatePool (DataSize);
1483 if (Ip6Addr == NULL) {
1484 return EFI_OUT_OF_RESOURCES;
1485 }
1486 Status = Ip6Cfg->GetData (
1487 Ip6Cfg,
1488 Ip6ConfigDataTypeManualAddress,
1489 &DataSize,
1490 (VOID*) Ip6Addr
1491 );
1492 if (EFI_ERROR (Status)) {
1493 Status = EFI_DEVICE_ERROR;
1494 goto ON_EXIT;
1495 }
1496
1497 for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) {
1498 if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) {
1499 break;
1500 }
1501 }
1502 if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {
1503 Status = EFI_ABORTED;
1504 goto ON_EXIT;
1505 }
1506 }
1507
1508 //
1509 // Set the default gateway address back if needed.
1510 //
1511 if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) {
1512 Status = Ip6Cfg->SetData (
1513 Ip6Cfg,
1514 Ip6ConfigDataTypeGateway,
1515 sizeof (EFI_IPv6_ADDRESS),
1516 &GatewayAddr
1517 );
1518 if (EFI_ERROR (Status)) {
1519 goto ON_EXIT;
1520 }
1521 }
1522
1523 ON_EXIT:
1524 if (MappedEvt != NULL) {
1525 Ip6Cfg->UnregisterDataNotify (
1526 Ip6Cfg,
1527 Ip6ConfigDataTypeManualAddress,
1528 MappedEvt
1529 );
1530 gBS->CloseEvent (MappedEvt);
1531 }
1532 if (Ip6Addr != NULL) {
1533 FreePool (Ip6Addr);
1534 }
1535 return Status;
1536 }
1537
1538 /**
1539 Set the IP6 policy to Automatic.
1540
1541 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1542
1543 @retval EFI_SUCCESS Switch the IP policy succesfully.
1544 @retval Others Unexpect error happened.
1545
1546 **/
1547 EFI_STATUS
PxeBcSetIp6Policy(IN PXEBC_PRIVATE_DATA * Private)1548 PxeBcSetIp6Policy (
1549 IN PXEBC_PRIVATE_DATA *Private
1550 )
1551 {
1552 EFI_IP6_CONFIG_POLICY Policy;
1553 EFI_STATUS Status;
1554 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
1555 UINTN DataSize;
1556
1557 Ip6Cfg = Private->Ip6Cfg;
1558 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
1559
1560 //
1561 // Get and store the current policy of IP6 driver.
1562 //
1563 Status = Ip6Cfg->GetData (
1564 Ip6Cfg,
1565 Ip6ConfigDataTypePolicy,
1566 &DataSize,
1567 &Private->Ip6Policy
1568 );
1569 if (EFI_ERROR (Status)) {
1570 return Status;
1571 }
1572
1573 if (Private->Ip6Policy == Ip6ConfigPolicyManual) {
1574 Policy = Ip6ConfigPolicyAutomatic;
1575 Status = Ip6Cfg->SetData (
1576 Ip6Cfg,
1577 Ip6ConfigDataTypePolicy,
1578 sizeof(EFI_IP6_CONFIG_POLICY),
1579 &Policy
1580 );
1581 if (EFI_ERROR (Status)) {
1582 //
1583 // There is no need to recover later.
1584 //
1585 Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
1586 }
1587 }
1588
1589 return Status;
1590 }
1591
1592 /**
1593 This function will register the station IP address and flush IP instance to start using the new IP address.
1594
1595 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.
1596
1597 @retval EFI_SUCCESS The new IP address has been configured successfully.
1598 @retval Others Failed to configure the address.
1599
1600 **/
1601 EFI_STATUS
PxeBcSetIp6Address(IN PXEBC_PRIVATE_DATA * Private)1602 PxeBcSetIp6Address (
1603 IN PXEBC_PRIVATE_DATA *Private
1604 )
1605 {
1606 EFI_STATUS Status;
1607 EFI_DHCP6_PROTOCOL *Dhcp6;
1608
1609 Dhcp6 = Private->Dhcp6;
1610
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));
1613
1614 Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);
1615 if (EFI_ERROR (Status)) {
1616 Dhcp6->Stop (Dhcp6);
1617 return Status;
1618 }
1619
1620 Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL);
1621 if (EFI_ERROR (Status)) {
1622 PxeBcUnregisterIp6Address (Private);
1623 Dhcp6->Stop (Dhcp6);
1624 return Status;
1625 }
1626
1627 AsciiPrint ("\n Station IP address is ");
1628 PxeBcShowIp6Addr (&Private->StationIp.v6);
1629
1630 return EFI_SUCCESS;
1631 }
1632
1633 /**
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.
1636
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
1641 state transition.
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.
1644
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.
1649
1650 **/
1651 EFI_STATUS
1652 EFIAPI
PxeBcDhcp6CallBack(IN EFI_DHCP6_PROTOCOL * This,IN VOID * Context,IN EFI_DHCP6_STATE CurrentState,IN EFI_DHCP6_EVENT Dhcp6Event,IN EFI_DHCP6_PACKET * Packet,OUT EFI_DHCP6_PACKET ** NewPacket OPTIONAL)1653 PxeBcDhcp6CallBack (
1654 IN EFI_DHCP6_PROTOCOL *This,
1655 IN VOID *Context,
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
1660 )
1661 {
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;
1666 EFI_STATUS Status;
1667 BOOLEAN Received;
1668
1669 if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&
1670 (Dhcp6Event != Dhcp6SelectAdvertise) &&
1671 (Dhcp6Event != Dhcp6SendSolicit) &&
1672 (Dhcp6Event != Dhcp6SendRequest) &&
1673 (Dhcp6Event != Dhcp6RcvdReply)) {
1674 return EFI_SUCCESS;
1675 }
1676
1677 ASSERT (Packet != NULL);
1678
1679 Private = (PXEBC_PRIVATE_DATA *) Context;
1680 Mode = Private->PxeBc.Mode;
1681 Callback = Private->PxeBcCallback;
1682
1683 //
1684 // Callback to user when any traffic ocurred if has.
1685 //
1686 if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {
1687 Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);
1688 Status = Callback->Callback (
1689 Callback,
1690 Private->Function,
1691 Received,
1692 Packet->Length,
1693 (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6
1694 );
1695 if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
1696 return EFI_ABORTED;
1697 }
1698 }
1699
1700 Status = EFI_SUCCESS;
1701
1702 switch (Dhcp6Event) {
1703
1704 case Dhcp6SendSolicit:
1705 //
1706 // Record the first Solicate msg time
1707 //
1708 if (Private->SolicitTimes == 0) {
1709 CalcElapsedTime (Private);
1710 Private->SolicitTimes++;
1711 }
1712 //
1713 // Cache the dhcp discover packet to mode data directly.
1714 //
1715 CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);
1716 break;
1717
1718 case Dhcp6RcvdAdvertise:
1719 Status = EFI_NOT_READY;
1720 if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
1721 //
1722 // Cache the dhcp offers to OfferBuffer[] for select later, and record
1723 // the OfferIndex and OfferCount.
1724 //
1725 PxeBcCacheDhcp6Offer (Private, Packet);
1726 }
1727 break;
1728
1729 case Dhcp6SendRequest:
1730 //
1731 // Store the request packet as seed packet for discover.
1732 //
1733 if (Private->Dhcp6Request != NULL) {
1734 FreePool (Private->Dhcp6Request);
1735 }
1736 Private->Dhcp6Request = AllocateZeroPool (Packet->Size);
1737 if (Private->Dhcp6Request != NULL) {
1738 CopyMem (Private->Dhcp6Request, Packet, Packet->Size);
1739 }
1740 break;
1741
1742 case Dhcp6SelectAdvertise:
1743 //
1744 // Select offer by the default policy or by order, and record the SelectIndex
1745 // and SelectProxyType.
1746 //
1747 PxeBcSelectDhcp6Offer (Private);
1748
1749 if (Private->SelectIndex == 0) {
1750 Status = EFI_ABORTED;
1751 } else {
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);
1757 }
1758 break;
1759
1760 case Dhcp6RcvdReply:
1761 //
1762 // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
1763 // without verification.
1764 //
1765 ASSERT (Private->SelectIndex != 0);
1766 PxeBcCopyDhcp6Ack (Private, Packet, FALSE);
1767 break;
1768
1769 default:
1770 ASSERT (0);
1771 }
1772
1773 return Status;
1774 }
1775
1776
1777 /**
1778 Build and send out the request packet for the bootfile, and parse the reply.
1779
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.
1785
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.
1790
1791 **/
1792 EFI_STATUS
PxeBcDhcp6Discover(IN PXEBC_PRIVATE_DATA * Private,IN UINT16 Type,IN UINT16 * Layer,IN BOOLEAN UseBis,IN EFI_IP_ADDRESS * DestIp)1793 PxeBcDhcp6Discover (
1794 IN PXEBC_PRIVATE_DATA *Private,
1795 IN UINT16 Type,
1796 IN UINT16 *Layer,
1797 IN BOOLEAN UseBis,
1798 IN EFI_IP_ADDRESS *DestIp
1799 )
1800 {
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;
1806 UINTN DiscoverLen;
1807 EFI_DHCP6_PACKET *Request;
1808 UINTN RequestLen;
1809 EFI_DHCP6_PACKET *Reply;
1810 UINT8 *RequestOpt;
1811 UINT8 *DiscoverOpt;
1812 UINTN ReadSize;
1813 UINT16 OpCode;
1814 UINT16 OpLen;
1815 UINT32 Xid;
1816 EFI_STATUS Status;
1817
1818 PxeBc = &Private->PxeBc;
1819 Mode = PxeBc->Mode;
1820 Request = Private->Dhcp6Request;
1821 SrcPort = PXEBC_BS_DISCOVER_PORT;
1822 DestPort = PXEBC_BS_DISCOVER_PORT;
1823
1824 if (!UseBis && Layer != NULL) {
1825 *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
1826 }
1827
1828 if (Request == NULL) {
1829 return EFI_DEVICE_ERROR;
1830 }
1831
1832 Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
1833 if (Discover == NULL) {
1834 return EFI_OUT_OF_RESOURCES;
1835 }
1836
1837 //
1838 // Build the discover packet by the cached request packet before.
1839 //
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;
1847
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) {
1853 //
1854 // Copy all the options except IA option.
1855 //
1856 CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
1857 DiscoverOpt += (OpLen + 4);
1858 DiscoverLen += (OpLen + 4);
1859 }
1860 RequestOpt += (OpLen + 4);
1861 RequestLen += (OpLen + 4);
1862 }
1863
1864 Status = PxeBc->UdpWrite (
1865 PxeBc,
1866 0,
1867 &Private->ServerIp,
1868 &DestPort,
1869 NULL,
1870 &Private->StationIp,
1871 &SrcPort,
1872 NULL,
1873 NULL,
1874 &DiscoverLen,
1875 (VOID *) Discover
1876 );
1877 if (EFI_ERROR (Status)) {
1878 return Status;
1879 }
1880
1881 //
1882 // Cache the right PXE reply packet here, set valid flag later.
1883 // Especially for PXE discover packet, store it into mode data here.
1884 //
1885 if (Private->IsDoDiscover) {
1886 CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);
1887 Reply = &Private->PxeReply.Dhcp6.Packet.Ack;
1888 } else {
1889 Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
1890 }
1891 ReadSize = (UINTN) Reply->Size;
1892
1893 //
1894 // Start Udp6Read instance
1895 //
1896 Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
1897 if (EFI_ERROR (Status)) {
1898 return Status;
1899 }
1900
1901 Status = PxeBc->UdpRead (
1902 PxeBc,
1903 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
1904 NULL,
1905 &SrcPort,
1906 &Private->ServerIp,
1907 &DestPort,
1908 NULL,
1909 NULL,
1910 &ReadSize,
1911 (VOID *) &Reply->Dhcp6
1912 );
1913 //
1914 // Stop Udp6Read instance
1915 //
1916 Private->Udp6Read->Configure (Private->Udp6Read, NULL);
1917 if (EFI_ERROR (Status)) {
1918 return Status;
1919 }
1920
1921 return EFI_SUCCESS;
1922 }
1923
1924
1925 /**
1926 Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
1927
1928 @param[in] Private The pointer to PxeBc private data.
1929 @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL
1930
1931 @retval EFI_SUCCESS The S.A.R.R. process successfully finished.
1932 @retval Others Failed to finish the S.A.R.R. process.
1933
1934 **/
1935 EFI_STATUS
PxeBcDhcp6Sarr(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP6_PROTOCOL * Dhcp6)1936 PxeBcDhcp6Sarr (
1937 IN PXEBC_PRIVATE_DATA *Private,
1938 IN EFI_DHCP6_PROTOCOL *Dhcp6
1939 )
1940 {
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];
1947 UINT32 OptCount;
1948 EFI_STATUS Status;
1949 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
1950 EFI_STATUS TimerStatus;
1951 EFI_EVENT Timer;
1952 UINT64 GetMappingTimeOut;
1953 UINTN DataSize;
1954 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;
1955
1956 Status = EFI_SUCCESS;
1957 PxeMode = Private->PxeBc.Mode;
1958 Ip6Cfg = Private->Ip6Cfg;
1959 Timer = NULL;
1960
1961 //
1962 // Build option list for the request packet.
1963 //
1964 OptCount = PxeBcBuildDhcp6Options (Private, OptList, Buffer);
1965 ASSERT (OptCount> 0);
1966
1967 Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
1968 if (Retransmit == NULL) {
1969 return EFI_OUT_OF_RESOURCES;
1970 }
1971
1972 ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));
1973 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
1974
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;
1989
1990 //
1991 // Configure the DHCPv6 instance for PXE boot.
1992 //
1993 Status = Dhcp6->Configure (Dhcp6, &Config);
1994 FreePool (Retransmit);
1995 if (EFI_ERROR (Status)) {
1996 return Status;
1997 }
1998
1999 //
2000 // Initialize the record fields for DHCPv6 offer in private data.
2001 //
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));
2007
2008
2009 //
2010 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
2011 //
2012 Status = Dhcp6->Start (Dhcp6);
2013 if (Status == EFI_NO_MAPPING) {
2014 //
2015 // IP6 Linklocal address is not available for use, so stop current Dhcp process
2016 // and wait for duplicate address detection to finish.
2017 //
2018 Dhcp6->Stop (Dhcp6);
2019
2020 //
2021 // Get Duplicate Address Detection Transmits count.
2022 //
2023 DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
2024 Status = Ip6Cfg->GetData (
2025 Ip6Cfg,
2026 Ip6ConfigDataTypeDupAddrDetectTransmits,
2027 &DataSize,
2028 &DadXmits
2029 );
2030 if (EFI_ERROR (Status)) {
2031 Dhcp6->Configure (Dhcp6, NULL);
2032 return Status;
2033 }
2034
2035 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
2036 if (EFI_ERROR (Status)) {
2037 Dhcp6->Configure (Dhcp6, NULL);
2038 return Status;
2039 }
2040
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);
2046 return Status;
2047 }
2048
2049 do {
2050
2051 TimerStatus = gBS->CheckEvent (Timer);
2052 if (!EFI_ERROR (TimerStatus)) {
2053 Status = Dhcp6->Start (Dhcp6);
2054 }
2055 } while (TimerStatus == EFI_NOT_READY);
2056
2057 gBS->CloseEvent (Timer);
2058 }
2059 if (EFI_ERROR (Status)) {
2060 if (Status == EFI_ICMP_ERROR) {
2061 PxeMode->IcmpErrorReceived = TRUE;
2062 }
2063 Dhcp6->Configure (Dhcp6, NULL);
2064 return Status;
2065 }
2066
2067 //
2068 // Get the acquired IPv6 address and store them.
2069 //
2070 Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);
2071 if (EFI_ERROR (Status)) {
2072 Dhcp6->Stop (Dhcp6);
2073 return Status;
2074 }
2075
2076 ASSERT (Mode.Ia->State == Dhcp6Bound);
2077 //
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.
2083 //
2084 CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));
2085
2086 //
2087 // Check the selected offer whether BINL retry is needed.
2088 //
2089 Status = PxeBcHandleDhcp6Offer (Private);
2090 if (EFI_ERROR (Status)) {
2091 Dhcp6->Stop (Dhcp6);
2092 return Status;
2093 }
2094
2095 return EFI_SUCCESS;
2096 }
2097