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