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
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    = 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 *
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 != 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
HttpBootCacheDhcp4Packet(IN EFI_DHCP4_PACKET * Dst,IN EFI_DHCP4_PACKET * Src)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
HttpBootParseDhcp4Packet(IN HTTP_BOOT_DHCP4_PACKET_CACHE * Cache4)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
HttpBootCacheDhcp4Offer(IN HTTP_BOOT_PRIVATE_DATA * Private,IN EFI_DHCP4_PACKET * RcvdOffer)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
HttpBootSelectDhcpOffer(IN HTTP_BOOT_PRIVATE_DATA * Private)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
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)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
HttpBootRegisterIp4Gateway(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootRegisterIp4Dns(IN HTTP_BOOT_PRIVATE_DATA * Private,IN UINTN DataLength,IN VOID * DnsServerData)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
HttpBootSetIp4Policy(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootDhcp4Dora(IN HTTP_BOOT_PRIVATE_DATA * Private)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