1/** @file 2 Functions implementation related with DHCPv4 for HTTP boot driver. 3 4Copyright (c) 2015 - 2016, 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// This is a map from the interested DHCP4 option tags' index to the tag value. 19// 20UINT8 mInterestedDhcp4Tags[HTTP_BOOT_DHCP4_TAG_INDEX_MAX] = { 21 DHCP4_TAG_BOOTFILE_LEN, 22 DHCP4_TAG_OVERLOAD, 23 DHCP4_TAG_MSG_TYPE, 24 DHCP4_TAG_SERVER_ID, 25 DHCP4_TAG_VENDOR_CLASS_ID, 26 DHCP4_TAG_BOOTFILE, 27 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// 33UINT32 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**/ 45UINT32 46HttpBootBuildDhcp4Options ( 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 = 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] = DHCP4_TAG_NETMASK; 66 OptEnt.Para->ParaList[1] = DHCP4_TAG_TIME_OFFSET; 67 OptEnt.Para->ParaList[2] = DHCP4_TAG_ROUTER; 68 OptEnt.Para->ParaList[3] = DHCP4_TAG_TIME_SERVER; 69 OptEnt.Para->ParaList[4] = DHCP4_TAG_NAME_SERVER; 70 OptEnt.Para->ParaList[5] = DHCP4_TAG_DNS_SERVER; 71 OptEnt.Para->ParaList[6] = DHCP4_TAG_HOSTNAME; 72 OptEnt.Para->ParaList[7] = DHCP4_TAG_BOOTFILE_LEN; 73 OptEnt.Para->ParaList[8] = DHCP4_TAG_DOMAINNAME; 74 OptEnt.Para->ParaList[9] = DHCP4_TAG_ROOTPATH; 75 OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH; 76 OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU; 77 OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL; 78 OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST; 79 OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN; 80 OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER; 81 OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER; 82 OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR; 83 OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP; 84 OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE; 85 OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID; 86 OptEnt.Para->ParaList[21] = DHCP4_TAG_T1; 87 OptEnt.Para->ParaList[22] = DHCP4_TAG_T2; 88 OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID; 89 OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE; 90 OptEnt.Para->ParaList[26] = 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 = 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 = 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 = 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 = DHCP4_TAG_VENDOR_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**/ 180EFI_DHCP4_PACKET_OPTION * 181HttpBootParseDhcp4Options ( 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 != 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 == 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 @retval EFI_SUCCESS Packet is copied. 224 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. 225 226**/ 227EFI_STATUS 228HttpBootCacheDhcp4Packet ( 229 IN EFI_DHCP4_PACKET *Dst, 230 IN EFI_DHCP4_PACKET *Src 231 ) 232{ 233 if (Dst->Size < Src->Length) { 234 return EFI_BUFFER_TOO_SMALL; 235 } 236 237 CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); 238 Dst->Length = Src->Length; 239 240 return EFI_SUCCESS; 241} 242 243/** 244 Parse the cached DHCPv4 packet, including all the options. 245 246 @param[in] Cache4 Pointer to cached DHCPv4 packet. 247 248 @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. 249 @retval EFI_DEVICE_ERROR Failed to parse an invalid packet. 250 251**/ 252EFI_STATUS 253HttpBootParseDhcp4Packet ( 254 IN HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4 255 ) 256{ 257 EFI_DHCP4_PACKET *Offer; 258 EFI_DHCP4_PACKET_OPTION **Options; 259 UINTN Index; 260 EFI_DHCP4_PACKET_OPTION *Option; 261 BOOLEAN IsProxyOffer; 262 BOOLEAN IsHttpOffer; 263 BOOLEAN IsDnsOffer; 264 BOOLEAN IpExpressedUri; 265 UINT8 *Ptr8; 266 EFI_STATUS Status; 267 HTTP_BOOT_OFFER_TYPE OfferType; 268 EFI_IPv4_ADDRESS IpAddr; 269 BOOLEAN FileFieldOverloaded; 270 271 IsDnsOffer = FALSE; 272 IpExpressedUri = FALSE; 273 IsProxyOffer = FALSE; 274 IsHttpOffer = FALSE; 275 FileFieldOverloaded = FALSE; 276 277 ZeroMem (Cache4->OptList, sizeof (Cache4->OptList)); 278 279 Offer = &Cache4->Packet.Offer; 280 Options = Cache4->OptList; 281 282 // 283 // Parse DHCPv4 options in this offer, and store the pointers. 284 // First, try to parse DHCPv4 options from the DHCP optional parameters field. 285 // 286 for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { 287 Options[Index] = HttpBootParseDhcp4Options ( 288 Offer->Dhcp4.Option, 289 GET_OPTION_BUFFER_LEN (Offer), 290 mInterestedDhcp4Tags[Index] 291 ); 292 } 293 // 294 // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132. 295 // If yes, try to parse options from the BootFileName field, then ServerName field. 296 // 297 Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD]; 298 if (Option != NULL) { 299 if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_FILE) != 0) { 300 FileFieldOverloaded = TRUE; 301 for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { 302 if (Options[Index] == NULL) { 303 Options[Index] = HttpBootParseDhcp4Options ( 304 (UINT8 *) Offer->Dhcp4.Header.BootFileName, 305 sizeof (Offer->Dhcp4.Header.BootFileName), 306 mInterestedDhcp4Tags[Index] 307 ); 308 } 309 } 310 } 311 if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME) != 0) { 312 for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { 313 if (Options[Index] == NULL) { 314 Options[Index] = HttpBootParseDhcp4Options ( 315 (UINT8 *) Offer->Dhcp4.Header.ServerName, 316 sizeof (Offer->Dhcp4.Header.ServerName), 317 mInterestedDhcp4Tags[Index] 318 ); 319 } 320 } 321 } 322 } 323 324 // 325 // The offer with "yiaddr" is a proxy offer. 326 // 327 if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { 328 IsProxyOffer = TRUE; 329 } 330 331 // 332 // The offer with "HTTPClient" is a Http offer. 333 // 334 Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID]; 335 if ((Option != NULL) && (Option->Length >= 9) && 336 (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) { 337 IsHttpOffer = TRUE; 338 } 339 340 // 341 // The offer with Domain Server is a DNS offer. 342 // 343 Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER]; 344 if (Option != NULL) { 345 IsDnsOffer = TRUE; 346 } 347 348 // 349 // Parse boot file name: 350 // Boot URI information is provided thru 'file' field in DHCP Header or option 67. 351 // According to RFC 2132, boot file name should be read from DHCP option 67 (bootfile name) if present. 352 // Otherwise, read from boot file field in DHCP header. 353 // 354 if (Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { 355 // 356 // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null 357 // terminated string. So force to append null terminated character at the end of string. 358 // 359 Ptr8 = (UINT8*)&Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data[0]; 360 Ptr8 += Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Length; 361 if (*(Ptr8 - 1) != '\0') { 362 *Ptr8 = '\0'; 363 } 364 } else if (!FileFieldOverloaded && Offer->Dhcp4.Header.BootFileName[0] != 0) { 365 // 366 // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it. 367 // Do not count dhcp option header here, or else will destroy the serverhostname. 368 // 369 Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) 370 (&Offer->Dhcp4.Header.BootFileName[0] - 371 OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0])); 372 } 373 374 // 375 // Http offer must have a boot URI. 376 // 377 if (IsHttpOffer && Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { 378 return EFI_DEVICE_ERROR; 379 } 380 381 // 382 // Try to retrieve the IP of HTTP server from URI. 383 // 384 if (IsHttpOffer) { 385 Status = HttpParseUrl ( 386 (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, 387 (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data), 388 FALSE, 389 &Cache4->UriParser 390 ); 391 if (EFI_ERROR (Status)) { 392 return EFI_DEVICE_ERROR; 393 } 394 395 Status = HttpUrlGetIp4 ( 396 (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, 397 Cache4->UriParser, 398 &IpAddr 399 ); 400 IpExpressedUri = !EFI_ERROR (Status); 401 } 402 403 // 404 // Determine offer type of the DHCPv4 packet. 405 // 406 if (IsHttpOffer) { 407 if (IpExpressedUri) { 408 if (IsProxyOffer) { 409 OfferType = HttpOfferTypeProxyIpUri; 410 } else { 411 OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : HttpOfferTypeDhcpIpUri; 412 } 413 } else { 414 if (!IsProxyOffer) { 415 OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri; 416 } else { 417 OfferType = HttpOfferTypeProxyNameUri; 418 } 419 } 420 421 } else { 422 if (!IsProxyOffer) { 423 OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly; 424 } else { 425 return EFI_DEVICE_ERROR; 426 } 427 } 428 429 Cache4->OfferType = OfferType; 430 return EFI_SUCCESS; 431} 432 433/** 434 Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount. 435 436 @param[in] Private Pointer to HTTP boot driver private data. 437 @param[in] RcvdOffer Pointer to the received offer packet. 438 439 @retval EFI_SUCCESS Cache and parse the packet successfully. 440 @retval Others Operation failed. 441**/ 442EFI_STATUS 443HttpBootCacheDhcp4Offer ( 444 IN HTTP_BOOT_PRIVATE_DATA *Private, 445 IN EFI_DHCP4_PACKET *RcvdOffer 446 ) 447{ 448 HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4; 449 EFI_DHCP4_PACKET *Offer; 450 HTTP_BOOT_OFFER_TYPE OfferType; 451 EFI_STATUS Status; 452 453 ASSERT (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM); 454 Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4; 455 Offer = &Cache4->Packet.Offer; 456 457 // 458 // Cache the content of DHCPv4 packet firstly. 459 // 460 Status = HttpBootCacheDhcp4Packet (Offer, RcvdOffer); 461 if (EFI_ERROR (Status)) { 462 return Status; 463 } 464 465 // 466 // Validate the DHCPv4 packet, and parse the options and offer type. 467 // 468 if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) { 469 return EFI_ABORTED; 470 } 471 472 // 473 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. 474 // 475 OfferType = Cache4->OfferType; 476 ASSERT (OfferType < HttpOfferTypeMax); 477 ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM); 478 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; 479 Private->OfferCount[OfferType]++; 480 Private->OfferNum++; 481 482 return EFI_SUCCESS; 483} 484 485/** 486 Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType. 487 488 @param[in] Private Pointer to HTTP boot driver private data. 489 490**/ 491VOID 492HttpBootSelectDhcpOffer ( 493 IN HTTP_BOOT_PRIVATE_DATA *Private 494 ) 495{ 496 Private->SelectIndex = 0; 497 Private->SelectProxyType = HttpOfferTypeMax; 498 499 if (Private->FilePathUri != NULL) { 500 // 501 // We are in home environment, the URI is already specified. 502 // Just need to choose a DHCP offer. 503 // The offer with DNS server address takes priority here. 504 // 505 if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0) { 506 507 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; 508 509 } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) { 510 511 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1; 512 513 } else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) { 514 515 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1; 516 517 } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0) { 518 519 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1; 520 521 } else if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) { 522 523 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1; 524 } 525 526 } else { 527 // 528 // We are in corporate environment. 529 // 530 // Priority1: HttpOfferTypeDhcpIpUri or HttpOfferTypeDhcpIpUriDns 531 // Priority2: HttpOfferTypeDhcpNameUriDns 532 // Priority3: HttpOfferTypeDhcpOnly + HttpOfferTypeProxyIpUri 533 // Priority4: HttpOfferTypeDhcpDns + HttpOfferTypeProxyIpUri 534 // Priority5: HttpOfferTypeDhcpDns + HttpOfferTypeProxyNameUri 535 // Priority6: HttpOfferTypeDhcpDns + HttpOfferTypeDhcpNameUri 536 // 537 if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) { 538 539 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1; 540 541 } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) { 542 543 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1; 544 545 }else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) { 546 547 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1; 548 549 } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0 && 550 Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) { 551 552 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1; 553 Private->SelectProxyType = HttpOfferTypeProxyIpUri; 554 555 } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && 556 Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) { 557 558 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; 559 Private->SelectProxyType = HttpOfferTypeProxyIpUri; 560 561 } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && 562 Private->OfferCount[HttpOfferTypeProxyNameUri] > 0) { 563 564 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; 565 Private->SelectProxyType = HttpOfferTypeProxyNameUri; 566 567 } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && 568 Private->OfferCount[HttpOfferTypeDhcpNameUri] > 0) { 569 570 Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; 571 Private->SelectProxyType = HttpOfferTypeDhcpNameUri; 572 } 573 } 574} 575 576 577/** 578 EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver 579 to intercept events that occurred in the configuration process. 580 581 @param[in] This Pointer to the EFI DHCPv4 Protocol. 582 @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure(). 583 @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver. 584 @param[in] Dhcp4Event The event that occurs in the current state, which usually means a 585 state transition. 586 @param[in] Packet The DHCPv4 packet that is going to be sent or already received. 587 @param[out] NewPacket The packet that is used to replace the above Packet. 588 589 @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. 590 @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol 591 driver will continue to wait for more DHCPOFFER packets until the 592 retry timeout expires. 593 @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process 594 and return to the Dhcp4Init or Dhcp4InitReboot state. 595 596**/ 597EFI_STATUS 598EFIAPI 599HttpBootDhcp4CallBack ( 600 IN EFI_DHCP4_PROTOCOL *This, 601 IN VOID *Context, 602 IN EFI_DHCP4_STATE CurrentState, 603 IN EFI_DHCP4_EVENT Dhcp4Event, 604 IN EFI_DHCP4_PACKET *Packet OPTIONAL, 605 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL 606 ) 607{ 608 HTTP_BOOT_PRIVATE_DATA *Private; 609 EFI_DHCP4_PACKET_OPTION *MaxMsgSize; 610 UINT16 Value; 611 EFI_STATUS Status; 612 613 if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) { 614 return EFI_SUCCESS; 615 } 616 617 Private = (HTTP_BOOT_PRIVATE_DATA *) Context; 618 619 // 620 // Override the Maximum DHCP Message Size. 621 // 622 MaxMsgSize = HttpBootParseDhcp4Options ( 623 Packet->Dhcp4.Option, 624 GET_OPTION_BUFFER_LEN (Packet), 625 DHCP4_TAG_MAXMSG 626 ); 627 if (MaxMsgSize != NULL) { 628 Value = HTONS (HTTP_BOOT_DHCP4_PACKET_MAX_SIZE); 629 CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); 630 } 631 632 Status = EFI_SUCCESS; 633 switch (Dhcp4Event) { 634 case Dhcp4RcvdOffer: 635 Status = EFI_NOT_READY; 636 if (Packet->Length > HTTP_BOOT_DHCP4_PACKET_MAX_SIZE) { 637 // 638 // Ignore the incoming packets which exceed the maximum length. 639 // 640 break; 641 } 642 if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { 643 // 644 // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record 645 // the OfferIndex and OfferCount. 646 // If error happens, just ignore this packet and continue to wait more offer. 647 // 648 HttpBootCacheDhcp4Offer (Private, Packet); 649 } 650 break; 651 652 case Dhcp4SelectOffer: 653 // 654 // Select offer according to the priority in UEFI spec, and record the SelectIndex 655 // and SelectProxyType. 656 // 657 HttpBootSelectDhcpOffer (Private); 658 659 if (Private->SelectIndex == 0) { 660 Status = EFI_ABORTED; 661 } else { 662 *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer; 663 } 664 break; 665 666 default: 667 break; 668 } 669 670 return Status; 671} 672 673/** 674 This function will register the IPv4 gateway address to the network device. 675 676 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. 677 678 @retval EFI_SUCCESS The new IP configuration has been configured successfully. 679 @retval Others Failed to configure the address. 680 681**/ 682EFI_STATUS 683HttpBootRegisterIp4Gateway ( 684 IN HTTP_BOOT_PRIVATE_DATA *Private 685 ) 686{ 687 EFI_STATUS Status; 688 EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; 689 690 ASSERT (!Private->UsingIpv6); 691 692 Ip4Config2 = Private->Ip4Config2; 693 694 // 695 // Configure the gateway if valid. 696 // 697 if (!EFI_IP4_EQUAL (&Private->GatewayIp, &mZeroIp4Addr)) { 698 Status = Ip4Config2->SetData ( 699 Ip4Config2, 700 Ip4Config2DataTypeGateway, 701 sizeof (EFI_IPv4_ADDRESS), 702 &Private->GatewayIp 703 ); 704 if (EFI_ERROR (Status)) { 705 return Status; 706 } 707 } 708 709 return EFI_SUCCESS; 710} 711 712/** 713 This function will register the default DNS addresses to the network device. 714 715 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. 716 @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes. 717 @param[in] DnsServerData Point a list of DNS server address in an array 718 of EFI_IPv4_ADDRESS instances. 719 720 @retval EFI_SUCCESS The DNS configuration has been configured successfully. 721 @retval Others Failed to configure the address. 722 723**/ 724EFI_STATUS 725HttpBootRegisterIp4Dns ( 726 IN HTTP_BOOT_PRIVATE_DATA *Private, 727 IN UINTN DataLength, 728 IN VOID *DnsServerData 729 ) 730{ 731 EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; 732 733 ASSERT (!Private->UsingIpv6); 734 735 Ip4Config2 = Private->Ip4Config2; 736 737 return Ip4Config2->SetData ( 738 Ip4Config2, 739 Ip4Config2DataTypeDnsServer, 740 DataLength, 741 DnsServerData 742 ); 743} 744 745 746/** 747 This function will switch the IP4 configuration policy to Static. 748 749 @param[in] Private Pointer to HTTP boot driver private data. 750 751 @retval EFI_SUCCESS The policy is already configured to static. 752 @retval Others Other error as indicated.. 753 754**/ 755EFI_STATUS 756HttpBootSetIp4Policy ( 757 IN HTTP_BOOT_PRIVATE_DATA *Private 758 ) 759{ 760 EFI_IP4_CONFIG2_POLICY Policy; 761 EFI_STATUS Status; 762 EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; 763 UINTN DataSize; 764 765 Ip4Config2 = Private->Ip4Config2; 766 767 DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); 768 Status = Ip4Config2->GetData ( 769 Ip4Config2, 770 Ip4Config2DataTypePolicy, 771 &DataSize, 772 &Policy 773 ); 774 if (EFI_ERROR (Status)) { 775 return Status; 776 } 777 778 if (Policy != Ip4Config2PolicyStatic) { 779 Policy = Ip4Config2PolicyStatic; 780 Status= Ip4Config2->SetData ( 781 Ip4Config2, 782 Ip4Config2DataTypePolicy, 783 sizeof (EFI_IP4_CONFIG2_POLICY), 784 &Policy 785 ); 786 if (EFI_ERROR (Status)) { 787 return Status; 788 } 789 } 790 791 return EFI_SUCCESS; 792} 793 794/** 795 Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information. 796 797 @param[in] Private Pointer to HTTP boot driver private data. 798 799 @retval EFI_SUCCESS The D.O.R.A process successfully finished. 800 @retval Others Failed to finish the D.O.R.A process. 801 802**/ 803EFI_STATUS 804HttpBootDhcp4Dora ( 805 IN HTTP_BOOT_PRIVATE_DATA *Private 806 ) 807{ 808 EFI_DHCP4_PROTOCOL *Dhcp4; 809 UINT32 OptCount; 810 EFI_DHCP4_PACKET_OPTION *OptList[HTTP_BOOT_DHCP4_OPTION_MAX_NUM]; 811 UINT8 Buffer[HTTP_BOOT_DHCP4_OPTION_MAX_SIZE]; 812 EFI_DHCP4_CONFIG_DATA Config; 813 EFI_STATUS Status; 814 EFI_DHCP4_MODE_DATA Mode; 815 816 Dhcp4 = Private->Dhcp4; 817 ASSERT (Dhcp4 != NULL); 818 819 Status = HttpBootSetIp4Policy (Private); 820 if (EFI_ERROR (Status)) { 821 return Status; 822 } 823 824 // 825 // Build option list for the request packet. 826 // 827 OptCount = HttpBootBuildDhcp4Options (Private, OptList, Buffer); 828 ASSERT (OptCount > 0); 829 830 ZeroMem (&Config, sizeof(Config)); 831 Config.OptionCount = OptCount; 832 Config.OptionList = OptList; 833 Config.Dhcp4Callback = HttpBootDhcp4CallBack; 834 Config.CallbackContext = Private; 835 Config.DiscoverTryCount = HTTP_BOOT_DHCP_RETRIES; 836 Config.DiscoverTimeout = mHttpDhcpTimeout; 837 838 // 839 // Configure the DHCPv4 instance for HTTP boot. 840 // 841 Status = Dhcp4->Configure (Dhcp4, &Config); 842 if (EFI_ERROR (Status)) { 843 goto ON_EXIT; 844 } 845 846 // 847 // Initialize the record fields for DHCPv4 offer in private data. 848 // 849 Private->OfferNum = 0; 850 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); 851 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); 852 853 // 854 // Start DHCPv4 D.O.R.A. process to acquire IPv4 address. 855 // 856 Status = Dhcp4->Start (Dhcp4, NULL); 857 if (EFI_ERROR (Status)) { 858 goto ON_EXIT; 859 } 860 861 // 862 // Get the acquired IPv4 address and store them. 863 // 864 Status = Dhcp4->GetModeData (Dhcp4, &Mode); 865 if (EFI_ERROR (Status)) { 866 goto ON_EXIT; 867 } 868 869 ASSERT (Mode.State == Dhcp4Bound); 870 CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); 871 CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); 872 CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); 873 874 Status = HttpBootRegisterIp4Gateway (Private); 875 if (EFI_ERROR (Status)) { 876 goto ON_EXIT; 877 } 878 879 AsciiPrint ("\n Station IP address is "); 880 HttpBootShowIp4Addr (&Private->StationIp.v4); 881 AsciiPrint ("\n"); 882 883ON_EXIT: 884 if (EFI_ERROR (Status)) { 885 Dhcp4->Stop (Dhcp4); 886 Dhcp4->Configure (Dhcp4, NULL); 887 } else { 888 ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); 889 Dhcp4->Configure (Dhcp4, &Config); 890 } 891 892 return Status; 893} 894