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