]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/HttpBootDxe/HttpBootDhcp6.c
BaseTools/Capsule: Do not support -o with --dump-info
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootDhcp6.c
CommitLineData
b659408b
ZL
1/** @file\r
2 Functions implementation related with DHCPv6 for HTTP boot driver.\r
3\r
f75a7f56
LG
4Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
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
b659408b 7The full text of the license may be found at\r
f75a7f56
LG
8http://opensource.org/licenses/bsd-license.php.\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
b659408b
ZL
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 Build the options buffer for the DHCPv6 request packet.\r
19\r
20 @param[in] Private The pointer to HTTP BOOT driver private data.\r
21 @param[out] OptList The pointer to the option pointer array.\r
22 @param[in] Buffer The pointer to the buffer to contain the option list.\r
23\r
24 @return Index The count of the built-in options.\r
25\r
26**/\r
27UINT32\r
28HttpBootBuildDhcp6Options (\r
29 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
30 OUT EFI_DHCP6_PACKET_OPTION **OptList,\r
31 IN UINT8 *Buffer\r
32 )\r
33{\r
34 HTTP_BOOT_DHCP6_OPTION_ENTRY OptEnt;\r
35 UINT16 Value;\r
36 UINT32 Index;\r
37\r
38 Index = 0;\r
39 OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
40\r
41 //\r
42 // Append client option request option\r
43 //\r
142c00c3 44 OptList[Index]->OpCode = HTONS (DHCP6_OPT_ORO);\r
b659408b
ZL
45 OptList[Index]->OpLen = HTONS (8);\r
46 OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data;\r
142c00c3
ZL
47 OptEnt.Oro->OpCode[0] = HTONS(DHCP6_OPT_BOOT_FILE_URL);\r
48 OptEnt.Oro->OpCode[1] = HTONS(DHCP6_OPT_BOOT_FILE_PARAM);\r
49 OptEnt.Oro->OpCode[2] = HTONS(DHCP6_OPT_DNS_SERVERS);\r
50 OptEnt.Oro->OpCode[3] = HTONS(DHCP6_OPT_VENDOR_CLASS);\r
b659408b
ZL
51 Index++;\r
52 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
53\r
54 //\r
55 // Append client network device interface option\r
56 //\r
142c00c3 57 OptList[Index]->OpCode = HTONS (DHCP6_OPT_UNDI);\r
b659408b
ZL
58 OptList[Index]->OpLen = HTONS ((UINT16)3);\r
59 OptEnt.Undi = (HTTP_BOOT_DHCP6_OPTION_UNDI *) OptList[Index]->Data;\r
60\r
61 if (Private->Nii != NULL) {\r
62 OptEnt.Undi->Type = Private->Nii->Type;\r
63 OptEnt.Undi->MajorVer = Private->Nii->MajorVer;\r
64 OptEnt.Undi->MinorVer = Private->Nii->MinorVer;\r
65 } else {\r
66 OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;\r
67 OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;\r
68 OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;\r
69 }\r
70\r
71 Index++;\r
72 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
73\r
74 //\r
75 // Append client system architecture option\r
76 //\r
142c00c3 77 OptList[Index]->OpCode = HTONS (DHCP6_OPT_ARCH);\r
b659408b
ZL
78 OptList[Index]->OpLen = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_ARCH));\r
79 OptEnt.Arch = (HTTP_BOOT_DHCP6_OPTION_ARCH *) OptList[Index]->Data;\r
80 Value = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE);\r
81 CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));\r
82 Index++;\r
83 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
84\r
85 //\r
86 // Append vendor class identify option.\r
87 //\r
142c00c3 88 OptList[Index]->OpCode = HTONS (DHCP6_OPT_VENDOR_CLASS);\r
b659408b
ZL
89 OptList[Index]->OpLen = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS));\r
90 OptEnt.VendorClass = (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;\r
91 OptEnt.VendorClass->Vendor = HTONL (HTTP_BOOT_DHCP6_ENTERPRISE_NUM);\r
92 OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (HTTP_BOOT_CLASS_ID));\r
93 CopyMem (\r
94 &OptEnt.VendorClass->ClassId,\r
95 DEFAULT_CLASS_ID_DATA,\r
96 sizeof (HTTP_BOOT_CLASS_ID)\r
97 );\r
98 HttpBootUintnToAscDecWithFormat (\r
99 EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE,\r
100 OptEnt.VendorClass->ClassId.ArchitectureType,\r
101 sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)\r
102 );\r
103\r
104 if (Private->Nii != NULL) {\r
105 CopyMem (\r
106 OptEnt.VendorClass->ClassId.InterfaceName,\r
107 Private->Nii->StringId,\r
108 sizeof (OptEnt.VendorClass->ClassId.InterfaceName)\r
109 );\r
110 HttpBootUintnToAscDecWithFormat (\r
111 Private->Nii->MajorVer,\r
112 OptEnt.VendorClass->ClassId.UndiMajor,\r
113 sizeof (OptEnt.VendorClass->ClassId.UndiMajor)\r
114 );\r
115 HttpBootUintnToAscDecWithFormat (\r
116 Private->Nii->MinorVer,\r
117 OptEnt.VendorClass->ClassId.UndiMinor,\r
118 sizeof (OptEnt.VendorClass->ClassId.UndiMinor)\r
119 );\r
120 }\r
121\r
122 Index++;\r
123\r
124 return Index;\r
125}\r
126\r
127/**\r
128 Parse out a DHCPv6 option by OptTag, and find the position in buffer.\r
129\r
130 @param[in] Buffer The pointer to the option buffer.\r
131 @param[in] Length Length of the option buffer.\r
132 @param[in] OptTag The required option tag.\r
133\r
134 @retval NULL Failed to parse the required option.\r
135 @retval Others The postion of the required option in buffer.\r
136\r
137**/\r
138EFI_DHCP6_PACKET_OPTION *\r
139HttpBootParseDhcp6Options (\r
140 IN UINT8 *Buffer,\r
141 IN UINT32 Length,\r
142 IN UINT16 OptTag\r
143 )\r
144{\r
145 EFI_DHCP6_PACKET_OPTION *Option;\r
146 UINT32 Offset;\r
147\r
148 Option = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
149 Offset = 0;\r
150\r
151 //\r
152 // OpLen and OpCode here are both stored in network order.\r
153 //\r
154 while (Offset < Length) {\r
155\r
156 if (NTOHS (Option->OpCode) == OptTag) {\r
157\r
158 return Option;\r
159 }\r
160\r
161 Offset += (NTOHS(Option->OpLen) + 4);\r
162 Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);\r
163 }\r
164\r
165 return NULL;\r
166\r
167}\r
168\r
169/**\r
170 Parse the cached DHCPv6 packet, including all the options.\r
171\r
172 @param[in] Cache6 The pointer to a cached DHCPv6 packet.\r
173\r
174 @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.\r
175 @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.\r
176\r
177**/\r
178EFI_STATUS\r
179HttpBootParseDhcp6Packet (\r
180 IN HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6\r
181 )\r
182{\r
183 EFI_DHCP6_PACKET *Offer;\r
184 EFI_DHCP6_PACKET_OPTION **Options;\r
185 EFI_DHCP6_PACKET_OPTION *Option;\r
186 HTTP_BOOT_OFFER_TYPE OfferType;\r
187 EFI_IPv6_ADDRESS IpAddr;\r
188 BOOLEAN IsProxyOffer;\r
189 BOOLEAN IsHttpOffer;\r
190 BOOLEAN IsDnsOffer;\r
191 BOOLEAN IpExpressedUri;\r
192 EFI_STATUS Status;\r
193 UINT32 Offset;\r
194 UINT32 Length;\r
f75a7f56 195\r
b659408b
ZL
196 IsDnsOffer = FALSE;\r
197 IpExpressedUri = FALSE;\r
198 IsProxyOffer = TRUE;\r
199 IsHttpOffer = FALSE;\r
200 Offer = &Cache6->Packet.Offer;\r
201 Options = Cache6->OptList;\r
f75a7f56 202\r
b659408b
ZL
203 ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));\r
204\r
205 Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);\r
206 Offset = 0;\r
207 Length = GET_DHCP6_OPTION_SIZE (Offer);\r
208\r
209 //\r
210 // OpLen and OpCode here are both stored in network order, since they are from original packet.\r
211 //\r
212 while (Offset < Length) {\r
213\r
142c00c3 214 if (NTOHS (Option->OpCode) == DHCP6_OPT_IA_NA) {\r
b659408b 215 Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option;\r
142c00c3 216 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_URL) {\r
b659408b
ZL
217 //\r
218 // The server sends this option to inform the client about an URL to a boot file.\r
219 //\r
220 Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] = Option;\r
142c00c3 221 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_PARAM) {\r
b659408b 222 Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option;\r
142c00c3 223 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {\r
b659408b 224 Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option;\r
142c00c3 225 } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) {\r
b659408b
ZL
226 Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER] = Option;\r
227 }\r
228\r
229 Offset += (NTOHS (Option->OpLen) + 4);\r
230 Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);\r
231 }\r
232 //\r
233 // The offer with assigned client address is NOT a proxy offer.\r
234 // An ia_na option, embeded with valid ia_addr option and a status_code of success.\r
235 //\r
236 Option = Options[HTTP_BOOT_DHCP6_IDX_IA_NA];\r
237 if (Option != NULL) {\r
238 Option = HttpBootParseDhcp6Options (\r
239 Option->Data + 12,\r
240 NTOHS (Option->OpLen),\r
142c00c3 241 DHCP6_OPT_STATUS_CODE\r
b659408b
ZL
242 );\r
243 if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {\r
244 IsProxyOffer = FALSE;\r
245 }\r
246 }\r
247\r
248 //\r
249 // The offer with "HTTPClient" is a Http offer.\r
250 //\r
251 Option = Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS];\r
252\r
253 if (Option != NULL &&\r
3decba3d
ZL
254 NTOHS(Option->OpLen) >= 16 &&\r
255 CompareMem ((Option->Data + 6), DEFAULT_CLASS_ID_DATA, 10) == 0) {\r
b659408b
ZL
256 IsHttpOffer = TRUE;\r
257 }\r
258\r
259 //\r
260 // The offer with Domain Server is a DNS offer.\r
261 //\r
262 Option = Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];\r
263 if (Option != NULL) {\r
264 IsDnsOffer = TRUE;\r
265 }\r
266\r
267 //\r
268 // Http offer must have a boot URI.\r
269 //\r
270 if (IsHttpOffer && Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
271 return EFI_DEVICE_ERROR;\r
272 }\r
f75a7f56 273\r
b659408b 274 //\r
f75a7f56 275 // Try to retrieve the IP of HTTP server from URI.\r
b659408b
ZL
276 //\r
277 if (IsHttpOffer) {\r
278 Status = HttpParseUrl (\r
279 (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
280 (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
281 FALSE,\r
282 &Cache6->UriParser\r
283 );\r
284 if (EFI_ERROR (Status)) {\r
285 return EFI_DEVICE_ERROR;\r
286 }\r
287\r
288 Status = HttpUrlGetIp6 (\r
289 (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
290 Cache6->UriParser,\r
291 &IpAddr\r
292 );\r
293 IpExpressedUri = !EFI_ERROR (Status);\r
294 }\r
295\r
296 //\r
297 // Determine offer type of the DHCPv6 packet.\r
298 //\r
299 if (IsHttpOffer) {\r
300 if (IpExpressedUri) {\r
fa848a40
FS
301 if (IsProxyOffer) {\r
302 OfferType = HttpOfferTypeProxyIpUri;\r
303 } else {\r
304 OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : HttpOfferTypeDhcpIpUri;\r
305 }\r
b659408b
ZL
306 } else {\r
307 if (!IsProxyOffer) {\r
308 OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri;\r
309 } else {\r
310 OfferType = HttpOfferTypeProxyNameUri;\r
311 }\r
312 }\r
313\r
314 } else {\r
315 if (!IsProxyOffer) {\r
316 OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly;\r
317 } else {\r
318 return EFI_DEVICE_ERROR;\r
319 }\r
320 }\r
f75a7f56 321\r
b659408b
ZL
322 Cache6->OfferType = OfferType;\r
323 return EFI_SUCCESS;\r
324}\r
325\r
326/**\r
327 Cache the DHCPv6 packet.\r
328\r
329 @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.\r
330 @param[in] Src The pointer to the DHCPv6 packet to be cached.\r
331\r
a35dc649
FS
332 @retval EFI_SUCCESS Packet is copied.\r
333 @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.\r
334\r
b659408b 335**/\r
a35dc649 336EFI_STATUS\r
b659408b
ZL
337HttpBootCacheDhcp6Packet (\r
338 IN EFI_DHCP6_PACKET *Dst,\r
339 IN EFI_DHCP6_PACKET *Src\r
340 )\r
341{\r
a35dc649
FS
342 if (Dst->Size < Src->Length) {\r
343 return EFI_BUFFER_TOO_SMALL;\r
344 }\r
b659408b
ZL
345\r
346 CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);\r
347 Dst->Length = Src->Length;\r
f75a7f56 348\r
a35dc649 349 return EFI_SUCCESS;\r
b659408b
ZL
350}\r
351\r
352/**\r
353 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.\r
354\r
355 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
356 @param[in] RcvdOffer The pointer to the received offer packet.\r
357\r
a35dc649
FS
358 @retval EFI_SUCCESS Cache and parse the packet successfully.\r
359 @retval Others Operation failed.\r
360\r
b659408b 361**/\r
a35dc649 362EFI_STATUS\r
b659408b
ZL
363HttpBootCacheDhcp6Offer (\r
364 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
365 IN EFI_DHCP6_PACKET *RcvdOffer\r
366 )\r
367{\r
368 HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6;\r
369 EFI_DHCP6_PACKET *Offer;\r
370 HTTP_BOOT_OFFER_TYPE OfferType;\r
a35dc649 371 EFI_STATUS Status;\r
b659408b
ZL
372\r
373 Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;\r
374 Offer = &Cache6->Packet.Offer;\r
375\r
376 //\r
377 // Cache the content of DHCPv6 packet firstly.\r
378 //\r
a35dc649
FS
379 Status = HttpBootCacheDhcp6Packet(Offer, RcvdOffer);\r
380 if (EFI_ERROR (Status)) {\r
381 return Status;\r
382 }\r
b659408b
ZL
383\r
384 //\r
385 // Validate the DHCPv6 packet, and parse the options and offer type.\r
386 //\r
387 if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) {\r
a35dc649 388 return EFI_ABORTED;\r
b659408b
ZL
389 }\r
390\r
391 //\r
392 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.\r
393 //\r
394 OfferType = Cache6->OfferType;\r
395 ASSERT (OfferType < HttpOfferTypeMax);\r
396 ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM);\r
397 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
398 Private->OfferCount[OfferType]++;\r
a35dc649 399 Private->OfferNum++;\r
f75a7f56 400\r
a35dc649 401 return EFI_SUCCESS;\r
b659408b
ZL
402}\r
403\r
404/**\r
405 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver\r
406 to intercept events that occurred in the configuration process.\r
407\r
408 @param[in] This The pointer to the EFI DHCPv6 Protocol.\r
409 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().\r
410 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.\r
411 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a\r
412 state transition.\r
413 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.\r
414 @param[out] NewPacket The packet that is used to replace the Packet above.\r
415\r
416 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.\r
417 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol\r
418 driver will continue to wait for more packets.\r
419 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.\r
7c275b3c 420 @retval EFI_OUT_OF_RESOURCES There are not enough resources.\r
b659408b
ZL
421\r
422**/\r
423EFI_STATUS\r
424EFIAPI\r
425HttpBootDhcp6CallBack (\r
426 IN EFI_DHCP6_PROTOCOL *This,\r
427 IN VOID *Context,\r
428 IN EFI_DHCP6_STATE CurrentState,\r
429 IN EFI_DHCP6_EVENT Dhcp6Event,\r
430 IN EFI_DHCP6_PACKET *Packet,\r
431 OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL\r
432 )\r
433{\r
95b5c32f
FS
434 HTTP_BOOT_PRIVATE_DATA *Private;\r
435 EFI_DHCP6_PACKET *SelectAd;\r
436 EFI_STATUS Status;\r
437 BOOLEAN Received;\r
f75a7f56 438\r
95b5c32f
FS
439 if ((Dhcp6Event != Dhcp6SendSolicit) &&\r
440 (Dhcp6Event != Dhcp6RcvdAdvertise) &&\r
441 (Dhcp6Event != Dhcp6SendRequest) &&\r
442 (Dhcp6Event != Dhcp6RcvdReply) &&\r
443 (Dhcp6Event != Dhcp6SelectAdvertise)) {\r
444 return EFI_SUCCESS;\r
445 }\r
b659408b 446\r
95b5c32f 447 ASSERT (Packet != NULL);\r
f75a7f56 448\r
95b5c32f
FS
449 Private = (HTTP_BOOT_PRIVATE_DATA *) Context;\r
450 Status = EFI_SUCCESS;\r
451 if (Private->HttpBootCallback != NULL && Dhcp6Event != Dhcp6SelectAdvertise) {\r
452 Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);\r
453 Status = Private->HttpBootCallback->Callback (\r
f75a7f56 454 Private->HttpBootCallback,\r
95b5c32f
FS
455 HttpBootDhcp6,\r
456 Received,\r
457 Packet->Length,\r
458 &Packet->Dhcp6\r
459 );\r
460 if (EFI_ERROR (Status)) {\r
461 return EFI_ABORTED;\r
462 }\r
463 }\r
464 switch (Dhcp6Event) {\r
f75a7f56 465\r
95b5c32f
FS
466 case Dhcp6RcvdAdvertise:\r
467 Status = EFI_NOT_READY;\r
632dcfd6
FS
468 if (Packet->Length > HTTP_BOOT_DHCP6_PACKET_MAX_SIZE) {\r
469 //\r
470 // Ignore the incoming packets which exceed the maximum length.\r
471 //\r
472 break;\r
473 }\r
95b5c32f
FS
474 if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {\r
475 //\r
476 // Cache the dhcp offers to OfferBuffer[] for select later, and record\r
477 // the OfferIndex and OfferCount.\r
478 // If error happens, just ignore this packet and continue to wait more offer.\r
479 //\r
480 HttpBootCacheDhcp6Offer (Private, Packet);\r
481 }\r
482 break;\r
483\r
484 case Dhcp6SelectAdvertise:\r
485 //\r
486 // Select offer by the default policy or by order, and record the SelectIndex\r
487 // and SelectProxyType.\r
488 //\r
489 HttpBootSelectDhcpOffer (Private);\r
490\r
491 if (Private->SelectIndex == 0) {\r
492 Status = EFI_ABORTED;\r
493 } else {\r
494 ASSERT (NewPacket != NULL);\r
495 SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;\r
496 *NewPacket = AllocateZeroPool (SelectAd->Size);\r
497 if (*NewPacket == NULL) {\r
498 return EFI_OUT_OF_RESOURCES;\r
499 }\r
500 CopyMem (*NewPacket, SelectAd, SelectAd->Size);\r
501 }\r
502 break;\r
f75a7f56 503\r
95b5c32f
FS
504 default:\r
505 break;\r
b659408b
ZL
506 }\r
507\r
f75a7f56 508 return Status;\r
b659408b
ZL
509}\r
510\r
511/**\r
512 Check whether IP driver could route the message which will be sent to ServerIp address.\r
f75a7f56 513\r
b659408b
ZL
514 This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid\r
515 route is found in IP6 route table, the address will be filed in GatewayAddr and return.\r
516\r
517 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
518 @param[in] TimeOutInSecond Timeout value in seconds.\r
519 @param[out] GatewayAddr Pointer to store the gateway IP address.\r
520\r
521 @retval EFI_SUCCESS Found a valid gateway address successfully.\r
522 @retval EFI_TIMEOUT The operation is time out.\r
523 @retval Other Unexpect error happened.\r
f75a7f56 524\r
b659408b
ZL
525**/\r
526EFI_STATUS\r
527HttpBootCheckRouteTable (\r
528 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
529 IN UINTN TimeOutInSecond,\r
530 OUT EFI_IPv6_ADDRESS *GatewayAddr\r
531 )\r
532{\r
533 EFI_STATUS Status;\r
534 EFI_IP6_PROTOCOL *Ip6;\r
535 EFI_IP6_MODE_DATA Ip6ModeData;\r
536 UINTN Index;\r
537 EFI_EVENT TimeOutEvt;\r
538 UINTN RetryCount;\r
539 BOOLEAN GatewayIsFound;\r
540\r
541 ASSERT (GatewayAddr != NULL);\r
542 ASSERT (Private != NULL);\r
543\r
544 Ip6 = Private->Ip6;\r
545 GatewayIsFound = FALSE;\r
546 RetryCount = 0;\r
547 TimeOutEvt = NULL;\r
548 Status = EFI_SUCCESS;\r
549 ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));\r
550\r
551 while (TRUE) {\r
552 Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);\r
553 if (EFI_ERROR (Status)) {\r
554 goto ON_EXIT;\r
555 }\r
f75a7f56 556\r
b659408b
ZL
557 //\r
558 // Find out the gateway address which can route the message which send to ServerIp.\r
559 //\r
560 for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {\r
561 if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {\r
562 IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);\r
563 GatewayIsFound = TRUE;\r
564 break;\r
565 }\r
566 }\r
567\r
568 if (Ip6ModeData.AddressList != NULL) {\r
569 FreePool (Ip6ModeData.AddressList);\r
570 }\r
571 if (Ip6ModeData.GroupTable != NULL) {\r
572 FreePool (Ip6ModeData.GroupTable);\r
573 }\r
574 if (Ip6ModeData.RouteTable != NULL) {\r
575 FreePool (Ip6ModeData.RouteTable);\r
576 }\r
577 if (Ip6ModeData.NeighborCache != NULL) {\r
578 FreePool (Ip6ModeData.NeighborCache);\r
579 }\r
580 if (Ip6ModeData.PrefixTable != NULL) {\r
581 FreePool (Ip6ModeData.PrefixTable);\r
582 }\r
583 if (Ip6ModeData.IcmpTypeList != NULL) {\r
584 FreePool (Ip6ModeData.IcmpTypeList);\r
585 }\r
f75a7f56 586\r
b659408b
ZL
587 if (GatewayIsFound || RetryCount == TimeOutInSecond) {\r
588 break;\r
589 }\r
f75a7f56 590\r
b659408b 591 RetryCount++;\r
f75a7f56 592\r
b659408b
ZL
593 //\r
594 // Delay 1 second then recheck it again.\r
595 //\r
596 if (TimeOutEvt == NULL) {\r
597 Status = gBS->CreateEvent (\r
598 EVT_TIMER,\r
599 TPL_CALLBACK,\r
600 NULL,\r
601 NULL,\r
602 &TimeOutEvt\r
603 );\r
604 if (EFI_ERROR (Status)) {\r
605 goto ON_EXIT;\r
606 }\r
607 }\r
608\r
609 Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);\r
610 if (EFI_ERROR (Status)) {\r
611 goto ON_EXIT;\r
612 }\r
613 while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
614 Ip6->Poll (Ip6);\r
615 }\r
616 }\r
f75a7f56 617\r
b659408b
ZL
618ON_EXIT:\r
619 if (TimeOutEvt != NULL) {\r
620 gBS->CloseEvent (TimeOutEvt);\r
621 }\r
f75a7f56 622\r
b659408b
ZL
623 if (GatewayIsFound) {\r
624 Status = EFI_SUCCESS;\r
625 } else if (RetryCount == TimeOutInSecond) {\r
626 Status = EFI_TIMEOUT;\r
627 }\r
628\r
f75a7f56 629 return Status;\r
b659408b
ZL
630}\r
631\r
632/**\r
633 Set the IP6 policy to Automatic.\r
634\r
635 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
636\r
637 @retval EFI_SUCCESS Switch the IP policy succesfully.\r
638 @retval Others Unexpect error happened.\r
639\r
640**/\r
641EFI_STATUS\r
642HttpBootSetIp6Policy (\r
643 IN HTTP_BOOT_PRIVATE_DATA *Private\r
644 )\r
645{\r
646 EFI_IP6_CONFIG_POLICY Policy;\r
647 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
648 EFI_STATUS Status;\r
649 UINTN DataSize;\r
650\r
651 Ip6Config = Private->Ip6Config;\r
652 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
f75a7f56 653\r
b659408b
ZL
654 //\r
655 // Get and store the current policy of IP6 driver.\r
656 //\r
657 Status = Ip6Config->GetData (\r
658 Ip6Config,\r
659 Ip6ConfigDataTypePolicy,\r
660 &DataSize,\r
661 &Policy\r
662 );\r
663 if (EFI_ERROR (Status)) {\r
664 return Status;\r
665 }\r
666\r
667 if (Policy == Ip6ConfigPolicyManual) {\r
668 Policy = Ip6ConfigPolicyAutomatic;\r
669 Status = Ip6Config->SetData (\r
670 Ip6Config,\r
671 Ip6ConfigDataTypePolicy,\r
672 sizeof(EFI_IP6_CONFIG_POLICY),\r
673 &Policy\r
674 );\r
675 if (EFI_ERROR (Status)) {\r
676 return Status;\r
677 }\r
678 }\r
679 return EFI_SUCCESS;\r
680}\r
681\r
682/**\r
683 This function will register the default DNS addresses to the network device.\r
f75a7f56 684\r
b659408b
ZL
685 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
686 @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes.\r
687 @param[in] DnsServerData Point a list of DNS server address in an array\r
688 of EFI_IPv6_ADDRESS instances.\r
689\r
690 @retval EFI_SUCCESS The DNS configuration has been configured successfully.\r
691 @retval Others Failed to configure the address.\r
692\r
693**/\r
694EFI_STATUS\r
695HttpBootSetIp6Dns (\r
696 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
697 IN UINTN DataLength,\r
698 IN VOID *DnsServerData\r
699 )\r
700{\r
701 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
f75a7f56 702\r
b659408b
ZL
703 ASSERT (Private->UsingIpv6);\r
704\r
705 Ip6Config = Private->Ip6Config;\r
f75a7f56 706\r
b659408b
ZL
707 return Ip6Config->SetData (\r
708 Ip6Config,\r
709 Ip6ConfigDataTypeDnsServer,\r
710 DataLength,\r
711 DnsServerData\r
712 );\r
713}\r
714\r
715/**\r
716 This function will register the IPv6 gateway address to the network device.\r
f75a7f56 717\r
b659408b
ZL
718 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
719\r
720 @retval EFI_SUCCESS The new IP configuration has been configured successfully.\r
721 @retval Others Failed to configure the address.\r
722\r
723**/\r
724EFI_STATUS\r
725HttpBootSetIp6Gateway (\r
726 IN HTTP_BOOT_PRIVATE_DATA *Private\r
727 )\r
728{\r
729 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
730 EFI_STATUS Status;\r
731\r
732 ASSERT (Private->UsingIpv6);\r
733 Ip6Config = Private->Ip6Config;\r
f75a7f56 734\r
b659408b 735 //\r
f75a7f56 736 // Set the default gateway address.\r
b659408b
ZL
737 //\r
738 if (!Private->NoGateway && !NetIp6IsUnspecifiedAddr (&Private->GatewayIp.v6)) {\r
739 Status = Ip6Config->SetData (\r
740 Ip6Config,\r
741 Ip6ConfigDataTypeGateway,\r
742 sizeof (EFI_IPv6_ADDRESS),\r
743 &Private->GatewayIp.v6\r
744 );\r
745 if (EFI_ERROR(Status)) {\r
746 return Status;\r
747 }\r
748 }\r
749\r
750 return EFI_SUCCESS;\r
751}\r
752\r
753/**\r
754 This function will register the station IP address.\r
f75a7f56 755\r
b659408b
ZL
756 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
757\r
758 @retval EFI_SUCCESS The new IP address has been configured successfully.\r
759 @retval Others Failed to configure the address.\r
760\r
761**/\r
762EFI_STATUS\r
763HttpBootSetIp6Address (\r
764 IN HTTP_BOOT_PRIVATE_DATA *Private\r
765)\r
766{\r
767 EFI_STATUS Status;\r
768 EFI_IP6_PROTOCOL *Ip6;\r
769 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
770 EFI_IP6_CONFIG_POLICY Policy;\r
771 EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr;\r
772 EFI_IPv6_ADDRESS *Ip6Addr;\r
773 EFI_IPv6_ADDRESS GatewayAddr;\r
774 EFI_IP6_CONFIG_DATA Ip6CfgData;\r
f75a7f56 775 EFI_EVENT MappedEvt;\r
b659408b
ZL
776 UINTN DataSize;\r
777 BOOLEAN IsAddressOk;\r
778 UINTN Index;\r
779\r
780 ASSERT (Private->UsingIpv6);\r
f75a7f56 781\r
b659408b
ZL
782 MappedEvt = NULL;\r
783 IsAddressOk = FALSE;\r
784 Ip6Addr = NULL;\r
785 Ip6Cfg = Private->Ip6Config;\r
786 Ip6 = Private->Ip6;\r
f75a7f56 787\r
b659408b
ZL
788 ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
789 CopyMem (&CfgAddr, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
790 ZeroMem (&Ip6CfgData, sizeof (EFI_IP6_CONFIG_DATA));\r
f75a7f56 791\r
b659408b
ZL
792 Ip6CfgData.AcceptIcmpErrors = TRUE;\r
793 Ip6CfgData.DefaultProtocol = IP6_ICMP;\r
794 Ip6CfgData.HopLimit = HTTP_BOOT_DEFAULT_HOPLIMIT;\r
795 Ip6CfgData.ReceiveTimeout = HTTP_BOOT_DEFAULT_LIFETIME;\r
796 Ip6CfgData.TransmitTimeout = HTTP_BOOT_DEFAULT_LIFETIME;\r
f75a7f56 797\r
b659408b
ZL
798 Status = Ip6->Configure (Ip6, &Ip6CfgData);\r
799 if (EFI_ERROR (Status)) {\r
800 goto ON_EXIT;\r
801 }\r
802\r
803 //\r
804 // Retrieve the gateway address from IP6 route table.\r
805 //\r
806 Status = HttpBootCheckRouteTable (Private, HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);\r
807 if (EFI_ERROR (Status)) {\r
808 Private->NoGateway = TRUE;\r
809 } else {\r
810 IP6_COPY_ADDRESS (&Private->GatewayIp.v6, &GatewayAddr);\r
811 }\r
812\r
813 //\r
814 // Set the new address by Ip6ConfigProtocol manually.\r
815 //\r
816 Policy = Ip6ConfigPolicyManual;\r
817 Status = Ip6Cfg->SetData (\r
818 Ip6Cfg,\r
819 Ip6ConfigDataTypePolicy,\r
820 sizeof(EFI_IP6_CONFIG_POLICY),\r
821 &Policy\r
822 );\r
823 if (EFI_ERROR (Status)) {\r
824 goto ON_EXIT;\r
825 }\r
f75a7f56 826\r
b659408b
ZL
827 //\r
828 // Create a notify event to set address flag when DAD if IP6 driver succeeded.\r
829 //\r
830 Status = gBS->CreateEvent (\r
831 EVT_NOTIFY_SIGNAL,\r
832 TPL_NOTIFY,\r
833 HttpBootCommonNotify,\r
834 &IsAddressOk,\r
835 &MappedEvt\r
836 );\r
837 if (EFI_ERROR (Status)) {\r
838 goto ON_EXIT;\r
839 }\r
f75a7f56 840\r
b659408b
ZL
841 //\r
842 // Set static host ip6 address. This is a asynchronous process.\r
843 //\r
844 Status = Ip6Cfg->RegisterDataNotify (\r
845 Ip6Cfg,\r
846 Ip6ConfigDataTypeManualAddress,\r
847 MappedEvt\r
848 );\r
849 if (EFI_ERROR(Status)) {\r
850 goto ON_EXIT;\r
851 }\r
852\r
853 Status = Ip6Cfg->SetData (\r
854 Ip6Cfg,\r
855 Ip6ConfigDataTypeManualAddress,\r
856 sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS),\r
857 &CfgAddr\r
f75a7f56 858 );\r
b659408b
ZL
859 if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {\r
860 goto ON_EXIT;\r
861 } else if (Status == EFI_NOT_READY) {\r
862 //\r
863 // Poll the network until the asynchronous process is finished.\r
864 //\r
865 while (!IsAddressOk) {\r
866 Ip6->Poll (Ip6);\r
867 }\r
868 //\r
869 // Check whether the Ip6 Address setting is successed.\r
870 //\r
871 DataSize = 0;\r
872 Status = Ip6Cfg->GetData (\r
873 Ip6Cfg,\r
874 Ip6ConfigDataTypeManualAddress,\r
875 &DataSize,\r
876 NULL\r
877 );\r
878 if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {\r
879 Status = EFI_DEVICE_ERROR;\r
880 goto ON_EXIT;\r
881 }\r
f75a7f56 882\r
b659408b
ZL
883 Ip6Addr = AllocatePool (DataSize);\r
884 if (Ip6Addr == NULL) {\r
885 return EFI_OUT_OF_RESOURCES;\r
886 }\r
887 Status = Ip6Cfg->GetData (\r
888 Ip6Cfg,\r
889 Ip6ConfigDataTypeManualAddress,\r
890 &DataSize,\r
891 (VOID *) Ip6Addr\r
892 );\r
893 if (EFI_ERROR (Status)) {\r
894 Status = EFI_DEVICE_ERROR;\r
895 goto ON_EXIT;\r
896 }\r
897\r
898 for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index ++) {\r
899 if (CompareMem (Ip6Addr + Index, &CfgAddr, sizeof (EFI_IPv6_ADDRESS)) == 0) {\r
900 break;\r
901 }\r
902 }\r
903 if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {\r
904 Status = EFI_ABORTED;\r
905 goto ON_EXIT;\r
f75a7f56 906 }\r
b659408b 907 }\r
f75a7f56 908\r
b659408b
ZL
909ON_EXIT:\r
910 if (MappedEvt != NULL) {\r
911 Ip6Cfg->UnregisterDataNotify (\r
912 Ip6Cfg,\r
913 Ip6ConfigDataTypeManualAddress,\r
914 MappedEvt\r
915 );\r
916 gBS->CloseEvent (MappedEvt);\r
917 }\r
918\r
919 if (Ip6Addr != NULL) {\r
920 FreePool (Ip6Addr);\r
921 }\r
f75a7f56
LG
922\r
923 return Status;\r
b659408b
ZL
924}\r
925\r
926/**\r
927 Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information.\r
928\r
929 @param[in] Private Pointer to HTTP_BOOT private data.\r
930\r
931 @retval EFI_SUCCESS The S.A.R.R process successfully finished.\r
932 @retval Others Failed to finish the S.A.R.R process.\r
933\r
934**/\r
935EFI_STATUS\r
936HttpBootDhcp6Sarr (\r
937 IN HTTP_BOOT_PRIVATE_DATA *Private\r
938 )\r
939{\r
940 EFI_DHCP6_PROTOCOL *Dhcp6;\r
941 EFI_DHCP6_CONFIG_DATA Config;\r
942 EFI_DHCP6_MODE_DATA Mode;\r
943 EFI_DHCP6_RETRANSMISSION *Retransmit;\r
944 EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_OPTION_MAX_NUM];\r
945 UINT32 OptCount;\r
946 UINT8 Buffer[HTTP_BOOT_DHCP6_OPTION_MAX_SIZE];\r
947 EFI_STATUS Status;\r
948\r
949 Dhcp6 = Private->Dhcp6;\r
950 ASSERT (Dhcp6 != NULL);\r
951\r
952 //\r
953 // Build options list for the request packet.\r
954 //\r
955 OptCount = HttpBootBuildDhcp6Options (Private, OptList, Buffer);\r
956 ASSERT (OptCount >0);\r
f75a7f56 957\r
b659408b
ZL
958 Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));\r
959 if (Retransmit == NULL) {\r
960 return EFI_OUT_OF_RESOURCES;\r
961 }\r
f75a7f56 962\r
b659408b
ZL
963 ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));\r
964 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
f75a7f56 965\r
b659408b
ZL
966 Config.OptionCount = OptCount;\r
967 Config.OptionList = OptList;\r
968 Config.Dhcp6Callback = HttpBootDhcp6CallBack;\r
969 Config.CallbackContext = Private;\r
970 Config.IaInfoEvent = NULL;\r
971 Config.RapidCommit = FALSE;\r
972 Config.ReconfigureAccept = FALSE;\r
973 Config.IaDescriptor.IaId = NET_RANDOM (NetRandomInitSeed ());\r
974 Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;\r
975 Config.SolicitRetransmission = Retransmit;\r
976 Retransmit->Irt = 4;\r
977 Retransmit->Mrc = 4;\r
978 Retransmit->Mrt = 32;\r
979 Retransmit->Mrd = 60;\r
f75a7f56 980\r
b659408b
ZL
981 //\r
982 // Configure the DHCPv6 instance for HTTP boot.\r
983 //\r
984 Status = Dhcp6->Configure (Dhcp6, &Config);\r
985 FreePool (Retransmit);\r
986 if (EFI_ERROR (Status)) {\r
987 goto ON_EXIT;\r
988 }\r
989 //\r
990 // Initialize the record fields for DHCPv6 offer in private data.\r
991 //\r
992 Private->OfferNum = 0;\r
993 Private->SelectIndex = 0;\r
994 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
995 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
f75a7f56 996\r
b659408b
ZL
997 //\r
998 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
999 //\r
1000 Status = Dhcp6->Start (Dhcp6);\r
1001 if (EFI_ERROR (Status)) {\r
1002 goto ON_EXIT;\r
1003 }\r
f75a7f56 1004\r
b659408b
ZL
1005 //\r
1006 // Get the acquired IPv6 address and store them.\r
1007 //\r
1008 Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);\r
1009 if (EFI_ERROR (Status)) {\r
1010 goto ON_EXIT;\r
1011 }\r
f75a7f56 1012\r
b659408b
ZL
1013 ASSERT (Mode.Ia->State == Dhcp6Bound);\r
1014 CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));\r
f75a7f56 1015\r
b659408b
ZL
1016 AsciiPrint ("\n Station IPv6 address is ");\r
1017 HttpBootShowIp6Addr (&Private->StationIp.v6);\r
1018 AsciiPrint ("\n");\r
f75a7f56 1019\r
b659408b
ZL
1020ON_EXIT:\r
1021 if (EFI_ERROR (Status)) {\r
1022 Dhcp6->Stop (Dhcp6);\r
1023 Dhcp6->Configure (Dhcp6, NULL);\r
1024 } else {\r
1025 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
b659408b 1026 Dhcp6->Configure (Dhcp6, &Config);\r
ce22514e
ZL
1027 if (Mode.ClientId != NULL) {\r
1028 FreePool (Mode.ClientId);\r
1029 }\r
1030 if (Mode.Ia != NULL) {\r
1031 FreePool (Mode.Ia);\r
1032 }\r
b659408b
ZL
1033 }\r
1034\r
f75a7f56
LG
1035 return Status;\r
1036\r
b659408b
ZL
1037}\r
1038\r