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