• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Functions implementation related with DHCPv4 for HTTP boot driver.
3 
4 Copyright (c) 2015 - 2016, 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   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 //
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
HttpBootBuildDhcp4Options(IN HTTP_BOOT_PRIVATE_DATA * Private,OUT EFI_DHCP4_PACKET_OPTION ** OptList,IN UINT8 * Buffer)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    = 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 **/
180 EFI_DHCP4_PACKET_OPTION *
HttpBootParseDhcp4Options(IN UINT8 * Buffer,IN UINT32 Length,IN UINT8 OptTag)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 != 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 **/
227 EFI_STATUS
HttpBootCacheDhcp4Packet(IN EFI_DHCP4_PACKET * Dst,IN EFI_DHCP4_PACKET * Src)228 HttpBootCacheDhcp4Packet (
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 **/
252 EFI_STATUS
HttpBootParseDhcp4Packet(IN HTTP_BOOT_DHCP4_PACKET_CACHE * Cache4)253 HttpBootParseDhcp4Packet (
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 **/
442 EFI_STATUS
HttpBootCacheDhcp4Offer(IN HTTP_BOOT_PRIVATE_DATA * Private,IN EFI_DHCP4_PACKET * RcvdOffer)443 HttpBootCacheDhcp4Offer (
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 **/
491 VOID
HttpBootSelectDhcpOffer(IN HTTP_BOOT_PRIVATE_DATA * Private)492 HttpBootSelectDhcpOffer (
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 **/
597 EFI_STATUS
598 EFIAPI
HttpBootDhcp4CallBack(IN EFI_DHCP4_PROTOCOL * This,IN VOID * Context,IN EFI_DHCP4_STATE CurrentState,IN EFI_DHCP4_EVENT Dhcp4Event,IN EFI_DHCP4_PACKET * Packet OPTIONAL,OUT EFI_DHCP4_PACKET ** NewPacket OPTIONAL)599 HttpBootDhcp4CallBack (
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 **/
682 EFI_STATUS
HttpBootRegisterIp4Gateway(IN HTTP_BOOT_PRIVATE_DATA * Private)683 HttpBootRegisterIp4Gateway (
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 **/
724 EFI_STATUS
HttpBootRegisterIp4Dns(IN HTTP_BOOT_PRIVATE_DATA * Private,IN UINTN DataLength,IN VOID * DnsServerData)725 HttpBootRegisterIp4Dns (
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 **/
755 EFI_STATUS
HttpBootSetIp4Policy(IN HTTP_BOOT_PRIVATE_DATA * Private)756 HttpBootSetIp4Policy (
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 **/
803 EFI_STATUS
HttpBootDhcp4Dora(IN HTTP_BOOT_PRIVATE_DATA * Private)804 HttpBootDhcp4Dora (
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 
883 ON_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