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