Home | History | Annotate | Download | only in HttpBootDxe
      1 /** @file
      2   Functions implementation related with DHCPv4 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 // This is a map from the interested DHCP4 option tags' index to the tag value.
     19 //
     20 UINT8 mInterestedDhcp4Tags[HTTP_BOOT_DHCP4_TAG_INDEX_MAX] = {
     21   HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN,
     22   HTTP_BOOT_DHCP4_TAG_OVERLOAD,
     23   HTTP_BOOT_DHCP4_TAG_MSG_TYPE,
     24   HTTP_BOOT_DHCP4_TAG_SERVER_ID,
     25   HTTP_BOOT_DHCP4_TAG_CLASS_ID,
     26   HTTP_BOOT_DHCP4_TAG_BOOTFILE,
     27   HTTP_BOOT_DHCP4_TAG_DNS_SERVER
     28 };
     29 
     30 //
     31 // There are 4 times retries with the value of 4, 8, 16 and 32, refers to UEFI 2.5 spec.
     32 //
     33 UINT32 mHttpDhcpTimeout[4] = {4, 8, 16, 32};
     34 
     35 /**
     36   Build the options buffer for the DHCPv4 request packet.
     37 
     38   @param[in]  Private             Pointer to HTTP boot driver private data.
     39   @param[out] OptList             Pointer to the option pointer array.
     40   @param[in]  Buffer              Pointer to the buffer to contain the option list.
     41 
     42   @return     Index               The count of the built-in options.
     43 
     44 **/
     45 UINT32
     46 HttpBootBuildDhcp4Options (
     47   IN  HTTP_BOOT_PRIVATE_DATA        *Private,
     48   OUT EFI_DHCP4_PACKET_OPTION       **OptList,
     49   IN  UINT8                         *Buffer
     50   )
     51 {
     52   HTTP_BOOT_DHCP4_OPTION_ENTRY  OptEnt;
     53   UINT16                        Value;
     54   UINT32                        Index;
     55 
     56   Index      = 0;
     57   OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;
     58 
     59   //
     60   // Append parameter request list option.
     61   //
     62   OptList[Index]->OpCode    = HTTP_BOOT_DHCP4_TAG_PARA_LIST;
     63   OptList[Index]->Length    = 27;
     64   OptEnt.Para               = (HTTP_BOOT_DHCP4_OPTION_PARA *) OptList[Index]->Data;
     65   OptEnt.Para->ParaList[0]  = HTTP_BOOT_DHCP4_TAG_NETMASK;
     66   OptEnt.Para->ParaList[1]  = HTTP_BOOT_DHCP4_TAG_TIME_OFFSET;
     67   OptEnt.Para->ParaList[2]  = HTTP_BOOT_DHCP4_TAG_ROUTER;
     68   OptEnt.Para->ParaList[3]  = HTTP_BOOT_DHCP4_TAG_TIME_SERVER;
     69   OptEnt.Para->ParaList[4]  = HTTP_BOOT_DHCP4_TAG_NAME_SERVER;
     70   OptEnt.Para->ParaList[5]  = HTTP_BOOT_DHCP4_TAG_DNS_SERVER;
     71   OptEnt.Para->ParaList[6]  = HTTP_BOOT_DHCP4_TAG_HOSTNAME;
     72   OptEnt.Para->ParaList[7]  = HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN;
     73   OptEnt.Para->ParaList[8]  = HTTP_BOOT_DHCP4_TAG_DOMAINNAME;
     74   OptEnt.Para->ParaList[9]  = HTTP_BOOT_DHCP4_TAG_ROOTPATH;
     75   OptEnt.Para->ParaList[10] = HTTP_BOOT_DHCP4_TAG_EXTEND_PATH;
     76   OptEnt.Para->ParaList[11] = HTTP_BOOT_DHCP4_TAG_EMTU;
     77   OptEnt.Para->ParaList[12] = HTTP_BOOT_DHCP4_TAG_TTL;
     78   OptEnt.Para->ParaList[13] = HTTP_BOOT_DHCP4_TAG_BROADCAST;
     79   OptEnt.Para->ParaList[14] = HTTP_BOOT_DHCP4_TAG_NIS_DOMAIN;
     80   OptEnt.Para->ParaList[15] = HTTP_BOOT_DHCP4_TAG_NIS_SERVER;
     81   OptEnt.Para->ParaList[16] = HTTP_BOOT_DHCP4_TAG_NTP_SERVER;
     82   OptEnt.Para->ParaList[17] = HTTP_BOOT_DHCP4_TAG_VENDOR;
     83   OptEnt.Para->ParaList[18] = HTTP_BOOT_DHCP4_TAG_REQUEST_IP;
     84   OptEnt.Para->ParaList[19] = HTTP_BOOT_DHCP4_TAG_LEASE;
     85   OptEnt.Para->ParaList[20] = HTTP_BOOT_DHCP4_TAG_SERVER_ID;
     86   OptEnt.Para->ParaList[21] = HTTP_BOOT_DHCP4_TAG_T1;
     87   OptEnt.Para->ParaList[22] = HTTP_BOOT_DHCP4_TAG_T2;
     88   OptEnt.Para->ParaList[23] = HTTP_BOOT_DHCP4_TAG_CLASS_ID;
     89   OptEnt.Para->ParaList[25] = HTTP_BOOT_DHCP4_TAG_BOOTFILE;
     90   OptEnt.Para->ParaList[26] = HTTP_BOOT_DHCP4_TAG_UUID;
     91   Index++;
     92   OptList[Index]            = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
     93 
     94   //
     95   // Append UUID/Guid-based client identifier option
     96   //
     97   OptList[Index]->OpCode  = HTTP_BOOT_DHCP4_TAG_UUID;
     98   OptList[Index]->Length  = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UUID);
     99   OptEnt.Uuid             = (HTTP_BOOT_DHCP4_OPTION_UUID *) OptList[Index]->Data;
    100   OptEnt.Uuid->Type       = 0;
    101   if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {
    102     //
    103     // Zero the Guid to indicate NOT programable if failed to get system Guid.
    104     //
    105     ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));
    106   }
    107   Index++;
    108   OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
    109 
    110   //
    111   // Append client network device interface option
    112   //
    113   OptList[Index]->OpCode  = HTTP_BOOT_DHCP4_TAG_UNDI;
    114   OptList[Index]->Length  = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UNDI);
    115   OptEnt.Undi             = (HTTP_BOOT_DHCP4_OPTION_UNDI *) OptList[Index]->Data;
    116 
    117   if (Private->Nii != NULL) {
    118     OptEnt.Undi->Type     = Private->Nii->Type;
    119     OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
    120     OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
    121   } else {
    122     OptEnt.Undi->Type     = DEFAULT_UNDI_TYPE;
    123     OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
    124     OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
    125   }
    126 
    127   Index++;
    128   OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
    129 
    130   //
    131   // Append client system architecture option
    132   //
    133   OptList[Index]->OpCode  = HTTP_BOOT_DHCP4_TAG_ARCH;
    134   OptList[Index]->Length  = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_ARCH);
    135   OptEnt.Arch             = (HTTP_BOOT_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
    136   Value                   = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE);
    137   CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
    138   Index++;
    139   OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
    140 
    141   //
    142   // Append vendor class identify option
    143   //
    144   OptList[Index]->OpCode  = HTTP_BOOT_DHCP4_TAG_CLASS_ID;
    145   OptList[Index]->Length  = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_CLID);
    146   OptEnt.Clid             = (HTTP_BOOT_DHCP4_OPTION_CLID *) OptList[Index]->Data;
    147   CopyMem (
    148     OptEnt.Clid,
    149     DEFAULT_CLASS_ID_DATA,
    150     sizeof (HTTP_BOOT_DHCP4_OPTION_CLID)
    151     );
    152   HttpBootUintnToAscDecWithFormat (
    153     EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE,
    154     OptEnt.Clid->ArchitectureType,
    155     sizeof (OptEnt.Clid->ArchitectureType)
    156     );
    157 
    158   if (Private->Nii != NULL) {
    159     CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));
    160     HttpBootUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
    161     HttpBootUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
    162   }
    163 
    164   Index++;
    165 
    166   return Index;
    167 }
    168 
    169 /**
    170   Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer.
    171 
    172   @param[in]  Buffer              Pointer to the option buffer.
    173   @param[in]  Length              Length of the option buffer.
    174   @param[in]  OptTag              Tag of the required option.
    175 
    176   @retval     NULL                Failed to find the required option.
    177   @retval     Others              The position of the required option.
    178 
    179 **/
    180 EFI_DHCP4_PACKET_OPTION *
    181 HttpBootParseDhcp4Options (
    182   IN UINT8                      *Buffer,
    183   IN UINT32                     Length,
    184   IN UINT8                      OptTag
    185   )
    186 {
    187   EFI_DHCP4_PACKET_OPTION       *Option;
    188   UINT32                        Offset;
    189 
    190   Option  = (EFI_DHCP4_PACKET_OPTION *) Buffer;
    191   Offset  = 0;
    192 
    193   while (Offset < Length && Option->OpCode != HTTP_BOOT_DHCP4_TAG_EOP) {
    194 
    195     if (Option->OpCode == OptTag) {
    196       //
    197       // Found the required option.
    198       //
    199       return Option;
    200     }
    201 
    202     //
    203     // Skip the current option to the next.
    204     //
    205     if (Option->OpCode == HTTP_BOOT_DHCP4_TAG_PAD) {
    206       Offset++;
    207     } else {
    208       Offset += Option->Length + 2;
    209     }
    210 
    211     Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
    212   }
    213 
    214   return NULL;
    215 }
    216 
    217 /**
    218   Cache the DHCPv4 packet.
    219 
    220   @param[in]  Dst          Pointer to the cache buffer for DHCPv4 packet.
    221   @param[in]  Src          Pointer to the DHCPv4 packet to be cached.
    222 
    223 **/
    224 VOID
    225 HttpBootCacheDhcp4Packet (
    226   IN EFI_DHCP4_PACKET     *Dst,
    227   IN EFI_DHCP4_PACKET     *Src
    228   )
    229 {
    230   ASSERT (Dst->Size >= Src->Length);
    231 
    232   CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
    233   Dst->Length = Src->Length;
    234 }
    235 
    236 /**
    237   Parse the cached DHCPv4 packet, including all the options.
    238 
    239   @param[in]  Cache4           Pointer to cached DHCPv4 packet.
    240 
    241   @retval     EFI_SUCCESS      Parsed the DHCPv4 packet successfully.
    242   @retval     EFI_DEVICE_ERROR Failed to parse an invalid packet.
    243 
    244 **/
    245 EFI_STATUS
    246 HttpBootParseDhcp4Packet (
    247   IN HTTP_BOOT_DHCP4_PACKET_CACHE    *Cache4
    248   )
    249 {
    250   EFI_DHCP4_PACKET               *Offer;
    251   EFI_DHCP4_PACKET_OPTION        **Options;
    252   UINTN                          Index;
    253   EFI_DHCP4_PACKET_OPTION        *Option;
    254   BOOLEAN                        IsProxyOffer;
    255   BOOLEAN                        IsHttpOffer;
    256   BOOLEAN                        IsDnsOffer;
    257   BOOLEAN                        IpExpressedUri;
    258   UINT8                          *Ptr8;
    259   EFI_STATUS                     Status;
    260   HTTP_BOOT_OFFER_TYPE           OfferType;
    261   EFI_IPv4_ADDRESS               IpAddr;
    262 
    263   IsDnsOffer     = FALSE;
    264   IpExpressedUri = FALSE;
    265   IsProxyOffer   = FALSE;
    266   IsHttpOffer    = FALSE;
    267 
    268   ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));
    269 
    270   Offer   = &Cache4->Packet.Offer;
    271   Options = Cache4->OptList;
    272 
    273   //
    274   // Parse DHCPv4 options in this offer, and store the pointers.
    275   // First, try to parse DHCPv4 options from the DHCP optional parameters field.
    276   //
    277   for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
    278     Options[Index] = HttpBootParseDhcp4Options (
    279                        Offer->Dhcp4.Option,
    280                        GET_OPTION_BUFFER_LEN (Offer),
    281                        mInterestedDhcp4Tags[Index]
    282                        );
    283   }
    284   //
    285   // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132.
    286   // If yes, try to parse options from the BootFileName field, then ServerName field.
    287   //
    288   Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD];
    289   if (Option != NULL) {
    290     if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_FILE) != 0) {
    291       for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
    292         if (Options[Index] == NULL) {
    293           Options[Index] = HttpBootParseDhcp4Options (
    294                              (UINT8 *) Offer->Dhcp4.Header.BootFileName,
    295                              sizeof (Offer->Dhcp4.Header.BootFileName),
    296                              mInterestedDhcp4Tags[Index]
    297                              );
    298         }
    299       }
    300     }
    301     if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME) != 0) {
    302       for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
    303         if (Options[Index] == NULL) {
    304           Options[Index] = HttpBootParseDhcp4Options (
    305                              (UINT8 *) Offer->Dhcp4.Header.ServerName,
    306                              sizeof (Offer->Dhcp4.Header.ServerName),
    307                              mInterestedDhcp4Tags[Index]
    308                              );
    309         }
    310       }
    311     }
    312   }
    313 
    314   //
    315   // The offer with "yiaddr" is a proxy offer.
    316   //
    317   if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {
    318     IsProxyOffer = TRUE;
    319   }
    320 
    321   //
    322   // The offer with "HTTPClient" is a Http offer.
    323   //
    324   Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID];
    325   if ((Option != NULL) && (Option->Length >= 9) &&
    326       (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {
    327     IsHttpOffer = TRUE;
    328   }
    329 
    330   //
    331   // The offer with Domain Server is a DNS offer.
    332   //
    333   Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
    334   if (Option != NULL) {
    335     IsDnsOffer = TRUE;
    336   }
    337 
    338   //
    339   // Parse boot file name:
    340   // Boot URI information is provided thru 'file' field in DHCP Header or option 67.
    341   // According to RFC 2132, boot file name should be read from DHCP option 67 (bootfile name) if present.
    342   // Otherwise, read from boot file field in DHCP header.
    343   //
    344   if (Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
    345     //
    346     // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null
    347     // terminated string. So force to append null terminated character at the end of string.
    348     //
    349     Ptr8 =  (UINT8*)&Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];
    350     Ptr8 += Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Length;
    351     if (*(Ptr8 - 1) != '\0') {
    352       *Ptr8 = '\0';
    353     }
    354   } else if (Offer->Dhcp4.Header.BootFileName[0] != 0) {
    355     //
    356     // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it.
    357     // Do not count dhcp option header here, or else will destroy the serverhostname.
    358     //
    359     Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)
    360                                                     (&Offer->Dhcp4.Header.BootFileName[0] -
    361                                                     OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));
    362   }
    363 
    364   //
    365   // Http offer must have a boot URI.
    366   //
    367   if (IsHttpOffer && Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
    368     return EFI_DEVICE_ERROR;
    369   }
    370 
    371   //
    372   // Try to retrieve the IP of HTTP server from URI.
    373   //
    374   if (IsHttpOffer) {
    375     Status = HttpParseUrl (
    376                (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
    377                (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data),
    378                FALSE,
    379                &Cache4->UriParser
    380                );
    381     if (EFI_ERROR (Status)) {
    382       return EFI_DEVICE_ERROR;
    383     }
    384 
    385     Status = HttpUrlGetIp4 (
    386                (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
    387                Cache4->UriParser,
    388                &IpAddr
    389                );
    390     IpExpressedUri = !EFI_ERROR (Status);
    391   }
    392 
    393   //
    394   // Determine offer type of the DHCPv4 packet.
    395   //
    396   if (IsHttpOffer) {
    397     if (IpExpressedUri) {
    398       OfferType = IsProxyOffer ? HttpOfferTypeProxyIpUri : HttpOfferTypeDhcpIpUri;
    399     } else {
    400       if (!IsProxyOffer) {
    401         OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri;
    402       } else {
    403         OfferType = HttpOfferTypeProxyNameUri;
    404       }
    405     }
    406 
    407   } else {
    408     if (!IsProxyOffer) {
    409       OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly;
    410     } else {
    411       return EFI_DEVICE_ERROR;
    412     }
    413   }
    414 
    415   Cache4->OfferType = OfferType;
    416   return EFI_SUCCESS;
    417 }
    418 
    419 /**
    420   Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.
    421 
    422   @param[in]  Private               Pointer to HTTP boot driver private data.
    423   @param[in]  RcvdOffer             Pointer to the received offer packet.
    424 
    425 **/
    426 VOID
    427 HttpBootCacheDhcp4Offer (
    428   IN HTTP_BOOT_PRIVATE_DATA  *Private,
    429   IN EFI_DHCP4_PACKET        *RcvdOffer
    430   )
    431 {
    432   HTTP_BOOT_DHCP4_PACKET_CACHE  *Cache4;
    433   EFI_DHCP4_PACKET              *Offer;
    434   HTTP_BOOT_OFFER_TYPE          OfferType;
    435 
    436   ASSERT (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM);
    437   Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;
    438   Offer  = &Cache4->Packet.Offer;
    439 
    440   //
    441   // Cache the content of DHCPv4 packet firstly.
    442   //
    443   HttpBootCacheDhcp4Packet (Offer, RcvdOffer);
    444 
    445   //
    446   // Validate the DHCPv4 packet, and parse the options and offer type.
    447   //
    448   if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) {
    449     return;
    450   }
    451 
    452   //
    453   // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
    454   //
    455   OfferType = Cache4->OfferType;
    456   ASSERT (OfferType < HttpOfferTypeMax);
    457   ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM);
    458   Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
    459   Private->OfferCount[OfferType]++;
    460   Private->OfferNum++;
    461 }
    462 
    463 /**
    464   Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType.
    465 
    466   @param[in]  Private             Pointer to HTTP boot driver private data.
    467 
    468 **/
    469 VOID
    470 HttpBootSelectDhcpOffer (
    471   IN HTTP_BOOT_PRIVATE_DATA  *Private
    472   )
    473 {
    474   Private->SelectIndex = 0;
    475   Private->SelectProxyType = HttpOfferTypeMax;
    476 
    477   //
    478   // Priority1: HttpOfferTypeDhcpIpUri
    479   // Priority2: HttpOfferTypeDhcpNameUriDns
    480   // Priority3: HttpOfferTypeDhcpOnly + HttpOfferTypeProxyIpUri
    481   // Priority4: HttpOfferTypeDhcpDns  + HttpOfferTypeProxyIpUri
    482   // Priority5: HttpOfferTypeDhcpDns  + HttpOfferTypeProxyNameUri
    483   // Priority6: HttpOfferTypeDhcpDns  + HttpOfferTypeDhcpNameUri
    484   //
    485   if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) {
    486 
    487     Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1;
    488 
    489   } else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) {
    490 
    491     Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1;
    492 
    493   } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0 &&
    494              Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) {
    495 
    496     Private->SelectIndex     = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1;
    497     Private->SelectProxyType = HttpOfferTypeProxyIpUri;
    498 
    499   } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
    500              Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) {
    501 
    502     Private->SelectIndex     = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
    503     Private->SelectProxyType = HttpOfferTypeProxyIpUri;
    504 
    505   } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
    506              Private->OfferCount[HttpOfferTypeProxyNameUri] > 0) {
    507 
    508     Private->SelectIndex     = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
    509     Private->SelectProxyType = HttpOfferTypeProxyNameUri;
    510 
    511   } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
    512              Private->OfferCount[HttpOfferTypeDhcpNameUri] > 0) {
    513 
    514     Private->SelectIndex     = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
    515     Private->SelectProxyType = HttpOfferTypeDhcpNameUri;
    516   }
    517 }
    518 
    519 
    520 /**
    521   EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
    522   to intercept events that occurred in the configuration process.
    523 
    524   @param[in]  This              Pointer to the EFI DHCPv4 Protocol.
    525   @param[in]  Context           Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure().
    526   @param[in]  CurrentState      The current operational state of the EFI DHCPv4 Protocol driver.
    527   @param[in]  Dhcp4Event        The event that occurs in the current state, which usually means a
    528                                 state transition.
    529   @param[in]  Packet            The DHCPv4 packet that is going to be sent or already received.
    530   @param[out] NewPacket         The packet that is used to replace the above Packet.
    531 
    532   @retval EFI_SUCCESS           Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
    533   @retval EFI_NOT_READY         Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
    534                                 driver will continue to wait for more DHCPOFFER packets until the
    535                                 retry timeout expires.
    536   @retval EFI_ABORTED           Tells the EFI DHCPv4 Protocol driver to abort the current process
    537                                 and return to the Dhcp4Init or Dhcp4InitReboot state.
    538 
    539 **/
    540 EFI_STATUS
    541 EFIAPI
    542 HttpBootDhcp4CallBack (
    543   IN  EFI_DHCP4_PROTOCOL               *This,
    544   IN  VOID                             *Context,
    545   IN  EFI_DHCP4_STATE                  CurrentState,
    546   IN  EFI_DHCP4_EVENT                  Dhcp4Event,
    547   IN  EFI_DHCP4_PACKET                 *Packet            OPTIONAL,
    548   OUT EFI_DHCP4_PACKET                 **NewPacket        OPTIONAL
    549   )
    550 {
    551   HTTP_BOOT_PRIVATE_DATA               *Private;
    552   EFI_DHCP4_PACKET_OPTION              *MaxMsgSize;
    553   UINT16                               Value;
    554   EFI_STATUS                           Status;
    555 
    556   if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
    557     return EFI_SUCCESS;
    558   }
    559 
    560   Private = (HTTP_BOOT_PRIVATE_DATA *) Context;
    561 
    562   //
    563   // Override the Maximum DHCP Message Size.
    564   //
    565   MaxMsgSize = HttpBootParseDhcp4Options (
    566                  Packet->Dhcp4.Option,
    567                  GET_OPTION_BUFFER_LEN (Packet),
    568                  HTTP_BOOT_DHCP4_TAG_MAXMSG
    569                  );
    570   if (MaxMsgSize != NULL) {
    571     Value = HTONS (HTTP_BOOT_DHCP4_PACKET_MAX_SIZE);
    572     CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
    573   }
    574 
    575   Status = EFI_SUCCESS;
    576   switch (Dhcp4Event) {
    577   case Dhcp4RcvdOffer:
    578     Status = EFI_NOT_READY;
    579     if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {
    580       //
    581       // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record
    582       // the OfferIndex and OfferCount.
    583       //
    584       HttpBootCacheDhcp4Offer (Private, Packet);
    585     }
    586     break;
    587 
    588   case Dhcp4SelectOffer:
    589     //
    590     // Select offer according to the priority in UEFI spec, and record the SelectIndex
    591     // and SelectProxyType.
    592     //
    593     HttpBootSelectDhcpOffer (Private);
    594 
    595     if (Private->SelectIndex == 0) {
    596       Status = EFI_ABORTED;
    597     } else {
    598       *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;
    599     }
    600     break;
    601 
    602   default:
    603     break;
    604   }
    605 
    606   return Status;
    607 }
    608 
    609 /**
    610   This function will register the IPv4 gateway address to the network device.
    611 
    612   @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.
    613 
    614   @retval     EFI_SUCCESS         The new IP configuration has been configured successfully.
    615   @retval     Others              Failed to configure the address.
    616 
    617 **/
    618 EFI_STATUS
    619 HttpBootRegisterIp4Gateway (
    620   IN HTTP_BOOT_PRIVATE_DATA         *Private
    621   )
    622 {
    623   EFI_STATUS                      Status;
    624   EFI_IP4_CONFIG2_PROTOCOL        *Ip4Config2;
    625 
    626   ASSERT (!Private->UsingIpv6);
    627 
    628   Ip4Config2 = Private->Ip4Config2;
    629 
    630   //
    631   // Configure the gateway if valid.
    632   //
    633   if (!EFI_IP4_EQUAL (&Private->GatewayIp, &mZeroIp4Addr)) {
    634     Status = Ip4Config2->SetData (
    635                            Ip4Config2,
    636                            Ip4Config2DataTypeGateway,
    637                            sizeof (EFI_IPv4_ADDRESS),
    638                            &Private->GatewayIp
    639                            );
    640     if (EFI_ERROR (Status)) {
    641       return Status;
    642     }
    643   }
    644 
    645   return EFI_SUCCESS;
    646 }
    647 
    648 /**
    649   This function will register the default DNS addresses to the network device.
    650 
    651   @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.
    652   @param[in]  DataLength          Size of the buffer pointed to by DnsServerData in bytes.
    653   @param[in]  DnsServerData       Point a list of DNS server address in an array
    654                                   of EFI_IPv4_ADDRESS instances.
    655 
    656   @retval     EFI_SUCCESS         The DNS configuration has been configured successfully.
    657   @retval     Others              Failed to configure the address.
    658 
    659 **/
    660 EFI_STATUS
    661 HttpBootRegisterIp4Dns (
    662   IN HTTP_BOOT_PRIVATE_DATA         *Private,
    663   IN UINTN                          DataLength,
    664   IN VOID                           *DnsServerData
    665   )
    666 {
    667   EFI_IP4_CONFIG2_PROTOCOL        *Ip4Config2;
    668 
    669   ASSERT (!Private->UsingIpv6);
    670 
    671   Ip4Config2 = Private->Ip4Config2;
    672 
    673   return Ip4Config2->SetData (
    674                        Ip4Config2,
    675                        Ip4Config2DataTypeDnsServer,
    676                        DataLength,
    677                        DnsServerData
    678                        );
    679 }
    680 
    681 
    682 /**
    683   This function will switch the IP4 configuration policy to Static.
    684 
    685   @param[in]  Private             Pointer to HTTP boot driver private data.
    686 
    687   @retval     EFI_SUCCESS         The policy is already configured to static.
    688   @retval     Others              Other error as indicated..
    689 
    690 **/
    691 EFI_STATUS
    692 HttpBootSetIp4Policy (
    693   IN HTTP_BOOT_PRIVATE_DATA         *Private
    694   )
    695 {
    696   EFI_IP4_CONFIG2_POLICY          Policy;
    697   EFI_STATUS                      Status;
    698   EFI_IP4_CONFIG2_PROTOCOL        *Ip4Config2;
    699   UINTN                           DataSize;
    700 
    701   Ip4Config2 = Private->Ip4Config2;
    702 
    703   DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
    704   Status = Ip4Config2->GetData (
    705                          Ip4Config2,
    706                          Ip4Config2DataTypePolicy,
    707                          &DataSize,
    708                          &Policy
    709                          );
    710   if (EFI_ERROR (Status)) {
    711     return Status;
    712   }
    713 
    714   if (Policy != Ip4Config2PolicyStatic) {
    715     Policy = Ip4Config2PolicyStatic;
    716     Status= Ip4Config2->SetData (
    717                           Ip4Config2,
    718                           Ip4Config2DataTypePolicy,
    719                           sizeof (EFI_IP4_CONFIG2_POLICY),
    720                           &Policy
    721                           );
    722     if (EFI_ERROR (Status)) {
    723       return Status;
    724     }
    725   }
    726 
    727   return EFI_SUCCESS;
    728 }
    729 
    730 /**
    731   Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information.
    732 
    733   @param[in]  Private           Pointer to HTTP boot driver private data.
    734 
    735   @retval EFI_SUCCESS           The D.O.R.A process successfully finished.
    736   @retval Others                Failed to finish the D.O.R.A process.
    737 
    738 **/
    739 EFI_STATUS
    740 HttpBootDhcp4Dora (
    741   IN HTTP_BOOT_PRIVATE_DATA         *Private
    742   )
    743 {
    744   EFI_DHCP4_PROTOCOL           *Dhcp4;
    745   UINT32                       OptCount;
    746   EFI_DHCP4_PACKET_OPTION      *OptList[HTTP_BOOT_DHCP4_OPTION_MAX_NUM];
    747   UINT8                        Buffer[HTTP_BOOT_DHCP4_OPTION_MAX_SIZE];
    748   EFI_DHCP4_CONFIG_DATA        Config;
    749   EFI_STATUS                   Status;
    750   EFI_DHCP4_MODE_DATA          Mode;
    751 
    752   Dhcp4 = Private->Dhcp4;
    753   ASSERT (Dhcp4 != NULL);
    754 
    755   Status = HttpBootSetIp4Policy (Private);
    756   if (EFI_ERROR (Status)) {
    757     return Status;
    758   }
    759 
    760   //
    761   // Build option list for the request packet.
    762   //
    763   OptCount = HttpBootBuildDhcp4Options (Private, OptList, Buffer);
    764   ASSERT (OptCount > 0);
    765 
    766   ZeroMem (&Config, sizeof(Config));
    767   Config.OptionCount      = OptCount;
    768   Config.OptionList       = OptList;
    769   Config.Dhcp4Callback    = HttpBootDhcp4CallBack;
    770   Config.CallbackContext  = Private;
    771   Config.DiscoverTryCount = HTTP_BOOT_DHCP_RETRIES;
    772   Config.DiscoverTimeout  = mHttpDhcpTimeout;
    773 
    774   //
    775   // Configure the DHCPv4 instance for HTTP boot.
    776   //
    777   Status = Dhcp4->Configure (Dhcp4, &Config);
    778   if (EFI_ERROR (Status)) {
    779     goto ON_EXIT;
    780   }
    781 
    782   //
    783   // Initialize the record fields for DHCPv4 offer in private data.
    784   //
    785   Private->OfferNum = 0;
    786   ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
    787   ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
    788 
    789   //
    790   // Start DHCPv4 D.O.R.A. process to acquire IPv4 address.
    791   //
    792   Status = Dhcp4->Start (Dhcp4, NULL);
    793   if (EFI_ERROR (Status)) {
    794     goto ON_EXIT;
    795   }
    796 
    797   //
    798   // Get the acquired IPv4 address and store them.
    799   //
    800   Status = Dhcp4->GetModeData (Dhcp4, &Mode);
    801   if (EFI_ERROR (Status)) {
    802     goto ON_EXIT;
    803   }
    804 
    805   ASSERT (Mode.State == Dhcp4Bound);
    806   CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
    807   CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
    808   CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
    809 
    810   Status = HttpBootRegisterIp4Gateway (Private);
    811   if (EFI_ERROR (Status)) {
    812     goto ON_EXIT;
    813   }
    814 
    815   AsciiPrint ("\n  Station IP address is ");
    816   HttpBootShowIp4Addr (&Private->StationIp.v4);
    817   AsciiPrint ("\n");
    818 
    819 ON_EXIT:
    820   if (EFI_ERROR (Status)) {
    821     Dhcp4->Stop (Dhcp4);
    822     Dhcp4->Configure (Dhcp4, NULL);
    823   } else {
    824     ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));
    825     Dhcp4->Configure (Dhcp4, &Config);
    826   }
    827 
    828   return Status;
    829 }
    830