]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/HttpBootDxe/HttpBootDhcp6.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootDhcp6.c
1 /** @file
2 Functions implementation related with DHCPv6 for HTTP boot driver.
3
4 Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "HttpBootDxe.h"
10
11 /**
12 Build the options buffer for the DHCPv6 request packet.
13
14 @param[in] Private The pointer to HTTP BOOT driver private data.
15 @param[out] OptList The pointer to the option pointer array.
16 @param[in] Buffer The pointer to the buffer to contain the option list.
17
18 @return Index The count of the built-in options.
19
20 **/
21 UINT32
22 HttpBootBuildDhcp6Options (
23 IN HTTP_BOOT_PRIVATE_DATA *Private,
24 OUT EFI_DHCP6_PACKET_OPTION **OptList,
25 IN UINT8 *Buffer
26 )
27 {
28 HTTP_BOOT_DHCP6_OPTION_ENTRY OptEnt;
29 UINT16 Value;
30 UINT32 Index;
31
32 Index = 0;
33 OptList[0] = (EFI_DHCP6_PACKET_OPTION *)Buffer;
34
35 //
36 // Append client option request option
37 //
38 OptList[Index]->OpCode = HTONS (DHCP6_OPT_ORO);
39 OptList[Index]->OpLen = HTONS (8);
40 OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *)OptList[Index]->Data;
41 OptEnt.Oro->OpCode[0] = HTONS (DHCP6_OPT_BOOT_FILE_URL);
42 OptEnt.Oro->OpCode[1] = HTONS (DHCP6_OPT_BOOT_FILE_PARAM);
43 OptEnt.Oro->OpCode[2] = HTONS (DHCP6_OPT_DNS_SERVERS);
44 OptEnt.Oro->OpCode[3] = HTONS (DHCP6_OPT_VENDOR_CLASS);
45 Index++;
46 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
47
48 //
49 // Append client network device interface option
50 //
51 OptList[Index]->OpCode = HTONS (DHCP6_OPT_UNDI);
52 OptList[Index]->OpLen = HTONS ((UINT16)3);
53 OptEnt.Undi = (HTTP_BOOT_DHCP6_OPTION_UNDI *)OptList[Index]->Data;
54
55 if (Private->Nii != NULL) {
56 OptEnt.Undi->Type = Private->Nii->Type;
57 OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
58 OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
59 } else {
60 OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
61 OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
62 OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
63 }
64
65 Index++;
66 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
67
68 //
69 // Append client system architecture option
70 //
71 OptList[Index]->OpCode = HTONS (DHCP6_OPT_ARCH);
72 OptList[Index]->OpLen = HTONS ((UINT16)sizeof (HTTP_BOOT_DHCP6_OPTION_ARCH));
73 OptEnt.Arch = (HTTP_BOOT_DHCP6_OPTION_ARCH *)OptList[Index]->Data;
74 Value = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE);
75 CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
76 Index++;
77 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
78
79 //
80 // Append vendor class identify option.
81 //
82 OptList[Index]->OpCode = HTONS (DHCP6_OPT_VENDOR_CLASS);
83 OptList[Index]->OpLen = HTONS ((UINT16)sizeof (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS));
84 OptEnt.VendorClass = (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *)OptList[Index]->Data;
85 OptEnt.VendorClass->Vendor = HTONL (HTTP_BOOT_DHCP6_ENTERPRISE_NUM);
86 OptEnt.VendorClass->ClassLen = HTONS ((UINT16)sizeof (HTTP_BOOT_CLASS_ID));
87 CopyMem (
88 &OptEnt.VendorClass->ClassId,
89 DEFAULT_CLASS_ID_DATA,
90 sizeof (HTTP_BOOT_CLASS_ID)
91 );
92 HttpBootUintnToAscDecWithFormat (
93 EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE,
94 OptEnt.VendorClass->ClassId.ArchitectureType,
95 sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)
96 );
97
98 if (Private->Nii != NULL) {
99 CopyMem (
100 OptEnt.VendorClass->ClassId.InterfaceName,
101 Private->Nii->StringId,
102 sizeof (OptEnt.VendorClass->ClassId.InterfaceName)
103 );
104 HttpBootUintnToAscDecWithFormat (
105 Private->Nii->MajorVer,
106 OptEnt.VendorClass->ClassId.UndiMajor,
107 sizeof (OptEnt.VendorClass->ClassId.UndiMajor)
108 );
109 HttpBootUintnToAscDecWithFormat (
110 Private->Nii->MinorVer,
111 OptEnt.VendorClass->ClassId.UndiMinor,
112 sizeof (OptEnt.VendorClass->ClassId.UndiMinor)
113 );
114 }
115
116 Index++;
117
118 return Index;
119 }
120
121 /**
122 Parse out a DHCPv6 option by OptTag, and find the position in buffer.
123
124 @param[in] Buffer The pointer to the option buffer.
125 @param[in] Length Length of the option buffer.
126 @param[in] OptTag The required option tag.
127
128 @retval NULL Failed to parse the required option.
129 @retval Others The position of the required option in buffer.
130
131 **/
132 EFI_DHCP6_PACKET_OPTION *
133 HttpBootParseDhcp6Options (
134 IN UINT8 *Buffer,
135 IN UINT32 Length,
136 IN UINT16 OptTag
137 )
138 {
139 EFI_DHCP6_PACKET_OPTION *Option;
140 UINT32 Offset;
141
142 Option = (EFI_DHCP6_PACKET_OPTION *)Buffer;
143 Offset = 0;
144
145 //
146 // OpLen and OpCode here are both stored in network order.
147 //
148 while (Offset < Length) {
149 if (NTOHS (Option->OpCode) == OptTag) {
150 return Option;
151 }
152
153 Offset += (NTOHS (Option->OpLen) + 4);
154 Option = (EFI_DHCP6_PACKET_OPTION *)(Buffer + Offset);
155 }
156
157 return NULL;
158 }
159
160 /**
161 Parse the cached DHCPv6 packet, including all the options.
162
163 @param[in] Cache6 The pointer to a cached DHCPv6 packet.
164
165 @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
166 @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.
167
168 **/
169 EFI_STATUS
170 HttpBootParseDhcp6Packet (
171 IN HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6
172 )
173 {
174 EFI_DHCP6_PACKET *Offer;
175 EFI_DHCP6_PACKET_OPTION **Options;
176 EFI_DHCP6_PACKET_OPTION *Option;
177 HTTP_BOOT_OFFER_TYPE OfferType;
178 EFI_IPv6_ADDRESS IpAddr;
179 BOOLEAN IsProxyOffer;
180 BOOLEAN IsHttpOffer;
181 BOOLEAN IsDnsOffer;
182 BOOLEAN IpExpressedUri;
183 EFI_STATUS Status;
184 UINT32 Offset;
185 UINT32 Length;
186
187 IsDnsOffer = FALSE;
188 IpExpressedUri = FALSE;
189 IsProxyOffer = TRUE;
190 IsHttpOffer = FALSE;
191 Offer = &Cache6->Packet.Offer;
192 Options = Cache6->OptList;
193
194 ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));
195
196 Option = (EFI_DHCP6_PACKET_OPTION *)(Offer->Dhcp6.Option);
197 Offset = 0;
198 Length = GET_DHCP6_OPTION_SIZE (Offer);
199
200 //
201 // OpLen and OpCode here are both stored in network order, since they are from original packet.
202 //
203 while (Offset < Length) {
204 if (NTOHS (Option->OpCode) == DHCP6_OPT_IA_NA) {
205 Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option;
206 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_URL) {
207 //
208 // The server sends this option to inform the client about an URL to a boot file.
209 //
210 Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] = Option;
211 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_PARAM) {
212 Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
213 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {
214 Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option;
215 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) {
216 Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER] = Option;
217 }
218
219 Offset += (NTOHS (Option->OpLen) + 4);
220 Option = (EFI_DHCP6_PACKET_OPTION *)(Offer->Dhcp6.Option + Offset);
221 }
222
223 //
224 // The offer with assigned client address is NOT a proxy offer.
225 // An ia_na option, embedded with valid ia_addr option and a status_code of success.
226 //
227 Option = Options[HTTP_BOOT_DHCP6_IDX_IA_NA];
228 if (Option != NULL) {
229 Option = HttpBootParseDhcp6Options (
230 Option->Data + 12,
231 NTOHS (Option->OpLen),
232 DHCP6_OPT_STATUS_CODE
233 );
234 if (((Option != NULL) && (Option->Data[0] == 0)) || (Option == NULL)) {
235 IsProxyOffer = FALSE;
236 }
237 }
238
239 //
240 // The offer with "HTTPClient" is a Http offer.
241 //
242 Option = Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS];
243
244 if ((Option != NULL) &&
245 (NTOHS (Option->OpLen) >= 16) &&
246 (CompareMem ((Option->Data + 6), DEFAULT_CLASS_ID_DATA, 10) == 0))
247 {
248 IsHttpOffer = TRUE;
249 }
250
251 //
252 // The offer with Domain Server is a DNS offer.
253 //
254 Option = Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];
255 if (Option != NULL) {
256 IsDnsOffer = TRUE;
257 }
258
259 //
260 // Http offer must have a boot URI.
261 //
262 if (IsHttpOffer && (Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] == NULL)) {
263 return EFI_DEVICE_ERROR;
264 }
265
266 //
267 // Try to retrieve the IP of HTTP server from URI.
268 //
269 if (IsHttpOffer) {
270 Status = HttpParseUrl (
271 (CHAR8 *)Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,
272 (UINT32)AsciiStrLen ((CHAR8 *)Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data),
273 FALSE,
274 &Cache6->UriParser
275 );
276 if (EFI_ERROR (Status)) {
277 return EFI_DEVICE_ERROR;
278 }
279
280 Status = HttpUrlGetIp6 (
281 (CHAR8 *)Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,
282 Cache6->UriParser,
283 &IpAddr
284 );
285 IpExpressedUri = !EFI_ERROR (Status);
286 }
287
288 //
289 // Determine offer type of the DHCPv6 packet.
290 //
291 if (IsHttpOffer) {
292 if (IpExpressedUri) {
293 if (IsProxyOffer) {
294 OfferType = HttpOfferTypeProxyIpUri;
295 } else {
296 OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : HttpOfferTypeDhcpIpUri;
297 }
298 } else {
299 if (!IsProxyOffer) {
300 OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri;
301 } else {
302 OfferType = HttpOfferTypeProxyNameUri;
303 }
304 }
305 } else {
306 if (!IsProxyOffer) {
307 OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly;
308 } else {
309 return EFI_DEVICE_ERROR;
310 }
311 }
312
313 Cache6->OfferType = OfferType;
314 return EFI_SUCCESS;
315 }
316
317 /**
318 Cache the DHCPv6 packet.
319
320 @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.
321 @param[in] Src The pointer to the DHCPv6 packet to be cached.
322
323 @retval EFI_SUCCESS Packet is copied.
324 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
325
326 **/
327 EFI_STATUS
328 HttpBootCacheDhcp6Packet (
329 IN EFI_DHCP6_PACKET *Dst,
330 IN EFI_DHCP6_PACKET *Src
331 )
332 {
333 if (Dst->Size < Src->Length) {
334 return EFI_BUFFER_TOO_SMALL;
335 }
336
337 CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
338 Dst->Length = Src->Length;
339
340 return EFI_SUCCESS;
341 }
342
343 /**
344 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
345
346 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
347 @param[in] RcvdOffer The pointer to the received offer packet.
348
349 @retval EFI_SUCCESS Cache and parse the packet successfully.
350 @retval Others Operation failed.
351
352 **/
353 EFI_STATUS
354 HttpBootCacheDhcp6Offer (
355 IN HTTP_BOOT_PRIVATE_DATA *Private,
356 IN EFI_DHCP6_PACKET *RcvdOffer
357 )
358 {
359 HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6;
360 EFI_DHCP6_PACKET *Offer;
361 HTTP_BOOT_OFFER_TYPE OfferType;
362 EFI_STATUS Status;
363
364 Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
365 Offer = &Cache6->Packet.Offer;
366
367 //
368 // Cache the content of DHCPv6 packet firstly.
369 //
370 Status = HttpBootCacheDhcp6Packet (Offer, RcvdOffer);
371 if (EFI_ERROR (Status)) {
372 return Status;
373 }
374
375 //
376 // Validate the DHCPv6 packet, and parse the options and offer type.
377 //
378 if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) {
379 return EFI_ABORTED;
380 }
381
382 //
383 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
384 //
385 OfferType = Cache6->OfferType;
386 ASSERT (OfferType < HttpOfferTypeMax);
387 ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM);
388 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
389 Private->OfferCount[OfferType]++;
390 Private->OfferNum++;
391
392 return EFI_SUCCESS;
393 }
394
395 /**
396 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
397 to intercept events that occurred in the configuration process.
398
399 @param[in] This The pointer to the EFI DHCPv6 Protocol.
400 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
401 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
402 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
403 state transition.
404 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
405 @param[out] NewPacket The packet that is used to replace the Packet above.
406
407 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
408 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
409 driver will continue to wait for more packets.
410 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
411 @retval EFI_OUT_OF_RESOURCES There are not enough resources.
412
413 **/
414 EFI_STATUS
415 EFIAPI
416 HttpBootDhcp6CallBack (
417 IN EFI_DHCP6_PROTOCOL *This,
418 IN VOID *Context,
419 IN EFI_DHCP6_STATE CurrentState,
420 IN EFI_DHCP6_EVENT Dhcp6Event,
421 IN EFI_DHCP6_PACKET *Packet,
422 OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL
423 )
424 {
425 HTTP_BOOT_PRIVATE_DATA *Private;
426 EFI_DHCP6_PACKET *SelectAd;
427 EFI_STATUS Status;
428 BOOLEAN Received;
429
430 if ((Dhcp6Event != Dhcp6SendSolicit) &&
431 (Dhcp6Event != Dhcp6RcvdAdvertise) &&
432 (Dhcp6Event != Dhcp6SendRequest) &&
433 (Dhcp6Event != Dhcp6RcvdReply) &&
434 (Dhcp6Event != Dhcp6SelectAdvertise))
435 {
436 return EFI_SUCCESS;
437 }
438
439 ASSERT (Packet != NULL);
440
441 Private = (HTTP_BOOT_PRIVATE_DATA *)Context;
442 Status = EFI_SUCCESS;
443 if ((Private->HttpBootCallback != NULL) && (Dhcp6Event != Dhcp6SelectAdvertise)) {
444 Received = (BOOLEAN)(Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);
445 Status = Private->HttpBootCallback->Callback (
446 Private->HttpBootCallback,
447 HttpBootDhcp6,
448 Received,
449 Packet->Length,
450 &Packet->Dhcp6
451 );
452 if (EFI_ERROR (Status)) {
453 return EFI_ABORTED;
454 }
455 }
456
457 switch (Dhcp6Event) {
458 case Dhcp6RcvdAdvertise:
459 Status = EFI_NOT_READY;
460 if (Packet->Length > HTTP_BOOT_DHCP6_PACKET_MAX_SIZE) {
461 //
462 // Ignore the incoming packets which exceed the maximum length.
463 //
464 break;
465 }
466
467 if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {
468 //
469 // Cache the dhcp offers to OfferBuffer[] for select later, and record
470 // the OfferIndex and OfferCount.
471 // If error happens, just ignore this packet and continue to wait more offer.
472 //
473 HttpBootCacheDhcp6Offer (Private, Packet);
474 }
475
476 break;
477
478 case Dhcp6SelectAdvertise:
479 //
480 // Select offer by the default policy or by order, and record the SelectIndex
481 // and SelectProxyType.
482 //
483 HttpBootSelectDhcpOffer (Private);
484
485 if (Private->SelectIndex == 0) {
486 Status = EFI_ABORTED;
487 } else {
488 ASSERT (NewPacket != NULL);
489 SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;
490 *NewPacket = AllocateZeroPool (SelectAd->Size);
491 if (*NewPacket == NULL) {
492 return EFI_OUT_OF_RESOURCES;
493 }
494
495 CopyMem (*NewPacket, SelectAd, SelectAd->Size);
496 }
497
498 break;
499
500 default:
501 break;
502 }
503
504 return Status;
505 }
506
507 /**
508 Check whether IP driver could route the message which will be sent to ServerIp address.
509
510 This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid
511 route is found in IP6 route table, the address will be filed in GatewayAddr and return.
512
513 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
514 @param[in] TimeOutInSecond Timeout value in seconds.
515 @param[out] GatewayAddr Pointer to store the gateway IP address.
516
517 @retval EFI_SUCCESS Found a valid gateway address successfully.
518 @retval EFI_TIMEOUT The operation is time out.
519 @retval Other Unexpected error happened.
520
521 **/
522 EFI_STATUS
523 HttpBootCheckRouteTable (
524 IN HTTP_BOOT_PRIVATE_DATA *Private,
525 IN UINTN TimeOutInSecond,
526 OUT EFI_IPv6_ADDRESS *GatewayAddr
527 )
528 {
529 EFI_STATUS Status;
530 EFI_IP6_PROTOCOL *Ip6;
531 EFI_IP6_MODE_DATA Ip6ModeData;
532 UINTN Index;
533 EFI_EVENT TimeOutEvt;
534 UINTN RetryCount;
535 BOOLEAN GatewayIsFound;
536
537 ASSERT (GatewayAddr != NULL);
538 ASSERT (Private != NULL);
539
540 Ip6 = Private->Ip6;
541 GatewayIsFound = FALSE;
542 RetryCount = 0;
543 TimeOutEvt = NULL;
544 Status = EFI_SUCCESS;
545 ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));
546
547 while (TRUE) {
548 Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);
549 if (EFI_ERROR (Status)) {
550 goto ON_EXIT;
551 }
552
553 //
554 // Find out the gateway address which can route the message which send to ServerIp.
555 //
556 for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {
557 if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {
558 IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);
559 GatewayIsFound = TRUE;
560 break;
561 }
562 }
563
564 if (Ip6ModeData.AddressList != NULL) {
565 FreePool (Ip6ModeData.AddressList);
566 }
567
568 if (Ip6ModeData.GroupTable != NULL) {
569 FreePool (Ip6ModeData.GroupTable);
570 }
571
572 if (Ip6ModeData.RouteTable != NULL) {
573 FreePool (Ip6ModeData.RouteTable);
574 }
575
576 if (Ip6ModeData.NeighborCache != NULL) {
577 FreePool (Ip6ModeData.NeighborCache);
578 }
579
580 if (Ip6ModeData.PrefixTable != NULL) {
581 FreePool (Ip6ModeData.PrefixTable);
582 }
583
584 if (Ip6ModeData.IcmpTypeList != NULL) {
585 FreePool (Ip6ModeData.IcmpTypeList);
586 }
587
588 if (GatewayIsFound || (RetryCount == TimeOutInSecond)) {
589 break;
590 }
591
592 RetryCount++;
593
594 //
595 // Delay 1 second then recheck it again.
596 //
597 if (TimeOutEvt == NULL) {
598 Status = gBS->CreateEvent (
599 EVT_TIMER,
600 TPL_CALLBACK,
601 NULL,
602 NULL,
603 &TimeOutEvt
604 );
605 if (EFI_ERROR (Status)) {
606 goto ON_EXIT;
607 }
608 }
609
610 Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);
611 if (EFI_ERROR (Status)) {
612 goto ON_EXIT;
613 }
614
615 while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
616 Ip6->Poll (Ip6);
617 }
618 }
619
620 ON_EXIT:
621 if (TimeOutEvt != NULL) {
622 gBS->CloseEvent (TimeOutEvt);
623 }
624
625 if (GatewayIsFound) {
626 Status = EFI_SUCCESS;
627 } else if (RetryCount == TimeOutInSecond) {
628 Status = EFI_TIMEOUT;
629 }
630
631 return Status;
632 }
633
634 /**
635 Set the IP6 policy to Automatic.
636
637 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
638
639 @retval EFI_SUCCESS Switch the IP policy successfully.
640 @retval Others Unexpected error happened.
641
642 **/
643 EFI_STATUS
644 HttpBootSetIp6Policy (
645 IN HTTP_BOOT_PRIVATE_DATA *Private
646 )
647 {
648 EFI_IP6_CONFIG_POLICY Policy;
649 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
650 EFI_STATUS Status;
651 UINTN DataSize;
652
653 Ip6Config = Private->Ip6Config;
654 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
655
656 //
657 // Get and store the current policy of IP6 driver.
658 //
659 Status = Ip6Config->GetData (
660 Ip6Config,
661 Ip6ConfigDataTypePolicy,
662 &DataSize,
663 &Policy
664 );
665 if (EFI_ERROR (Status)) {
666 return Status;
667 }
668
669 if (Policy == Ip6ConfigPolicyManual) {
670 Policy = Ip6ConfigPolicyAutomatic;
671 Status = Ip6Config->SetData (
672 Ip6Config,
673 Ip6ConfigDataTypePolicy,
674 sizeof (EFI_IP6_CONFIG_POLICY),
675 &Policy
676 );
677 if (EFI_ERROR (Status)) {
678 return Status;
679 }
680 }
681
682 return EFI_SUCCESS;
683 }
684
685 /**
686 This function will register the default DNS addresses to the network device.
687
688 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
689 @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes.
690 @param[in] DnsServerData Point a list of DNS server address in an array
691 of EFI_IPv6_ADDRESS instances.
692
693 @retval EFI_SUCCESS The DNS configuration has been configured successfully.
694 @retval Others Failed to configure the address.
695
696 **/
697 EFI_STATUS
698 HttpBootSetIp6Dns (
699 IN HTTP_BOOT_PRIVATE_DATA *Private,
700 IN UINTN DataLength,
701 IN VOID *DnsServerData
702 )
703 {
704 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
705
706 ASSERT (Private->UsingIpv6);
707
708 Ip6Config = Private->Ip6Config;
709
710 return Ip6Config->SetData (
711 Ip6Config,
712 Ip6ConfigDataTypeDnsServer,
713 DataLength,
714 DnsServerData
715 );
716 }
717
718 /**
719 This function will register the IPv6 gateway address to the network device.
720
721 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
722
723 @retval EFI_SUCCESS The new IP configuration has been configured successfully.
724 @retval Others Failed to configure the address.
725
726 **/
727 EFI_STATUS
728 HttpBootSetIp6Gateway (
729 IN HTTP_BOOT_PRIVATE_DATA *Private
730 )
731 {
732 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
733 EFI_STATUS Status;
734
735 ASSERT (Private->UsingIpv6);
736 Ip6Config = Private->Ip6Config;
737
738 //
739 // Set the default gateway address.
740 //
741 if (!Private->NoGateway && !NetIp6IsUnspecifiedAddr (&Private->GatewayIp.v6)) {
742 Status = Ip6Config->SetData (
743 Ip6Config,
744 Ip6ConfigDataTypeGateway,
745 sizeof (EFI_IPv6_ADDRESS),
746 &Private->GatewayIp.v6
747 );
748 if (EFI_ERROR (Status)) {
749 return Status;
750 }
751 }
752
753 return EFI_SUCCESS;
754 }
755
756 /**
757 This function will register the station IP address.
758
759 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
760
761 @retval EFI_SUCCESS The new IP address has been configured successfully.
762 @retval Others Failed to configure the address.
763
764 **/
765 EFI_STATUS
766 HttpBootSetIp6Address (
767 IN HTTP_BOOT_PRIVATE_DATA *Private
768 )
769 {
770 EFI_STATUS Status;
771 EFI_IP6_PROTOCOL *Ip6;
772 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
773 EFI_IP6_CONFIG_POLICY Policy;
774 EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr;
775 EFI_IPv6_ADDRESS *Ip6Addr;
776 EFI_IPv6_ADDRESS GatewayAddr;
777 EFI_IP6_CONFIG_DATA Ip6CfgData;
778 EFI_EVENT MappedEvt;
779 UINTN DataSize;
780 BOOLEAN IsAddressOk;
781 UINTN Index;
782
783 ASSERT (Private->UsingIpv6);
784
785 MappedEvt = NULL;
786 IsAddressOk = FALSE;
787 Ip6Addr = NULL;
788 Ip6Cfg = Private->Ip6Config;
789 Ip6 = Private->Ip6;
790
791 ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
792 CopyMem (&CfgAddr, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
793 ZeroMem (&Ip6CfgData, sizeof (EFI_IP6_CONFIG_DATA));
794
795 Ip6CfgData.AcceptIcmpErrors = TRUE;
796 Ip6CfgData.DefaultProtocol = IP6_ICMP;
797 Ip6CfgData.HopLimit = HTTP_BOOT_DEFAULT_HOPLIMIT;
798 Ip6CfgData.ReceiveTimeout = HTTP_BOOT_DEFAULT_LIFETIME;
799 Ip6CfgData.TransmitTimeout = HTTP_BOOT_DEFAULT_LIFETIME;
800
801 Status = Ip6->Configure (Ip6, &Ip6CfgData);
802 if (EFI_ERROR (Status)) {
803 goto ON_EXIT;
804 }
805
806 //
807 // Retrieve the gateway address from IP6 route table.
808 //
809 Status = HttpBootCheckRouteTable (Private, HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);
810 if (EFI_ERROR (Status)) {
811 Private->NoGateway = TRUE;
812 } else {
813 IP6_COPY_ADDRESS (&Private->GatewayIp.v6, &GatewayAddr);
814 }
815
816 //
817 // Set the new address by Ip6ConfigProtocol manually.
818 //
819 Policy = Ip6ConfigPolicyManual;
820 Status = Ip6Cfg->SetData (
821 Ip6Cfg,
822 Ip6ConfigDataTypePolicy,
823 sizeof (EFI_IP6_CONFIG_POLICY),
824 &Policy
825 );
826 if (EFI_ERROR (Status)) {
827 goto ON_EXIT;
828 }
829
830 //
831 // Create a notify event to set address flag when DAD if IP6 driver succeeded.
832 //
833 Status = gBS->CreateEvent (
834 EVT_NOTIFY_SIGNAL,
835 TPL_NOTIFY,
836 HttpBootCommonNotify,
837 &IsAddressOk,
838 &MappedEvt
839 );
840 if (EFI_ERROR (Status)) {
841 goto ON_EXIT;
842 }
843
844 //
845 // Set static host ip6 address. This is a asynchronous process.
846 //
847 Status = Ip6Cfg->RegisterDataNotify (
848 Ip6Cfg,
849 Ip6ConfigDataTypeManualAddress,
850 MappedEvt
851 );
852 if (EFI_ERROR (Status)) {
853 goto ON_EXIT;
854 }
855
856 Status = Ip6Cfg->SetData (
857 Ip6Cfg,
858 Ip6ConfigDataTypeManualAddress,
859 sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS),
860 &CfgAddr
861 );
862 if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {
863 goto ON_EXIT;
864 } else if (Status == EFI_NOT_READY) {
865 //
866 // Poll the network until the asynchronous process is finished.
867 //
868 while (!IsAddressOk) {
869 Ip6->Poll (Ip6);
870 }
871
872 //
873 // Check whether the Ip6 Address setting is successed.
874 //
875 DataSize = 0;
876 Status = Ip6Cfg->GetData (
877 Ip6Cfg,
878 Ip6ConfigDataTypeManualAddress,
879 &DataSize,
880 NULL
881 );
882 if ((Status != EFI_BUFFER_TOO_SMALL) || (DataSize == 0)) {
883 Status = EFI_DEVICE_ERROR;
884 goto ON_EXIT;
885 }
886
887 Ip6Addr = AllocatePool (DataSize);
888 if (Ip6Addr == NULL) {
889 return EFI_OUT_OF_RESOURCES;
890 }
891
892 Status = Ip6Cfg->GetData (
893 Ip6Cfg,
894 Ip6ConfigDataTypeManualAddress,
895 &DataSize,
896 (VOID *)Ip6Addr
897 );
898 if (EFI_ERROR (Status)) {
899 Status = EFI_DEVICE_ERROR;
900 goto ON_EXIT;
901 }
902
903 for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) {
904 if (CompareMem (Ip6Addr + Index, &CfgAddr, sizeof (EFI_IPv6_ADDRESS)) == 0) {
905 break;
906 }
907 }
908
909 if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {
910 Status = EFI_ABORTED;
911 goto ON_EXIT;
912 }
913 }
914
915 ON_EXIT:
916 if (MappedEvt != NULL) {
917 Ip6Cfg->UnregisterDataNotify (
918 Ip6Cfg,
919 Ip6ConfigDataTypeManualAddress,
920 MappedEvt
921 );
922 gBS->CloseEvent (MappedEvt);
923 }
924
925 if (Ip6Addr != NULL) {
926 FreePool (Ip6Addr);
927 }
928
929 return Status;
930 }
931
932 /**
933 Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information.
934
935 @param[in] Private Pointer to HTTP_BOOT private data.
936
937 @retval EFI_SUCCESS The S.A.R.R process successfully finished.
938 @retval Others Failed to finish the S.A.R.R process.
939
940 **/
941 EFI_STATUS
942 HttpBootDhcp6Sarr (
943 IN HTTP_BOOT_PRIVATE_DATA *Private
944 )
945 {
946 EFI_DHCP6_PROTOCOL *Dhcp6;
947 EFI_DHCP6_CONFIG_DATA Config;
948 EFI_DHCP6_MODE_DATA Mode;
949 EFI_DHCP6_RETRANSMISSION *Retransmit;
950 EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_OPTION_MAX_NUM];
951 UINT32 OptCount;
952 UINT8 Buffer[HTTP_BOOT_DHCP6_OPTION_MAX_SIZE];
953 EFI_STATUS Status;
954
955 Dhcp6 = Private->Dhcp6;
956 ASSERT (Dhcp6 != NULL);
957
958 //
959 // Build options list for the request packet.
960 //
961 OptCount = HttpBootBuildDhcp6Options (Private, OptList, Buffer);
962 ASSERT (OptCount > 0);
963
964 Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
965 if (Retransmit == NULL) {
966 return EFI_OUT_OF_RESOURCES;
967 }
968
969 ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));
970 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
971
972 Config.OptionCount = OptCount;
973 Config.OptionList = OptList;
974 Config.Dhcp6Callback = HttpBootDhcp6CallBack;
975 Config.CallbackContext = Private;
976 Config.IaInfoEvent = NULL;
977 Config.RapidCommit = FALSE;
978 Config.ReconfigureAccept = FALSE;
979 Config.IaDescriptor.IaId = NET_RANDOM (NetRandomInitSeed ());
980 Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;
981 Config.SolicitRetransmission = Retransmit;
982 Retransmit->Irt = 4;
983 Retransmit->Mrc = 4;
984 Retransmit->Mrt = 32;
985 Retransmit->Mrd = 60;
986
987 //
988 // Configure the DHCPv6 instance for HTTP boot.
989 //
990 Status = Dhcp6->Configure (Dhcp6, &Config);
991 FreePool (Retransmit);
992 if (EFI_ERROR (Status)) {
993 goto ON_EXIT;
994 }
995
996 //
997 // Initialize the record fields for DHCPv6 offer in private data.
998 //
999 Private->OfferNum = 0;
1000 Private->SelectIndex = 0;
1001 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
1002 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
1003
1004 //
1005 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
1006 //
1007 Status = Dhcp6->Start (Dhcp6);
1008 if (EFI_ERROR (Status)) {
1009 goto ON_EXIT;
1010 }
1011
1012 //
1013 // Get the acquired IPv6 address and store them.
1014 //
1015 Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);
1016 if (EFI_ERROR (Status)) {
1017 goto ON_EXIT;
1018 }
1019
1020 ASSERT (Mode.Ia->State == Dhcp6Bound);
1021 CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));
1022
1023 AsciiPrint ("\n Station IPv6 address is ");
1024 HttpBootShowIp6Addr (&Private->StationIp.v6);
1025 AsciiPrint ("\n");
1026
1027 ON_EXIT:
1028 if (EFI_ERROR (Status)) {
1029 Dhcp6->Stop (Dhcp6);
1030 Dhcp6->Configure (Dhcp6, NULL);
1031 } else {
1032 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
1033 Dhcp6->Configure (Dhcp6, &Config);
1034 if (Mode.ClientId != NULL) {
1035 FreePool (Mode.ClientId);
1036 }
1037
1038 if (Mode.Ia != NULL) {
1039 FreePool (Mode.Ia);
1040 }
1041 }
1042
1043 return Status;
1044 }