NetworkPkg:Enable Http Boot over Ipv6 stack
[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
4Copyright (c) 2015, 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
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 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
44 OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_ORO);\r
45 OptList[Index]->OpLen = HTONS (8);\r
46 OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data;\r
47 OptEnt.Oro->OpCode[0] = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL);\r
48 OptEnt.Oro->OpCode[1] = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM);\r
49 OptEnt.Oro->OpCode[2] = HTONS(HTTP_BOOT_DHCP6_OPT_DNS_SERVERS);\r
50 OptEnt.Oro->OpCode[3] = HTONS(HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS);\r
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
57 OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_UNDI);\r
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
77 OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_ARCH);\r
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
88 OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS);\r
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
195 \r
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
202 \r
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
214 if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_IA_NA) {\r
215 Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option;\r
216 } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL) {\r
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
221 } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM) {\r
222 Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option;\r
223 } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS) {\r
224 Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option;\r
225 } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_DNS_SERVERS) {\r
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
241 HTTP_BOOT_DHCP6_OPT_STATUS_CODE\r
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
254 NTOHS(Option->OpLen) >= 10 &&\r
255 CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 10) == 0) {\r
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
273 \r
274 //\r
275 // Try to retrieve the IP of HTTP server from URI. \r
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
301 OfferType = IsProxyOffer ? HttpOfferTypeProxyIpUri : HttpOfferTypeDhcpIpUri;\r
302 } else {\r
303 if (!IsProxyOffer) {\r
304 OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri;\r
305 } else {\r
306 OfferType = HttpOfferTypeProxyNameUri;\r
307 }\r
308 }\r
309\r
310 } else {\r
311 if (!IsProxyOffer) {\r
312 OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly;\r
313 } else {\r
314 return EFI_DEVICE_ERROR;\r
315 }\r
316 }\r
317 \r
318 Cache6->OfferType = OfferType;\r
319 return EFI_SUCCESS;\r
320}\r
321\r
322/**\r
323 Cache the DHCPv6 packet.\r
324\r
325 @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.\r
326 @param[in] Src The pointer to the DHCPv6 packet to be cached.\r
327\r
328**/\r
329VOID\r
330HttpBootCacheDhcp6Packet (\r
331 IN EFI_DHCP6_PACKET *Dst,\r
332 IN EFI_DHCP6_PACKET *Src\r
333 )\r
334{\r
335 ASSERT (Dst->Size >= Src->Length);\r
336\r
337 CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);\r
338 Dst->Length = Src->Length;\r
339}\r
340\r
341/**\r
342 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.\r
343\r
344 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
345 @param[in] RcvdOffer The pointer to the received offer packet.\r
346\r
347**/\r
348VOID\r
349HttpBootCacheDhcp6Offer (\r
350 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
351 IN EFI_DHCP6_PACKET *RcvdOffer\r
352 )\r
353{\r
354 HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6;\r
355 EFI_DHCP6_PACKET *Offer;\r
356 HTTP_BOOT_OFFER_TYPE OfferType;\r
357\r
358 Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;\r
359 Offer = &Cache6->Packet.Offer;\r
360\r
361 //\r
362 // Cache the content of DHCPv6 packet firstly.\r
363 //\r
364 HttpBootCacheDhcp6Packet(Offer, RcvdOffer);\r
365\r
366 //\r
367 // Validate the DHCPv6 packet, and parse the options and offer type.\r
368 //\r
369 if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) {\r
370 return ;\r
371 }\r
372\r
373 //\r
374 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.\r
375 //\r
376 OfferType = Cache6->OfferType;\r
377 ASSERT (OfferType < HttpOfferTypeMax);\r
378 ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM);\r
379 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
380 Private->OfferCount[OfferType]++;\r
381 Private->OfferNum++; \r
382}\r
383\r
384/**\r
385 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver\r
386 to intercept events that occurred in the configuration process.\r
387\r
388 @param[in] This The pointer to the EFI DHCPv6 Protocol.\r
389 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().\r
390 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.\r
391 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a\r
392 state transition.\r
393 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.\r
394 @param[out] NewPacket The packet that is used to replace the Packet above.\r
395\r
396 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.\r
397 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol\r
398 driver will continue to wait for more packets.\r
399 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.\r
400\r
401**/\r
402EFI_STATUS\r
403EFIAPI\r
404HttpBootDhcp6CallBack (\r
405 IN EFI_DHCP6_PROTOCOL *This,\r
406 IN VOID *Context,\r
407 IN EFI_DHCP6_STATE CurrentState,\r
408 IN EFI_DHCP6_EVENT Dhcp6Event,\r
409 IN EFI_DHCP6_PACKET *Packet,\r
410 OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL\r
411 )\r
412{\r
413 HTTP_BOOT_PRIVATE_DATA *Private;\r
414 EFI_DHCP6_PACKET *SelectAd;\r
415 EFI_STATUS Status;\r
416 if ((Dhcp6Event != Dhcp6RcvdAdvertise) && (Dhcp6Event != Dhcp6SelectAdvertise)) {\r
417 return EFI_SUCCESS;\r
418 }\r
419\r
420 ASSERT (Packet != NULL);\r
421 \r
422 Private = (HTTP_BOOT_PRIVATE_DATA *) Context;\r
423 Status = EFI_SUCCESS;\r
424 switch (Dhcp6Event) {\r
425 \r
426 case Dhcp6RcvdAdvertise:\r
427 Status = EFI_NOT_READY;\r
428 if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {\r
429 //\r
430 // Cache the dhcp offers to OfferBuffer[] for select later, and record\r
431 // the OfferIndex and OfferCount.\r
432 //\r
433 HttpBootCacheDhcp6Offer (Private, Packet);\r
434 }\r
435 break;\r
436\r
437 case Dhcp6SelectAdvertise:\r
438 //\r
439 // Select offer by the default policy or by order, and record the SelectIndex\r
440 // and SelectProxyType.\r
441 //\r
442 HttpBootSelectDhcpOffer (Private);\r
443\r
444 if (Private->SelectIndex == 0) {\r
445 Status = EFI_ABORTED;\r
446 } else {\r
447 ASSERT (NewPacket != NULL);\r
448 SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;\r
449 *NewPacket = AllocateZeroPool (SelectAd->Size);\r
450 ASSERT (*NewPacket != NULL);\r
451 CopyMem (*NewPacket, SelectAd, SelectAd->Size);\r
452 }\r
453 break;\r
454 \r
455 default:\r
456 break;\r
457 }\r
458\r
459 return Status; \r
460}\r
461\r
462/**\r
463 Check whether IP driver could route the message which will be sent to ServerIp address.\r
464 \r
465 This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid\r
466 route is found in IP6 route table, the address will be filed in GatewayAddr and return.\r
467\r
468 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
469 @param[in] TimeOutInSecond Timeout value in seconds.\r
470 @param[out] GatewayAddr Pointer to store the gateway IP address.\r
471\r
472 @retval EFI_SUCCESS Found a valid gateway address successfully.\r
473 @retval EFI_TIMEOUT The operation is time out.\r
474 @retval Other Unexpect error happened.\r
475 \r
476**/\r
477EFI_STATUS\r
478HttpBootCheckRouteTable (\r
479 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
480 IN UINTN TimeOutInSecond,\r
481 OUT EFI_IPv6_ADDRESS *GatewayAddr\r
482 )\r
483{\r
484 EFI_STATUS Status;\r
485 EFI_IP6_PROTOCOL *Ip6;\r
486 EFI_IP6_MODE_DATA Ip6ModeData;\r
487 UINTN Index;\r
488 EFI_EVENT TimeOutEvt;\r
489 UINTN RetryCount;\r
490 BOOLEAN GatewayIsFound;\r
491\r
492 ASSERT (GatewayAddr != NULL);\r
493 ASSERT (Private != NULL);\r
494\r
495 Ip6 = Private->Ip6;\r
496 GatewayIsFound = FALSE;\r
497 RetryCount = 0;\r
498 TimeOutEvt = NULL;\r
499 Status = EFI_SUCCESS;\r
500 ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));\r
501\r
502 while (TRUE) {\r
503 Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);\r
504 if (EFI_ERROR (Status)) {\r
505 goto ON_EXIT;\r
506 }\r
507 \r
508 //\r
509 // Find out the gateway address which can route the message which send to ServerIp.\r
510 //\r
511 for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {\r
512 if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {\r
513 IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);\r
514 GatewayIsFound = TRUE;\r
515 break;\r
516 }\r
517 }\r
518\r
519 if (Ip6ModeData.AddressList != NULL) {\r
520 FreePool (Ip6ModeData.AddressList);\r
521 }\r
522 if (Ip6ModeData.GroupTable != NULL) {\r
523 FreePool (Ip6ModeData.GroupTable);\r
524 }\r
525 if (Ip6ModeData.RouteTable != NULL) {\r
526 FreePool (Ip6ModeData.RouteTable);\r
527 }\r
528 if (Ip6ModeData.NeighborCache != NULL) {\r
529 FreePool (Ip6ModeData.NeighborCache);\r
530 }\r
531 if (Ip6ModeData.PrefixTable != NULL) {\r
532 FreePool (Ip6ModeData.PrefixTable);\r
533 }\r
534 if (Ip6ModeData.IcmpTypeList != NULL) {\r
535 FreePool (Ip6ModeData.IcmpTypeList);\r
536 }\r
537 \r
538 if (GatewayIsFound || RetryCount == TimeOutInSecond) {\r
539 break;\r
540 }\r
541 \r
542 RetryCount++;\r
543 \r
544 //\r
545 // Delay 1 second then recheck it again.\r
546 //\r
547 if (TimeOutEvt == NULL) {\r
548 Status = gBS->CreateEvent (\r
549 EVT_TIMER,\r
550 TPL_CALLBACK,\r
551 NULL,\r
552 NULL,\r
553 &TimeOutEvt\r
554 );\r
555 if (EFI_ERROR (Status)) {\r
556 goto ON_EXIT;\r
557 }\r
558 }\r
559\r
560 Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);\r
561 if (EFI_ERROR (Status)) {\r
562 goto ON_EXIT;\r
563 }\r
564 while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
565 Ip6->Poll (Ip6);\r
566 }\r
567 }\r
568 \r
569ON_EXIT:\r
570 if (TimeOutEvt != NULL) {\r
571 gBS->CloseEvent (TimeOutEvt);\r
572 }\r
573 \r
574 if (GatewayIsFound) {\r
575 Status = EFI_SUCCESS;\r
576 } else if (RetryCount == TimeOutInSecond) {\r
577 Status = EFI_TIMEOUT;\r
578 }\r
579\r
580 return Status; \r
581}\r
582\r
583/**\r
584 Set the IP6 policy to Automatic.\r
585\r
586 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
587\r
588 @retval EFI_SUCCESS Switch the IP policy succesfully.\r
589 @retval Others Unexpect error happened.\r
590\r
591**/\r
592EFI_STATUS\r
593HttpBootSetIp6Policy (\r
594 IN HTTP_BOOT_PRIVATE_DATA *Private\r
595 )\r
596{\r
597 EFI_IP6_CONFIG_POLICY Policy;\r
598 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
599 EFI_STATUS Status;\r
600 UINTN DataSize;\r
601\r
602 Ip6Config = Private->Ip6Config;\r
603 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
604 \r
605 //\r
606 // Get and store the current policy of IP6 driver.\r
607 //\r
608 Status = Ip6Config->GetData (\r
609 Ip6Config,\r
610 Ip6ConfigDataTypePolicy,\r
611 &DataSize,\r
612 &Policy\r
613 );\r
614 if (EFI_ERROR (Status)) {\r
615 return Status;\r
616 }\r
617\r
618 if (Policy == Ip6ConfigPolicyManual) {\r
619 Policy = Ip6ConfigPolicyAutomatic;\r
620 Status = Ip6Config->SetData (\r
621 Ip6Config,\r
622 Ip6ConfigDataTypePolicy,\r
623 sizeof(EFI_IP6_CONFIG_POLICY),\r
624 &Policy\r
625 );\r
626 if (EFI_ERROR (Status)) {\r
627 return Status;\r
628 }\r
629 }\r
630 return EFI_SUCCESS;\r
631}\r
632\r
633/**\r
634 This function will register the default DNS addresses to the network device.\r
635 \r
636 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
637 @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes.\r
638 @param[in] DnsServerData Point a list of DNS server address in an array\r
639 of EFI_IPv6_ADDRESS instances.\r
640\r
641 @retval EFI_SUCCESS The DNS configuration has been configured successfully.\r
642 @retval Others Failed to configure the address.\r
643\r
644**/\r
645EFI_STATUS\r
646HttpBootSetIp6Dns (\r
647 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
648 IN UINTN DataLength,\r
649 IN VOID *DnsServerData\r
650 )\r
651{\r
652 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
653 \r
654 ASSERT (Private->UsingIpv6);\r
655\r
656 Ip6Config = Private->Ip6Config;\r
657 \r
658 return Ip6Config->SetData (\r
659 Ip6Config,\r
660 Ip6ConfigDataTypeDnsServer,\r
661 DataLength,\r
662 DnsServerData\r
663 );\r
664}\r
665\r
666/**\r
667 This function will register the IPv6 gateway address to the network device.\r
668 \r
669 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
670\r
671 @retval EFI_SUCCESS The new IP configuration has been configured successfully.\r
672 @retval Others Failed to configure the address.\r
673\r
674**/\r
675EFI_STATUS\r
676HttpBootSetIp6Gateway (\r
677 IN HTTP_BOOT_PRIVATE_DATA *Private\r
678 )\r
679{\r
680 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
681 EFI_STATUS Status;\r
682\r
683 ASSERT (Private->UsingIpv6);\r
684 Ip6Config = Private->Ip6Config;\r
685 \r
686 //\r
687 // Set the default gateway address. \r
688 //\r
689 if (!Private->NoGateway && !NetIp6IsUnspecifiedAddr (&Private->GatewayIp.v6)) {\r
690 Status = Ip6Config->SetData (\r
691 Ip6Config,\r
692 Ip6ConfigDataTypeGateway,\r
693 sizeof (EFI_IPv6_ADDRESS),\r
694 &Private->GatewayIp.v6\r
695 );\r
696 if (EFI_ERROR(Status)) {\r
697 return Status;\r
698 }\r
699 }\r
700\r
701 return EFI_SUCCESS;\r
702}\r
703\r
704/**\r
705 This function will register the station IP address.\r
706 \r
707 @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.\r
708\r
709 @retval EFI_SUCCESS The new IP address has been configured successfully.\r
710 @retval Others Failed to configure the address.\r
711\r
712**/\r
713EFI_STATUS\r
714HttpBootSetIp6Address (\r
715 IN HTTP_BOOT_PRIVATE_DATA *Private\r
716)\r
717{\r
718 EFI_STATUS Status;\r
719 EFI_IP6_PROTOCOL *Ip6;\r
720 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
721 EFI_IP6_CONFIG_POLICY Policy;\r
722 EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr;\r
723 EFI_IPv6_ADDRESS *Ip6Addr;\r
724 EFI_IPv6_ADDRESS GatewayAddr;\r
725 EFI_IP6_CONFIG_DATA Ip6CfgData;\r
726 EFI_EVENT MappedEvt; \r
727 UINTN DataSize;\r
728 BOOLEAN IsAddressOk;\r
729 UINTN Index;\r
730\r
731 ASSERT (Private->UsingIpv6);\r
732 \r
733 MappedEvt = NULL;\r
734 IsAddressOk = FALSE;\r
735 Ip6Addr = NULL;\r
736 Ip6Cfg = Private->Ip6Config;\r
737 Ip6 = Private->Ip6;\r
738 \r
739 ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
740 CopyMem (&CfgAddr, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
741 ZeroMem (&Ip6CfgData, sizeof (EFI_IP6_CONFIG_DATA));\r
742 \r
743 Ip6CfgData.AcceptIcmpErrors = TRUE;\r
744 Ip6CfgData.DefaultProtocol = IP6_ICMP;\r
745 Ip6CfgData.HopLimit = HTTP_BOOT_DEFAULT_HOPLIMIT;\r
746 Ip6CfgData.ReceiveTimeout = HTTP_BOOT_DEFAULT_LIFETIME;\r
747 Ip6CfgData.TransmitTimeout = HTTP_BOOT_DEFAULT_LIFETIME;\r
748 \r
749 Status = Ip6->Configure (Ip6, &Ip6CfgData);\r
750 if (EFI_ERROR (Status)) {\r
751 goto ON_EXIT;\r
752 }\r
753\r
754 //\r
755 // Retrieve the gateway address from IP6 route table.\r
756 //\r
757 Status = HttpBootCheckRouteTable (Private, HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);\r
758 if (EFI_ERROR (Status)) {\r
759 Private->NoGateway = TRUE;\r
760 } else {\r
761 IP6_COPY_ADDRESS (&Private->GatewayIp.v6, &GatewayAddr);\r
762 }\r
763\r
764 //\r
765 // Set the new address by Ip6ConfigProtocol manually.\r
766 //\r
767 Policy = Ip6ConfigPolicyManual;\r
768 Status = Ip6Cfg->SetData (\r
769 Ip6Cfg,\r
770 Ip6ConfigDataTypePolicy,\r
771 sizeof(EFI_IP6_CONFIG_POLICY),\r
772 &Policy\r
773 );\r
774 if (EFI_ERROR (Status)) {\r
775 goto ON_EXIT;\r
776 }\r
777 \r
778 //\r
779 // Create a notify event to set address flag when DAD if IP6 driver succeeded.\r
780 //\r
781 Status = gBS->CreateEvent (\r
782 EVT_NOTIFY_SIGNAL,\r
783 TPL_NOTIFY,\r
784 HttpBootCommonNotify,\r
785 &IsAddressOk,\r
786 &MappedEvt\r
787 );\r
788 if (EFI_ERROR (Status)) {\r
789 goto ON_EXIT;\r
790 }\r
791 \r
792 //\r
793 // Set static host ip6 address. This is a asynchronous process.\r
794 //\r
795 Status = Ip6Cfg->RegisterDataNotify (\r
796 Ip6Cfg,\r
797 Ip6ConfigDataTypeManualAddress,\r
798 MappedEvt\r
799 );\r
800 if (EFI_ERROR(Status)) {\r
801 goto ON_EXIT;\r
802 }\r
803\r
804 Status = Ip6Cfg->SetData (\r
805 Ip6Cfg,\r
806 Ip6ConfigDataTypeManualAddress,\r
807 sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS),\r
808 &CfgAddr\r
809 ); \r
810 if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {\r
811 goto ON_EXIT;\r
812 } else if (Status == EFI_NOT_READY) {\r
813 //\r
814 // Poll the network until the asynchronous process is finished.\r
815 //\r
816 while (!IsAddressOk) {\r
817 Ip6->Poll (Ip6);\r
818 }\r
819 //\r
820 // Check whether the Ip6 Address setting is successed.\r
821 //\r
822 DataSize = 0;\r
823 Status = Ip6Cfg->GetData (\r
824 Ip6Cfg,\r
825 Ip6ConfigDataTypeManualAddress,\r
826 &DataSize,\r
827 NULL\r
828 );\r
829 if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {\r
830 Status = EFI_DEVICE_ERROR;\r
831 goto ON_EXIT;\r
832 }\r
833 \r
834 Ip6Addr = AllocatePool (DataSize);\r
835 if (Ip6Addr == NULL) {\r
836 return EFI_OUT_OF_RESOURCES;\r
837 }\r
838 Status = Ip6Cfg->GetData (\r
839 Ip6Cfg,\r
840 Ip6ConfigDataTypeManualAddress,\r
841 &DataSize,\r
842 (VOID *) Ip6Addr\r
843 );\r
844 if (EFI_ERROR (Status)) {\r
845 Status = EFI_DEVICE_ERROR;\r
846 goto ON_EXIT;\r
847 }\r
848\r
849 for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index ++) {\r
850 if (CompareMem (Ip6Addr + Index, &CfgAddr, sizeof (EFI_IPv6_ADDRESS)) == 0) {\r
851 break;\r
852 }\r
853 }\r
854 if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {\r
855 Status = EFI_ABORTED;\r
856 goto ON_EXIT;\r
857 } \r
858 }\r
859 \r
860ON_EXIT:\r
861 if (MappedEvt != NULL) {\r
862 Ip6Cfg->UnregisterDataNotify (\r
863 Ip6Cfg,\r
864 Ip6ConfigDataTypeManualAddress,\r
865 MappedEvt\r
866 );\r
867 gBS->CloseEvent (MappedEvt);\r
868 }\r
869\r
870 if (Ip6Addr != NULL) {\r
871 FreePool (Ip6Addr);\r
872 }\r
873 \r
874 return Status; \r
875}\r
876\r
877/**\r
878 Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information.\r
879\r
880 @param[in] Private Pointer to HTTP_BOOT private data.\r
881\r
882 @retval EFI_SUCCESS The S.A.R.R process successfully finished.\r
883 @retval Others Failed to finish the S.A.R.R process.\r
884\r
885**/\r
886EFI_STATUS\r
887HttpBootDhcp6Sarr (\r
888 IN HTTP_BOOT_PRIVATE_DATA *Private\r
889 )\r
890{\r
891 EFI_DHCP6_PROTOCOL *Dhcp6;\r
892 EFI_DHCP6_CONFIG_DATA Config;\r
893 EFI_DHCP6_MODE_DATA Mode;\r
894 EFI_DHCP6_RETRANSMISSION *Retransmit;\r
895 EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_OPTION_MAX_NUM];\r
896 UINT32 OptCount;\r
897 UINT8 Buffer[HTTP_BOOT_DHCP6_OPTION_MAX_SIZE];\r
898 EFI_STATUS Status;\r
899\r
900 Dhcp6 = Private->Dhcp6;\r
901 ASSERT (Dhcp6 != NULL);\r
902\r
903 //\r
904 // Build options list for the request packet.\r
905 //\r
906 OptCount = HttpBootBuildDhcp6Options (Private, OptList, Buffer);\r
907 ASSERT (OptCount >0);\r
908 \r
909 Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));\r
910 if (Retransmit == NULL) {\r
911 return EFI_OUT_OF_RESOURCES;\r
912 }\r
913 \r
914 ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));\r
915 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
916 \r
917 Config.OptionCount = OptCount;\r
918 Config.OptionList = OptList;\r
919 Config.Dhcp6Callback = HttpBootDhcp6CallBack;\r
920 Config.CallbackContext = Private;\r
921 Config.IaInfoEvent = NULL;\r
922 Config.RapidCommit = FALSE;\r
923 Config.ReconfigureAccept = FALSE;\r
924 Config.IaDescriptor.IaId = NET_RANDOM (NetRandomInitSeed ());\r
925 Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;\r
926 Config.SolicitRetransmission = Retransmit;\r
927 Retransmit->Irt = 4;\r
928 Retransmit->Mrc = 4;\r
929 Retransmit->Mrt = 32;\r
930 Retransmit->Mrd = 60;\r
931 \r
932 //\r
933 // Configure the DHCPv6 instance for HTTP boot.\r
934 //\r
935 Status = Dhcp6->Configure (Dhcp6, &Config);\r
936 FreePool (Retransmit);\r
937 if (EFI_ERROR (Status)) {\r
938 goto ON_EXIT;\r
939 }\r
940 //\r
941 // Initialize the record fields for DHCPv6 offer in private data.\r
942 //\r
943 Private->OfferNum = 0;\r
944 Private->SelectIndex = 0;\r
945 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
946 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
947 \r
948 //\r
949 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
950 //\r
951 Status = Dhcp6->Start (Dhcp6);\r
952 if (EFI_ERROR (Status)) {\r
953 goto ON_EXIT;\r
954 }\r
955 \r
956 //\r
957 // Get the acquired IPv6 address and store them.\r
958 //\r
959 Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);\r
960 if (EFI_ERROR (Status)) {\r
961 goto ON_EXIT;\r
962 }\r
963 \r
964 ASSERT (Mode.Ia->State == Dhcp6Bound);\r
965 CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));\r
966 \r
967 AsciiPrint ("\n Station IPv6 address is ");\r
968 HttpBootShowIp6Addr (&Private->StationIp.v6);\r
969 AsciiPrint ("\n");\r
970 \r
971ON_EXIT:\r
972 if (EFI_ERROR (Status)) {\r
973 Dhcp6->Stop (Dhcp6);\r
974 Dhcp6->Configure (Dhcp6, NULL);\r
975 } else {\r
976 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
977 ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));\r
978 Dhcp6->Configure (Dhcp6, &Config);\r
979 }\r
980\r
981 return Status; \r
982 \r
983}\r
984\r