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