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
HttpBootBuildDhcp6Options(IN HTTP_BOOT_PRIVATE_DATA * Private,OUT EFI_DHCP6_PACKET_OPTION ** OptList,IN UINT8 * Buffer)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 *
HttpBootParseDhcp6Options(IN UINT8 * Buffer,IN UINT32 Length,IN UINT16 OptTag)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
HttpBootParseDhcp6Packet(IN HTTP_BOOT_DHCP6_PACKET_CACHE * Cache6)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
HttpBootCacheDhcp6Packet(IN EFI_DHCP6_PACKET * Dst,IN EFI_DHCP6_PACKET * Src)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
HttpBootCacheDhcp6Offer(IN HTTP_BOOT_PRIVATE_DATA * Private,IN EFI_DHCP6_PACKET * RcvdOffer)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
HttpBootDhcp6CallBack(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)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
HttpBootCheckRouteTable(IN HTTP_BOOT_PRIVATE_DATA * Private,IN UINTN TimeOutInSecond,OUT EFI_IPv6_ADDRESS * GatewayAddr)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
HttpBootSetIp6Policy(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootSetIp6Dns(IN HTTP_BOOT_PRIVATE_DATA * Private,IN UINTN DataLength,IN VOID * DnsServerData)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
HttpBootSetIp6Gateway(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootSetIp6Address(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootDhcp6Sarr(IN HTTP_BOOT_PRIVATE_DATA * Private)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 
1033