Home | History | Annotate | Download | only in HttpBootDxe
      1 /** @file
      2   Functions implementation related with DHCPv6 for HTTP boot driver.
      3 
      4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials are licensed and made available under
      6 the terms and conditions of the BSD License that accompanies this distribution.
      7 The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php.
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT 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 **/
     27 UINT32
     28 HttpBootBuildDhcp6Options (
     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 **/
    138 EFI_DHCP6_PACKET_OPTION *
    139 HttpBootParseDhcp6Options (
    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 **/
    178 EFI_STATUS
    179 HttpBootParseDhcp6Packet (
    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 **/
    329 VOID
    330 HttpBootCacheDhcp6Packet (
    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 **/
    348 VOID
    349 HttpBootCacheDhcp6Offer (
    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 **/
    402 EFI_STATUS
    403 EFIAPI
    404 HttpBootDhcp6CallBack (
    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 **/
    477 EFI_STATUS
    478 HttpBootCheckRouteTable (
    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 
    569 ON_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 **/
    592 EFI_STATUS
    593 HttpBootSetIp6Policy (
    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 **/
    645 EFI_STATUS
    646 HttpBootSetIp6Dns (
    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 **/
    675 EFI_STATUS
    676 HttpBootSetIp6Gateway (
    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 **/
    713 EFI_STATUS
    714 HttpBootSetIp6Address (
    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 
    860 ON_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 **/
    886 EFI_STATUS
    887 HttpBootDhcp6Sarr (
    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 
    971 ON_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