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