]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
NetworkPkg: remove unnecessary timeout event when setting IPv6 address.
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcDhcp6.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 Functions implementation related with DHCPv6 for UefiPxeBc Driver.\r
3\r
8f586b85 4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
903d1fa9 5 Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>\r
a3bcde70
HT
6\r
7 This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions of the BSD License\r
9 which accompanies this distribution. The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php.\r
11\r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
17#include "PxeBcImpl.h"\r
18\r
d40002ba 19//\r
20// Well-known multi-cast address defined in section-24.1 of rfc-3315\r
21//\r
22// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2\r
23//\r
24EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};\r
a3bcde70
HT
25\r
26/**\r
27 Parse out a DHCPv6 option by OptTag, and find the position in buffer.\r
28\r
29 @param[in] Buffer The pointer to the option buffer.\r
30 @param[in] Length Length of the option buffer.\r
31 @param[in] OptTag The required option tag.\r
32\r
33 @retval NULL Failed to parse the required option.\r
34 @retval Others The postion of the required option in buffer.\r
35\r
36**/\r
37EFI_DHCP6_PACKET_OPTION *\r
38PxeBcParseDhcp6Options (\r
39 IN UINT8 *Buffer,\r
40 IN UINT32 Length,\r
41 IN UINT16 OptTag\r
42 )\r
43{\r
44 EFI_DHCP6_PACKET_OPTION *Option;\r
45 UINT32 Offset;\r
46\r
47 Option = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
48 Offset = 0;\r
49\r
50 //\r
51 // OpLen and OpCode here are both stored in network order.\r
52 //\r
53 while (Offset < Length) {\r
54\r
55 if (NTOHS (Option->OpCode) == OptTag) {\r
56\r
57 return Option;\r
58 }\r
59\r
60 Offset += (NTOHS(Option->OpLen) + 4);\r
61 Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);\r
62 }\r
63\r
64 return NULL;\r
65}\r
66\r
67\r
68/**\r
69 Build the options buffer for the DHCPv6 request packet.\r
70\r
71 @param[in] Private The pointer to PxeBc private data.\r
72 @param[out] OptList The pointer to the option pointer array.\r
73 @param[in] Buffer The pointer to the buffer to contain the option list.\r
74\r
75 @return Index The count of the built-in options.\r
76\r
77**/\r
78UINT32\r
79PxeBcBuildDhcp6Options (\r
80 IN PXEBC_PRIVATE_DATA *Private,\r
81 OUT EFI_DHCP6_PACKET_OPTION **OptList,\r
82 IN UINT8 *Buffer\r
83 )\r
84{\r
85 PXEBC_DHCP6_OPTION_ENTRY OptEnt;\r
86 UINT32 Index;\r
87 UINT16 Value;\r
88\r
89 Index = 0;\r
90 OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer;\r
91\r
92 //\r
93 // Append client option request option\r
94 //\r
95 OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ORO);\r
96 OptList[Index]->OpLen = HTONS (4);\r
97 OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;\r
98 OptEnt.Oro->OpCode[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL);\r
99 OptEnt.Oro->OpCode[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM);\r
100 Index++;\r
101 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
102\r
103 //\r
104 // Append client network device interface option\r
105 //\r
106 OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_UNDI);\r
852b6341 107 OptList[Index]->OpLen = HTONS ((UINT16)3);\r
a3bcde70
HT
108 OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data;\r
109\r
110 if (Private->Nii != NULL) {\r
111 OptEnt.Undi->Type = Private->Nii->Type;\r
112 OptEnt.Undi->MajorVer = Private->Nii->MajorVer;\r
113 OptEnt.Undi->MinorVer = Private->Nii->MinorVer;\r
114 } else {\r
115 OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;\r
116 OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;\r
117 OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;\r
118 }\r
119\r
a3bcde70
HT
120 Index++;\r
121 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
122\r
123 //\r
124 // Append client system architecture option\r
125 //\r
126 OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ARCH);\r
127 OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));\r
128 OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;\r
129 Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);\r
130 CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));\r
131 Index++;\r
132 OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);\r
133\r
134 //\r
135 // Append vendor class option to store the PXE class identifier.\r
136 //\r
137 OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS);\r
138 OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));\r
139 OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;\r
140 OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);\r
141 OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));\r
142 CopyMem (\r
143 &OptEnt.VendorClass->ClassId,\r
144 DEFAULT_CLASS_ID_DATA,\r
145 sizeof (PXEBC_CLASS_ID)\r
146 );\r
147 PxeBcUintnToAscDecWithFormat (\r
148 EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,\r
149 OptEnt.VendorClass->ClassId.ArchitectureType,\r
150 sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)\r
151 );\r
152\r
153 if (Private->Nii != NULL) {\r
154 CopyMem (\r
155 OptEnt.VendorClass->ClassId.InterfaceName,\r
156 Private->Nii->StringId,\r
157 sizeof (OptEnt.VendorClass->ClassId.InterfaceName)\r
158 );\r
159 PxeBcUintnToAscDecWithFormat (\r
160 Private->Nii->MajorVer,\r
161 OptEnt.VendorClass->ClassId.UndiMajor,\r
162 sizeof (OptEnt.VendorClass->ClassId.UndiMajor)\r
163 );\r
164 PxeBcUintnToAscDecWithFormat (\r
165 Private->Nii->MinorVer,\r
166 OptEnt.VendorClass->ClassId.UndiMinor,\r
167 sizeof (OptEnt.VendorClass->ClassId.UndiMinor)\r
168 );\r
169 }\r
170\r
171 Index++;\r
172\r
173 return Index;\r
174}\r
175\r
176\r
177/**\r
178 Cache the DHCPv6 packet.\r
179\r
180 @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.\r
181 @param[in] Src The pointer to the DHCPv6 packet to be cached.\r
182\r
183**/\r
184VOID\r
185PxeBcCacheDhcp6Packet (\r
186 IN EFI_DHCP6_PACKET *Dst,\r
187 IN EFI_DHCP6_PACKET *Src\r
188 )\r
189{\r
190 ASSERT (Dst->Size >= Src->Length);\r
191\r
192 CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);\r
193 Dst->Length = Src->Length;\r
194}\r
195\r
196\r
197/**\r
198 Free all the nodes in the list for boot file.\r
199\r
200 @param[in] Head The pointer to the head of list.\r
201\r
202**/\r
203VOID\r
204PxeBcFreeBootFileOption (\r
205 IN LIST_ENTRY *Head\r
206 )\r
207{\r
208 LIST_ENTRY *Entry;\r
209 LIST_ENTRY *NextEntry;\r
210 PXEBC_DHCP6_OPTION_NODE *Node;\r
211\r
212 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {\r
213 Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);\r
214 RemoveEntryList (Entry);\r
215 FreePool (Node);\r
216 }\r
217}\r
218\r
219\r
220/**\r
221 Parse the Boot File URL option.\r
222\r
223 @param[out] FileName The pointer to the boot file name.\r
224 @param[in, out] SrvAddr The pointer to the boot server address.\r
225 @param[in] BootFile The pointer to the boot file URL option data.\r
226 @param[in] Length The length of the boot file URL option data.\r
227\r
228 @retval EFI_ABORTED User cancel operation.\r
229 @retval EFI_SUCCESS Selected the boot menu successfully.\r
230 @retval EFI_NOT_READY Read the input key from the keybroad has not finish.\r
231\r
232**/\r
233EFI_STATUS\r
234PxeBcExtractBootFileUrl (\r
235 OUT UINT8 **FileName,\r
236 IN OUT EFI_IPv6_ADDRESS *SrvAddr,\r
237 IN CHAR8 *BootFile,\r
238 IN UINT16 Length\r
239 )\r
240{\r
241 UINT16 PrefixLen;\r
01d89e9c 242 CHAR8 *BootFileNamePtr;\r
243 CHAR8 *BootFileName;\r
a3bcde70
HT
244 UINT16 BootFileNameLen;\r
245 CHAR8 *TmpStr;\r
129b8b09 246 CHAR8 TmpChar;\r
a3bcde70
HT
247 CHAR8 *ServerAddressOption;\r
248 CHAR8 *ServerAddress;\r
eb2710af 249 CHAR8 *ModeStr;\r
a3bcde70
HT
250 EFI_STATUS Status;\r
251\r
252 //\r
253 // The format of the Boot File URL option is:\r
254 //\r
255 // 0 1 2 3\r
256 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r
257 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
258 // | OPT_BOOTFILE_URL | option-len |\r
259 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
260 // | |\r
261 // . bootfile-url (variable length) .\r
262 // | |\r
263 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
264 //\r
265\r
266 //\r
c01fa590 267 // Based upon RFC 5970 and UEFI 2.3 Errata D specification, bootfile-url format\r
268 // is tftp://[SERVER_ADDRESS]/BOOTFILE_NAME\r
a3bcde70
HT
269 // As an example where the BOOTFILE_NAME is the EFI loader and\r
270 // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.\r
271 //\r
272 PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);\r
273\r
274 if (Length <= PrefixLen ||\r
275 CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {\r
276 return EFI_NOT_FOUND;\r
277 }\r
278\r
279 BootFile = BootFile + PrefixLen;\r
280 Length = (UINT16) (Length - PrefixLen);\r
281\r
282 TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);\r
283 if (TmpStr == NULL) {\r
284 return EFI_OUT_OF_RESOURCES;\r
285 }\r
286\r
287 CopyMem (TmpStr, BootFile, Length);\r
288 TmpStr[Length] = '\0';\r
289\r
290 //\r
291 // Get the part of SERVER_ADDRESS string.\r
292 //\r
293 ServerAddressOption = TmpStr;\r
294 if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) {\r
295 FreePool (TmpStr);\r
296 return EFI_INVALID_PARAMETER;\r
297 }\r
298\r
299 ServerAddressOption ++;\r
300 ServerAddress = ServerAddressOption;\r
301 while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
302 ServerAddress++;\r
303 }\r
304\r
305 if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {\r
306 FreePool (TmpStr);\r
307 return EFI_INVALID_PARAMETER;\r
308 }\r
309\r
310 *ServerAddress = '\0';\r
311\r
312 //\r
313 // Convert the string of server address to Ipv6 address format and store it.\r
314 //\r
315 Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);\r
316 if (EFI_ERROR (Status)) {\r
317 FreePool (TmpStr);\r
318 return Status;\r
319 }\r
320\r
321 //\r
322 // Get the part of BOOTFILE_NAME string.\r
323 //\r
01d89e9c 324 BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);\r
a3bcde70
HT
325 if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {\r
326 FreePool (TmpStr);\r
327 return EFI_INVALID_PARAMETER;\r
328 }\r
329\r
330 ++BootFileNamePtr;\r
331 BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);\r
332 if (BootFileNameLen != 0 || FileName != NULL) {\r
eb2710af 333 //\r
334 // Remove trailing mode=octet if present and ignore. All other modes are\r
335 // invalid for netboot6, so reject them.\r
336 //\r
337 ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet");\r
338 if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') {\r
339 *ModeStr = '\0';\r
340 } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) {\r
341 return EFI_INVALID_PARAMETER;\r
342 }\r
343\r
129b8b09 344 //\r
345 // Extract boot file name from URL.\r
346 //\r
01d89e9c 347 BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen);\r
a3bcde70
HT
348 if (BootFileName == NULL) {\r
349 FreePool (TmpStr);\r
350 return EFI_OUT_OF_RESOURCES;\r
351 }\r
01d89e9c 352 *FileName = (UINT8*) BootFileName;\r
a3bcde70 353\r
129b8b09 354 //\r
355 // Decode percent-encoding in boot file name.\r
356 //\r
357 while (*BootFileNamePtr != '\0') {\r
358 if (*BootFileNamePtr == '%') {\r
359 TmpChar = *(BootFileNamePtr+ 3);\r
360 *(BootFileNamePtr+ 3) = '\0';\r
2f3f1a64 361 *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1));\r
129b8b09 362 BootFileName++;\r
363 *(BootFileNamePtr+ 3) = TmpChar;\r
364 BootFileNamePtr += 3;\r
365 } else {\r
366 *BootFileName = *BootFileNamePtr;\r
367 BootFileName++;\r
368 BootFileNamePtr++;\r
369 }\r
370 }\r
371 *BootFileName = '\0';\r
372 }\r
a3bcde70
HT
373\r
374 FreePool (TmpStr);\r
375\r
376 return EFI_SUCCESS;\r
377}\r
378\r
379\r
380/**\r
381 Parse the Boot File Parameter option.\r
382\r
383 @param[in] BootFilePara The pointer to boot file parameter option data.\r
384 @param[out] BootFileSize The pointer to the parsed boot file size.\r
385\r
386 @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option.\r
387 @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option.\r
388\r
389**/\r
390EFI_STATUS\r
391PxeBcExtractBootFileParam (\r
392 IN CHAR8 *BootFilePara,\r
393 OUT UINT16 *BootFileSize\r
394 )\r
395{\r
396 UINT16 Length;\r
397 UINT8 Index;\r
398 UINT8 Digit;\r
399 UINT32 Size;\r
400\r
401 CopyMem (&Length, BootFilePara, sizeof (UINT16));\r
402 Length = NTOHS (Length);\r
403\r
404 //\r
405 // The BootFile Size should be 1~5 byte ASCII strings\r
406 //\r
407 if (Length < 1 || Length > 5) {\r
408 return EFI_NOT_FOUND;\r
409 }\r
410\r
411 //\r
412 // Extract the value of BootFile Size.\r
413 //\r
414 BootFilePara = BootFilePara + sizeof (UINT16);\r
415 Size = 0;\r
416 for (Index = 0; Index < Length; Index++) {\r
417 if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {\r
418 return EFI_NOT_FOUND;\r
419 }\r
420\r
421 Size = (Size + Digit) * 10;\r
422 }\r
423\r
424 Size = Size / 10;\r
425 if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {\r
426 return EFI_NOT_FOUND;\r
427 }\r
428\r
429 *BootFileSize = (UINT16) Size;\r
430 return EFI_SUCCESS;\r
431}\r
432\r
433\r
434/**\r
435 Parse the cached DHCPv6 packet, including all the options.\r
436\r
437 @param[in] Cache6 The pointer to a cached DHCPv6 packet.\r
438\r
439 @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.\r
440 @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.\r
441\r
442**/\r
443EFI_STATUS\r
444PxeBcParseDhcp6Packet (\r
445 IN PXEBC_DHCP6_PACKET_CACHE *Cache6\r
446 )\r
447{\r
448 EFI_DHCP6_PACKET *Offer;\r
449 EFI_DHCP6_PACKET_OPTION **Options;\r
450 EFI_DHCP6_PACKET_OPTION *Option;\r
451 PXEBC_OFFER_TYPE OfferType;\r
452 BOOLEAN IsProxyOffer;\r
453 BOOLEAN IsPxeOffer;\r
454 UINT32 Offset;\r
455 UINT32 Length;\r
456 UINT32 EnterpriseNum;\r
457\r
458 IsProxyOffer = TRUE;\r
459 IsPxeOffer = FALSE;\r
460 Offer = &Cache6->Packet.Offer;\r
461 Options = Cache6->OptList;\r
462\r
463 ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));\r
464\r
465 Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);\r
466 Offset = 0;\r
467 Length = GET_DHCP6_OPTION_SIZE (Offer);\r
468\r
469 //\r
470 // OpLen and OpCode here are both stored in network order, since they are from original packet.\r
471 //\r
472 while (Offset < Length) {\r
473\r
474 if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) {\r
475 Options[PXEBC_DHCP6_IDX_IA_NA] = Option;\r
476 } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) {\r
477 //\r
478 // The server sends this option to inform the client about an URL to a boot file.\r
479 //\r
480 Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;\r
481 } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) {\r
482 Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;\r
483 } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) {\r
484 Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;\r
485 }\r
486\r
487 Offset += (NTOHS (Option->OpLen) + 4);\r
488 Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);\r
489 }\r
490\r
491 //\r
ae97201c 492 // The offer with assigned client address is NOT a proxy offer.\r
a3bcde70
HT
493 // An ia_na option, embeded with valid ia_addr option and a status_code of success.\r
494 //\r
495 Option = Options[PXEBC_DHCP6_IDX_IA_NA];\r
129b8b09 496 if (Option != NULL) {\r
a3bcde70
HT
497 Option = PxeBcParseDhcp6Options (\r
498 Option->Data + 12,\r
499 NTOHS (Option->OpLen),\r
500 PXEBC_DHCP6_OPT_STATUS_CODE\r
501 );\r
129b8b09 502 if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {\r
a3bcde70
HT
503 IsProxyOffer = FALSE;\r
504 }\r
505 }\r
506\r
507 //\r
508 // The offer with "PXEClient" is a pxe offer.\r
509 //\r
510 Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];\r
129b8b09 511 EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM);\r
512\r
a3bcde70
HT
513 if (Option != NULL &&\r
514 NTOHS(Option->OpLen) >= 13 &&\r
515 CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&\r
129b8b09 516 CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0) {\r
a3bcde70
HT
517 IsPxeOffer = TRUE;\r
518 }\r
519\r
520 //\r
521 // Determine offer type of the dhcp6 packet.\r
522 //\r
523 if (IsPxeOffer) {\r
524 //\r
525 // It's a binl offer only with PXEClient.\r
526 //\r
527 OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;\r
528 } else {\r
529 //\r
530 // It's a dhcp only offer, which is a pure dhcp6 offer packet.\r
531 //\r
532 OfferType = PxeOfferTypeDhcpOnly;\r
533 }\r
534\r
535 Cache6->OfferType = OfferType;\r
536\r
537 return EFI_SUCCESS;\r
538}\r
539\r
540\r
541/**\r
542 Cache the DHCPv6 ack packet, and parse it on demand.\r
543\r
544 @param[in] Private The pointer to PxeBc private data.\r
545 @param[in] Ack The pointer to the DHCPv6 ack packet.\r
546 @param[in] Verified If TRUE, parse the ACK packet and store info into mode data.\r
547\r
548**/\r
549VOID\r
550PxeBcCopyDhcp6Ack (\r
551 IN PXEBC_PRIVATE_DATA *Private,\r
552 IN EFI_DHCP6_PACKET *Ack,\r
553 IN BOOLEAN Verified\r
554 )\r
555{\r
556 EFI_PXE_BASE_CODE_MODE *Mode;\r
557\r
558 Mode = Private->PxeBc.Mode;\r
559\r
560 PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);\r
561\r
562 if (Verified) {\r
563 //\r
564 // Parse the ack packet and store it into mode data if needed.\r
565 //\r
566 PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);\r
567 CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);\r
568 Mode->DhcpAckReceived = TRUE;\r
569 }\r
570}\r
571\r
572\r
573/**\r
574 Cache the DHCPv6 proxy offer packet according to the received order.\r
575\r
576 @param[in] Private The pointer to PxeBc private data.\r
577 @param[in] OfferIndex The received order of offer packets.\r
578\r
579**/\r
580VOID\r
581PxeBcCopyDhcp6Proxy (\r
582 IN PXEBC_PRIVATE_DATA *Private,\r
583 IN UINT32 OfferIndex\r
584 )\r
585{\r
586 EFI_PXE_BASE_CODE_MODE *Mode;\r
587 EFI_DHCP6_PACKET *Offer;\r
588\r
589 ASSERT (OfferIndex < Private->OfferNum);\r
590 ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);\r
591\r
592 Mode = Private->PxeBc.Mode;\r
593 Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;\r
594\r
595 //\r
596 // Cache the proxy offer packet and parse it.\r
597 //\r
598 PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);\r
599 PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);\r
600\r
601 //\r
602 // Store this packet into mode data.\r
603 //\r
604 CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);\r
605 Mode->ProxyOfferReceived = TRUE;\r
606}\r
607\r
129b8b09 608/**\r
609 Seek the address of the first byte of the option header.\r
610\r
611 @param[in] Buf The pointer to the buffer.\r
612 @param[in] SeekLen The length to seek.\r
613 @param[in] OptType The option type.\r
614\r
615 @retval NULL If it failed to seek the option.\r
616 @retval others The position to the option.\r
617\r
618**/\r
619UINT8 *\r
620PxeBcDhcp6SeekOption (\r
621 IN UINT8 *Buf,\r
622 IN UINT32 SeekLen,\r
623 IN UINT16 OptType\r
624 )\r
625{\r
626 UINT8 *Cursor;\r
627 UINT8 *Option;\r
628 UINT16 DataLen;\r
629 UINT16 OpCode;\r
630\r
631 Option = NULL;\r
632 Cursor = Buf;\r
633\r
634 while (Cursor < Buf + SeekLen) {\r
635 OpCode = ReadUnaligned16 ((UINT16 *) Cursor);\r
636 if (OpCode == HTONS (OptType)) {\r
637 Option = Cursor;\r
638 break;\r
639 }\r
640 DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
641 Cursor += (DataLen + 4);\r
642 }\r
643\r
644 return Option;\r
645}\r
646\r
647\r
648/**\r
649 Build and send out the request packet for the bootfile, and parse the reply.\r
650\r
651 @param[in] Private The pointer to PxeBc private data.\r
652 @param[in] Index PxeBc option boot item type.\r
653\r
654 @retval EFI_SUCCESS Successfully discovered the boot file.\r
655 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
656 @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
657 @retval Others Failed to discover the boot file.\r
658\r
659**/\r
660EFI_STATUS\r
661PxeBcRequestBootService (\r
662 IN PXEBC_PRIVATE_DATA *Private,\r
663 IN UINT32 Index\r
664 )\r
665{\r
666 EFI_PXE_BASE_CODE_UDP_PORT SrcPort;\r
667 EFI_PXE_BASE_CODE_UDP_PORT DestPort;\r
129b8b09 668 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
669 EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover;\r
670 UINTN DiscoverLen;\r
671 EFI_DHCP6_PACKET *Request;\r
672 UINTN RequestLen;\r
673 EFI_DHCP6_PACKET *Reply;\r
674 UINT8 *RequestOpt;\r
675 UINT8 *DiscoverOpt;\r
676 UINTN ReadSize;\r
677 UINT16 OpFlags;\r
678 UINT16 OpCode;\r
679 UINT16 OpLen;\r
680 EFI_STATUS Status;\r
681 EFI_DHCP6_PACKET *ProxyOffer;\r
682 UINT8 *Option;\r
683\r
684 PxeBc = &Private->PxeBc;\r
129b8b09 685 Request = Private->Dhcp6Request;\r
686 ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer;\r
687 SrcPort = PXEBC_BS_DISCOVER_PORT;\r
688 DestPort = PXEBC_BS_DISCOVER_PORT;\r
689 OpFlags = 0;\r
690\r
691 if (Request == NULL) {\r
692 return EFI_DEVICE_ERROR;\r
693 }\r
694\r
695 Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));\r
696 if (Discover == NULL) {\r
697 return EFI_OUT_OF_RESOURCES;\r
698 }\r
699\r
700 //\r
701 // Build the request packet by the cached request packet before.\r
702 //\r
703 Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId;\r
704 Discover->MessageType = Request->Dhcp6.Header.MessageType;\r
705 RequestOpt = Request->Dhcp6.Option;\r
706 DiscoverOpt = Discover->DhcpOptions;\r
707 DiscoverLen = sizeof (EFI_DHCP6_HEADER);\r
708 RequestLen = DiscoverLen;\r
709\r
710 //\r
711 // Find Server ID Option from ProxyOffer.\r
712 //\r
713 Option = PxeBcDhcp6SeekOption (\r
714 ProxyOffer->Dhcp6.Option,\r
715 ProxyOffer->Length - 4,\r
716 PXEBC_DHCP6_OPT_SERVER_ID\r
717 );\r
718 if (Option == NULL) {\r
719 return EFI_NOT_FOUND;\r
720 }\r
721 \r
722 //\r
723 // Add Server ID Option.\r
724 //\r
725 OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen);\r
726 CopyMem (DiscoverOpt, Option, OpLen + 4);\r
727 DiscoverOpt += (OpLen + 4);\r
728 DiscoverLen += (OpLen + 4);\r
729\r
730 while (RequestLen < Request->Length) {\r
731 OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);\r
732 OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);\r
733 if (OpCode != EFI_DHCP6_IA_TYPE_NA &&\r
734 OpCode != EFI_DHCP6_IA_TYPE_TA &&\r
735 OpCode != PXEBC_DHCP6_OPT_SERVER_ID\r
736 ) {\r
737 //\r
738 // Copy all the options except IA option and Server ID\r
739 //\r
740 CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);\r
741 DiscoverOpt += (OpLen + 4);\r
742 DiscoverLen += (OpLen + 4);\r
743 }\r
744 RequestOpt += (OpLen + 4);\r
745 RequestLen += (OpLen + 4);\r
746 }\r
747\r
748 //\r
749 // Update Elapsed option in the package \r
750 //\r
751 Option = PxeBcDhcp6SeekOption (\r
752 Discover->DhcpOptions,\r
753 (UINT32)(RequestLen - 4),\r
754 PXEBC_DHCP6_OPT_ELAPSED_TIME\r
755 );\r
756 if (Option != NULL) {\r
757 CalcElapsedTime (Private);\r
758 WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime));\r
759 } \r
760\r
761 Status = PxeBc->UdpWrite (\r
762 PxeBc,\r
763 OpFlags,\r
764 &Private->ServerIp,\r
765 &DestPort,\r
766 NULL,\r
767 &Private->StationIp,\r
768 &SrcPort,\r
769 NULL,\r
770 NULL,\r
771 &DiscoverLen,\r
772 (VOID *) Discover\r
773 );\r
774\r
775 if (EFI_ERROR (Status)) {\r
776 return Status;\r
777 }\r
778\r
779 //\r
780 // Cache the right PXE reply packet here, set valid flag later.\r
781 // Especially for PXE discover packet, store it into mode data here.\r
782 //\r
783 Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;\r
784 ReadSize = (UINTN) Reply->Size;\r
785\r
786 //\r
787 // Start Udp6Read instance\r
788 //\r
789 Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);\r
790 if (EFI_ERROR (Status)) {\r
791 return Status;\r
792 }\r
793 \r
794 Status = PxeBc->UdpRead (\r
795 PxeBc,\r
903d1fa9
FS
796 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,\r
797 NULL,\r
129b8b09 798 &SrcPort,\r
799 &Private->ServerIp,\r
800 &DestPort,\r
801 NULL,\r
802 NULL,\r
803 &ReadSize,\r
804 (VOID *) &Reply->Dhcp6\r
805 );\r
806 //\r
807 // Stop Udp6Read instance\r
808 //\r
809 Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
810\r
811 if (EFI_ERROR (Status)) {\r
812 return Status;\r
813 }\r
814\r
815 //\r
816 // Update length\r
817 //\r
818 Reply->Length = (UINT32) ReadSize;\r
819\r
820 return EFI_SUCCESS;\r
821}\r
822\r
a3bcde70
HT
823\r
824/**\r
825 Retry to request bootfile name by the BINL offer.\r
826\r
827 @param[in] Private The pointer to PxeBc private data.\r
828 @param[in] Index The received order of offer packets.\r
829\r
830 @retval EFI_SUCCESS Successfully retried a request for the bootfile name.\r
831 @retval EFI_DEVICE_ERROR Failed to retry the bootfile name.\r
832\r
833**/\r
834EFI_STATUS\r
835PxeBcRetryDhcp6Binl (\r
836 IN PXEBC_PRIVATE_DATA *Private,\r
837 IN UINT32 Index\r
838 )\r
839{\r
840 EFI_PXE_BASE_CODE_MODE *Mode;\r
841 PXEBC_DHCP6_PACKET_CACHE *Offer;\r
842 PXEBC_DHCP6_PACKET_CACHE *Cache6;\r
a3bcde70
HT
843 EFI_STATUS Status;\r
844\r
845 ASSERT (Index < PXEBC_OFFER_MAX_NUM);\r
846 ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||\r
847 Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);\r
848\r
849 Mode = Private->PxeBc.Mode;\r
850 Private->IsDoDiscover = FALSE;\r
851 Offer = &Private->OfferBuffer[Index].Dhcp6;\r
d40002ba 852 if (Offer->OfferType == PxeOfferTypeDhcpBinl) {\r
853 //\r
854 // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.\r
855 //\r
856 CopyMem (\r
857 &Private->ServerIp.v6,\r
858 &mAllDhcpRelayAndServersAddress,\r
859 sizeof (EFI_IPv6_ADDRESS)\r
860 );\r
861 } else {\r
862 ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
863 //\r
864 // Parse out the next server address from the last offer, and store it\r
865 //\r
866 Status = PxeBcExtractBootFileUrl (\r
867 &Private->BootFileName,\r
868 &Private->ServerIp.v6,\r
869 (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
870 NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
871 );\r
872 if (EFI_ERROR (Status)) {\r
873 return Status;\r
874 }\r
a3bcde70
HT
875 }\r
876\r
877 //\r
878 // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.\r
879 //\r
129b8b09 880 Status = PxeBcRequestBootService (Private, Index);\r
881\r
a3bcde70
HT
882 if (EFI_ERROR (Status)) {\r
883 return Status;\r
884 }\r
885\r
886 Cache6 = &Private->ProxyOffer.Dhcp6;\r
887 Status = PxeBcParseDhcp6Packet (Cache6);\r
888 if (EFI_ERROR (Status)) {\r
889 return Status;\r
890 }\r
891\r
892 if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&\r
893 Cache6->OfferType != PxeOfferTypeProxyWfm11a &&\r
894 Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
895 //\r
896 // This BINL ack doesn't have discovery option set or multicast option set\r
897 // or bootfile name specified.\r
898 //\r
899 return EFI_DEVICE_ERROR;\r
900 }\r
901\r
902 Mode->ProxyOfferReceived = TRUE;\r
903 CopyMem (\r
904 &Mode->ProxyOffer.Dhcpv6,\r
905 &Cache6->Packet.Offer.Dhcp6,\r
906 Cache6->Packet.Offer.Length\r
907 );\r
908\r
909 return EFI_SUCCESS;\r
910}\r
911\r
912\r
913/**\r
914 Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.\r
915\r
916 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
917 @param[in] RcvdOffer The pointer to the received offer packet.\r
918\r
919**/\r
920VOID\r
921PxeBcCacheDhcp6Offer (\r
922 IN PXEBC_PRIVATE_DATA *Private,\r
923 IN EFI_DHCP6_PACKET *RcvdOffer\r
924 )\r
925{\r
926 PXEBC_DHCP6_PACKET_CACHE *Cache6;\r
927 EFI_DHCP6_PACKET *Offer;\r
928 PXEBC_OFFER_TYPE OfferType;\r
929\r
930 Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;\r
931 Offer = &Cache6->Packet.Offer;\r
932\r
933 //\r
934 // Cache the content of DHCPv6 packet firstly.\r
935 //\r
936 PxeBcCacheDhcp6Packet (Offer, RcvdOffer);\r
937\r
938 //\r
939 // Validate the DHCPv6 packet, and parse the options and offer type.\r
940 //\r
941 if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {\r
942 return ;\r
943 }\r
944\r
945 //\r
946 // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.\r
947 //\r
948 OfferType = Cache6->OfferType;\r
949 ASSERT (OfferType < PxeOfferTypeMax);\r
950 ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);\r
951\r
952 if (IS_PROXY_OFFER (OfferType)) {\r
953 //\r
954 // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.\r
955 //\r
956 Private->IsProxyRecved = TRUE;\r
957\r
958 if (OfferType == PxeOfferTypeProxyBinl) {\r
959 //\r
960 // Cache all proxy BINL offers.\r
961 //\r
962 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
963 Private->OfferCount[OfferType]++;\r
964 } else if (Private->OfferCount[OfferType] > 0) {\r
965 //\r
966 // Only cache the first PXE10/WFM11a offer, and discard the others.\r
967 //\r
968 Private->OfferIndex[OfferType][0] = Private->OfferNum;\r
969 Private->OfferCount[OfferType] = 1;\r
970 } else {\r
971 return;\r
972 }\r
973 } else {\r
974 //\r
975 // It's a DHCPv6 offer with yiaddr, and cache them all.\r
976 //\r
977 Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;\r
978 Private->OfferCount[OfferType]++;\r
979 }\r
980\r
981 Private->OfferNum++;\r
982}\r
983\r
984\r
985/**\r
986 Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.\r
987\r
988 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
989\r
990**/\r
991VOID\r
992PxeBcSelectDhcp6Offer (\r
993 IN PXEBC_PRIVATE_DATA *Private\r
994 )\r
995{\r
996 UINT32 Index;\r
997 UINT32 OfferIndex;\r
998 PXEBC_OFFER_TYPE OfferType;\r
999\r
1000 Private->SelectIndex = 0;\r
1001\r
1002 if (Private->IsOfferSorted) {\r
1003 //\r
1004 // Select offer by default policy.\r
1005 //\r
1006 if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {\r
1007 //\r
1008 // 1. DhcpPxe10 offer\r
1009 //\r
1010 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;\r
1011\r
1012 } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {\r
1013 //\r
1014 // 2. DhcpWfm11a offer\r
1015 //\r
1016 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;\r
1017\r
1018 } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
1019 Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {\r
1020 //\r
1021 // 3. DhcpOnly offer and ProxyPxe10 offer.\r
1022 //\r
1023 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
1024 Private->SelectProxyType = PxeOfferTypeProxyPxe10;\r
1025\r
1026 } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
1027 Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {\r
1028 //\r
1029 // 4. DhcpOnly offer and ProxyWfm11a offer.\r
1030 //\r
1031 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
1032 Private->SelectProxyType = PxeOfferTypeProxyWfm11a;\r
1033\r
1034 } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {\r
1035 //\r
1036 // 5. DhcpBinl offer.\r
1037 //\r
1038 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;\r
1039\r
1040 } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&\r
1041 Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {\r
1042 //\r
1043 // 6. DhcpOnly offer and ProxyBinl offer.\r
1044 //\r
1045 Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;\r
1046 Private->SelectProxyType = PxeOfferTypeProxyBinl;\r
1047\r
1048 } else {\r
1049 //\r
1050 // 7. DhcpOnly offer with bootfilename.\r
1051 //\r
1052 for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {\r
1053 OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];\r
1054 if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {\r
1055 Private->SelectIndex = OfferIndex + 1;\r
1056 break;\r
1057 }\r
1058 }\r
1059 }\r
1060 } else {\r
1061 //\r
1062 // Select offer by received order.\r
1063 //\r
1064 for (Index = 0; Index < Private->OfferNum; Index++) {\r
1065\r
1066 OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;\r
1067\r
1068 if (IS_PROXY_OFFER (OfferType)) {\r
1069 //\r
1070 // Skip proxy offers\r
1071 //\r
1072 continue;\r
1073 }\r
1074\r
1075 if (!Private->IsProxyRecved &&\r
1076 OfferType == PxeOfferTypeDhcpOnly &&\r
1077 Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {\r
1078 //\r
1079 // Skip if DhcpOnly offer without any other proxy offers or bootfilename.\r
1080 //\r
1081 continue;\r
1082 }\r
1083\r
1084 Private->SelectIndex = Index + 1;\r
1085 break;\r
1086 }\r
1087 }\r
1088}\r
1089\r
1090\r
1091/**\r
1092 Handle the DHCPv6 offer packet.\r
1093\r
1094 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
1095\r
1096 @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully.\r
1097 @retval EFI_NO_RESPONSE No response to the following request packet.\r
1098\r
1099**/\r
1100EFI_STATUS\r
1101PxeBcHandleDhcp6Offer (\r
1102 IN PXEBC_PRIVATE_DATA *Private\r
1103 )\r
1104{\r
1105 PXEBC_DHCP6_PACKET_CACHE *Cache6;\r
1106 EFI_STATUS Status;\r
1107 PXEBC_OFFER_TYPE OfferType;\r
1108 UINT32 ProxyIndex;\r
1109 UINT32 SelectIndex;\r
1110 UINT32 Index;\r
1111\r
1112 ASSERT (Private->SelectIndex > 0);\r
1113 SelectIndex = (UINT32) (Private->SelectIndex - 1);\r
1114 ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);\r
1115 Cache6 = &Private->OfferBuffer[SelectIndex].Dhcp6;\r
1116 Status = EFI_SUCCESS;\r
1117\r
1118 if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {\r
1119 //\r
1120 // DhcpBinl offer is selected, so need try to request bootfilename by this offer.\r
1121 //\r
1122 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {\r
1123 Status = EFI_NO_RESPONSE;\r
1124 }\r
1125 } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {\r
1126\r
1127 if (Private->IsProxyRecved) {\r
1128 //\r
1129 // DhcpOnly offer is selected, so need try to request bootfilename.\r
1130 //\r
1131 ProxyIndex = 0;\r
1132 if (Private->IsOfferSorted) {\r
1133 //\r
1134 // The proxy offer should be determined if select by default policy.\r
1135 // IsOfferSorted means all offers are labeled by OfferIndex.\r
1136 //\r
1137 ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);\r
1138\r
1139 if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {\r
1140 //\r
1141 // Try all the cached ProxyBinl offer one by one to request bootfilename.\r
1142 //\r
1143 for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {\r
1144\r
1145 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];\r
1146 if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {\r
1147 break;\r
1148 }\r
1149 }\r
1150 if (Index == Private->OfferCount[Private->SelectProxyType]) {\r
1151 Status = EFI_NO_RESPONSE;\r
1152 }\r
1153 } else {\r
1154 //\r
1155 // For other proxy offers (pxe10 or wfm11a), only one is buffered.\r
1156 //\r
1157 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
1158 }\r
1159 } else {\r
1160 //\r
1161 // The proxy offer should not be determined if select by received order.\r
1162 //\r
1163 Status = EFI_NO_RESPONSE;\r
1164\r
1165 for (Index = 0; Index < Private->OfferNum; Index++) {\r
1166\r
1167 OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;\r
1168\r
1169 if (!IS_PROXY_OFFER (OfferType)) {\r
1170 //\r
1171 // Skip non proxy dhcp offers.\r
1172 //\r
1173 continue;\r
1174 }\r
1175\r
1176 if (OfferType == PxeOfferTypeProxyBinl) {\r
1177 //\r
1178 // Try all the cached ProxyBinl offer one by one to request bootfilename.\r
1179 //\r
1180 if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {\r
1181 continue;\r
1182 }\r
1183 }\r
1184\r
1185 Private->SelectProxyType = OfferType;\r
1186 ProxyIndex = Index;\r
1187 Status = EFI_SUCCESS;\r
1188 break;\r
1189 }\r
1190 }\r
1191\r
1192 if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {\r
1193 //\r
1194 // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.\r
1195 //\r
1196 PxeBcCopyDhcp6Proxy (Private, ProxyIndex);\r
1197 }\r
1198 } else {\r
1199 //\r
1200 // Othewise, the bootfilename must be included in DhcpOnly offer.\r
1201 //\r
1202 ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
1203 }\r
1204 }\r
1205\r
1206 if (!EFI_ERROR (Status)) {\r
1207 //\r
1208 // All PXE boot information is ready by now.\r
1209 //\r
1210 PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);\r
1211 Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;\r
1212 }\r
1213\r
1214 return Status;\r
1215}\r
1216\r
1217\r
1218/**\r
1219 Unregister the address by Ip6Config protocol.\r
1220\r
1221 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
1222\r
1223**/\r
1224VOID\r
1225PxeBcUnregisterIp6Address (\r
1226 IN PXEBC_PRIVATE_DATA *Private\r
1227 )\r
1228{\r
1229 if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {\r
1230 //\r
1231 // PXE driver change the policy of IP6 driver, it's a chance to recover.\r
1232 // Keep the point and there is no enough requirements to do recovery.\r
1233 //\r
1234 }\r
1235}\r
1236\r
ae97201c
FS
1237/**\r
1238 Check whether IP driver could route the message which will be sent to ServerIp address.\r
1239 \r
1240 This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid\r
1241 route is found in IP6 route table, the address will be filed in GatewayAddr and return.\r
1242\r
1243 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
1244 @param[in] TimeOutInSecond Timeout value in seconds.\r
1245 @param[out] GatewayAddr Pointer to store the gateway IP address.\r
1246\r
1247 @retval EFI_SUCCESS Found a valid gateway address successfully.\r
1248 @retval EFI_TIMEOUT The operation is time out.\r
1249 @retval Other Unexpect error happened.\r
1250 \r
1251**/\r
1252EFI_STATUS\r
1253PxeBcCheckRouteTable (\r
1254 IN PXEBC_PRIVATE_DATA *Private,\r
1255 IN UINTN TimeOutInSecond,\r
1256 OUT EFI_IPv6_ADDRESS *GatewayAddr\r
1257 )\r
1258{\r
1259 EFI_STATUS Status;\r
1260 EFI_IP6_PROTOCOL *Ip6;\r
1261 EFI_IP6_MODE_DATA Ip6ModeData;\r
1262 UINTN Index;\r
1263 EFI_EVENT TimeOutEvt;\r
1264 UINTN RetryCount;\r
1265 BOOLEAN GatewayIsFound;\r
1266\r
1267 ASSERT (GatewayAddr != NULL);\r
1268 ASSERT (Private != NULL);\r
1269\r
1270 Ip6 = Private->Ip6;\r
1271 GatewayIsFound = FALSE;\r
1272 RetryCount = 0;\r
1273 TimeOutEvt = NULL;\r
1274 ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));\r
1275\r
1276 while (TRUE) {\r
1277 Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);\r
1278 if (EFI_ERROR (Status)) {\r
1279 goto ON_EXIT;\r
1280 }\r
1281\r
1282 //\r
07f986f1 1283 // Find out the gateway address which can route the message which send to ServerIp.\r
ae97201c
FS
1284 //\r
1285 for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {\r
1286 if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {\r
1287 IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);\r
1288 GatewayIsFound = TRUE;\r
1289 break;\r
1290 }\r
1291 }\r
1292\r
1293 if (Ip6ModeData.AddressList != NULL) {\r
1294 FreePool (Ip6ModeData.AddressList);\r
1295 }\r
1296 if (Ip6ModeData.GroupTable != NULL) {\r
1297 FreePool (Ip6ModeData.GroupTable);\r
1298 }\r
1299 if (Ip6ModeData.RouteTable != NULL) {\r
1300 FreePool (Ip6ModeData.RouteTable);\r
1301 }\r
1302 if (Ip6ModeData.NeighborCache != NULL) {\r
1303 FreePool (Ip6ModeData.NeighborCache);\r
1304 }\r
1305 if (Ip6ModeData.PrefixTable != NULL) {\r
1306 FreePool (Ip6ModeData.PrefixTable);\r
1307 }\r
1308 if (Ip6ModeData.IcmpTypeList != NULL) {\r
1309 FreePool (Ip6ModeData.IcmpTypeList);\r
1310 }\r
1311 \r
1312 if (GatewayIsFound || RetryCount == TimeOutInSecond) {\r
1313 break;\r
1314 }\r
1315 \r
1316 RetryCount++;\r
1317 \r
1318 //\r
1319 // Delay 1 second then recheck it again.\r
1320 //\r
1321 if (TimeOutEvt == NULL) {\r
1322 Status = gBS->CreateEvent (\r
1323 EVT_TIMER,\r
1324 TPL_CALLBACK,\r
1325 NULL,\r
1326 NULL,\r
1327 &TimeOutEvt\r
1328 );\r
1329 if (EFI_ERROR (Status)) {\r
1330 goto ON_EXIT;\r
1331 }\r
1332 }\r
1333\r
1334 Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);\r
1335 if (EFI_ERROR (Status)) {\r
1336 goto ON_EXIT;\r
1337 }\r
1338 while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
1339 Ip6->Poll (Ip6);\r
1340 }\r
1341 }\r
1342 \r
1343ON_EXIT:\r
1344 if (TimeOutEvt != NULL) {\r
1345 gBS->CloseEvent (TimeOutEvt);\r
1346 }\r
1347 \r
1348 if (GatewayIsFound) {\r
1349 Status = EFI_SUCCESS;\r
1350 } else if (RetryCount == TimeOutInSecond) {\r
1351 Status = EFI_TIMEOUT;\r
1352 }\r
1353\r
1354 return Status; \r
1355}\r
a3bcde70
HT
1356\r
1357/**\r
ae97201c 1358 Register the ready station address and gateway by Ip6Config protocol.\r
a3bcde70
HT
1359\r
1360 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
1361 @param[in] Address The pointer to the ready address.\r
1362\r
1363 @retval EFI_SUCCESS Registered the address succesfully.\r
1364 @retval Others Failed to register the address.\r
1365\r
1366**/\r
1367EFI_STATUS\r
1368PxeBcRegisterIp6Address (\r
1369 IN PXEBC_PRIVATE_DATA *Private,\r
1370 IN EFI_IPv6_ADDRESS *Address\r
1371 )\r
1372{\r
1373 EFI_IP6_PROTOCOL *Ip6;\r
1374 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
1375 EFI_IP6_CONFIG_POLICY Policy;\r
1376 EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr;\r
ae97201c 1377 EFI_IPv6_ADDRESS GatewayAddr;\r
a3bcde70 1378 UINTN DataSize;\r
a3bcde70
HT
1379 EFI_EVENT MappedEvt;\r
1380 EFI_STATUS Status;\r
07f986f1 1381 BOOLEAN NoGateway;\r
bf9f7cea
FS
1382 EFI_IPv6_ADDRESS *Ip6Addr;\r
1383 UINTN Index;\r
a3bcde70
HT
1384\r
1385 Status = EFI_SUCCESS;\r
a3bcde70 1386 MappedEvt = NULL;\r
bf9f7cea 1387 Ip6Addr = NULL;\r
a3bcde70
HT
1388 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
1389 Ip6Cfg = Private->Ip6Cfg;\r
1390 Ip6 = Private->Ip6;\r
07f986f1 1391 NoGateway = FALSE;\r
a3bcde70
HT
1392\r
1393 ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));\r
1394 CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));\r
1395\r
ae97201c
FS
1396 Status = Ip6->Configure (Ip6, &Private->Ip6CfgData);\r
1397 if (EFI_ERROR (Status)) {\r
1398 goto ON_EXIT;\r
1399 }\r
1400\r
a3bcde70 1401 //\r
ae97201c 1402 // Retrieve the gateway address from IP6 route table.\r
a3bcde70 1403 //\r
ae97201c 1404 Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);\r
a3bcde70 1405 if (EFI_ERROR (Status)) {\r
07f986f1 1406 NoGateway = TRUE;\r
a3bcde70 1407 }\r
ae97201c 1408 \r
a3bcde70
HT
1409 //\r
1410 // There is no channel between IP6 and PXE driver about address setting,\r
1411 // so it has to set the new address by Ip6ConfigProtocol manually.\r
1412 //\r
1413 Policy = Ip6ConfigPolicyManual;\r
1414 Status = Ip6Cfg->SetData (\r
1415 Ip6Cfg,\r
1416 Ip6ConfigDataTypePolicy,\r
1417 sizeof(EFI_IP6_CONFIG_POLICY),\r
1418 &Policy\r
1419 );\r
1420 if (EFI_ERROR (Status)) {\r
1421 //\r
1422 // There is no need to recover later.\r
1423 //\r
1424 Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;\r
1425 goto ON_EXIT;\r
1426 }\r
1427\r
a3bcde70
HT
1428 //\r
1429 // Create a notify event to set address flag when DAD if IP6 driver succeeded.\r
1430 //\r
1431 Status = gBS->CreateEvent (\r
1432 EVT_NOTIFY_SIGNAL,\r
1433 TPL_NOTIFY,\r
1434 PxeBcCommonNotify,\r
1435 &Private->IsAddressOk,\r
1436 &MappedEvt\r
1437 );\r
1438 if (EFI_ERROR (Status)) {\r
1439 goto ON_EXIT;\r
1440 }\r
1441\r
bf9f7cea 1442 Private->IsAddressOk = FALSE;\r
a3bcde70
HT
1443 Status = Ip6Cfg->RegisterDataNotify (\r
1444 Ip6Cfg,\r
1445 Ip6ConfigDataTypeManualAddress,\r
1446 MappedEvt\r
1447 );\r
1448 if (EFI_ERROR(Status)) {\r
1449 goto ON_EXIT;\r
1450 }\r
1451\r
1452 Status = Ip6Cfg->SetData (\r
1453 Ip6Cfg,\r
1454 Ip6ConfigDataTypeManualAddress,\r
1455 sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),\r
1456 &CfgAddr\r
1457 );\r
1458 if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {\r
1459 goto ON_EXIT;\r
bf9f7cea
FS
1460 } else if (Status == EFI_NOT_READY) {\r
1461 //\r
1462 // Poll the network until the asynchronous process is finished.\r
1463 //\r
1464 while (!Private->IsAddressOk) {\r
1465 Ip6->Poll (Ip6);\r
1466 }\r
1467 //\r
1468 // Check whether the IP6 address setting is successed.\r
1469 //\r
1470 DataSize = 0;\r
1471 Status = Ip6Cfg->GetData (\r
1472 Ip6Cfg,\r
1473 Ip6ConfigDataTypeManualAddress,\r
1474 &DataSize,\r
1475 NULL\r
1476 );\r
1477 if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {\r
1478 Status = EFI_DEVICE_ERROR;\r
1479 goto ON_EXIT;\r
1480 }\r
a3bcde70 1481\r
bf9f7cea
FS
1482 Ip6Addr = AllocatePool (DataSize);\r
1483 if (Ip6Addr == NULL) {\r
1484 return EFI_OUT_OF_RESOURCES;\r
1485 }\r
1486 Status = Ip6Cfg->GetData (\r
1487 Ip6Cfg,\r
1488 Ip6ConfigDataTypeManualAddress,\r
1489 &DataSize,\r
1490 (VOID*) Ip6Addr\r
1491 );\r
1492 if (EFI_ERROR (Status)) {\r
1493 Status = EFI_DEVICE_ERROR;\r
1494 goto ON_EXIT;\r
1495 }\r
a3bcde70 1496\r
bf9f7cea
FS
1497 for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) {\r
1498 if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) {\r
1499 break;\r
1500 }\r
1501 }\r
1502 if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {\r
1503 Status = EFI_ABORTED;\r
1504 goto ON_EXIT;\r
a3bcde70
HT
1505 }\r
1506 }\r
bf9f7cea 1507 \r
ae97201c
FS
1508 //\r
1509 // Set the default gateway address back if needed.\r
1510 //\r
07f986f1 1511 if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) {\r
ae97201c
FS
1512 Status = Ip6Cfg->SetData (\r
1513 Ip6Cfg,\r
1514 Ip6ConfigDataTypeGateway,\r
1515 sizeof (EFI_IPv6_ADDRESS),\r
1516 &GatewayAddr\r
1517 );\r
1518 if (EFI_ERROR (Status)) {\r
1519 goto ON_EXIT;\r
1520 }\r
1521 }\r
1522\r
a3bcde70
HT
1523ON_EXIT:\r
1524 if (MappedEvt != NULL) {\r
1525 Ip6Cfg->UnregisterDataNotify (\r
1526 Ip6Cfg,\r
1527 Ip6ConfigDataTypeManualAddress,\r
1528 MappedEvt\r
1529 );\r
1530 gBS->CloseEvent (MappedEvt);\r
1531 }\r
bf9f7cea
FS
1532 if (Ip6Addr != NULL) {\r
1533 FreePool (Ip6Addr);\r
a3bcde70
HT
1534 }\r
1535 return Status;\r
1536}\r
1537\r
ae97201c
FS
1538/**\r
1539 Set the IP6 policy to Automatic.\r
1540\r
1541 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
1542\r
1543 @retval EFI_SUCCESS Switch the IP policy succesfully.\r
1544 @retval Others Unexpect error happened.\r
1545\r
1546**/\r
1547EFI_STATUS\r
1548PxeBcSetIp6Policy (\r
1549 IN PXEBC_PRIVATE_DATA *Private\r
1550 )\r
1551{\r
1552 EFI_IP6_CONFIG_POLICY Policy;\r
1553 EFI_STATUS Status;\r
1554 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
1555 UINTN DataSize;\r
1556\r
1557 Ip6Cfg = Private->Ip6Cfg;\r
1558 DataSize = sizeof (EFI_IP6_CONFIG_POLICY);\r
1559\r
1560 //\r
1561 // Get and store the current policy of IP6 driver.\r
1562 //\r
1563 Status = Ip6Cfg->GetData (\r
1564 Ip6Cfg,\r
1565 Ip6ConfigDataTypePolicy,\r
1566 &DataSize,\r
1567 &Private->Ip6Policy\r
1568 );\r
1569 if (EFI_ERROR (Status)) {\r
1570 return Status;\r
1571 }\r
1572\r
1573 if (Private->Ip6Policy == Ip6ConfigPolicyManual) {\r
1574 Policy = Ip6ConfigPolicyAutomatic;\r
1575 Status = Ip6Cfg->SetData (\r
1576 Ip6Cfg,\r
1577 Ip6ConfigDataTypePolicy,\r
1578 sizeof(EFI_IP6_CONFIG_POLICY),\r
1579 &Policy\r
1580 );\r
1581 if (EFI_ERROR (Status)) {\r
1582 //\r
1583 // There is no need to recover later.\r
1584 //\r
1585 Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;\r
1586 }\r
1587 }\r
1588\r
1589 return Status;\r
1590}\r
1591\r
1592/**\r
1593 This function will register the station IP address and flush IP instance to start using the new IP address.\r
1594 \r
1595 @param[in] Private The pointer to PXEBC_PRIVATE_DATA.\r
1596\r
1597 @retval EFI_SUCCESS The new IP address has been configured successfully.\r
1598 @retval Others Failed to configure the address.\r
1599\r
1600**/\r
1601EFI_STATUS\r
1602PxeBcSetIp6Address (\r
1603 IN PXEBC_PRIVATE_DATA *Private\r
1604 )\r
1605{\r
1606 EFI_STATUS Status;\r
1607 EFI_DHCP6_PROTOCOL *Dhcp6;\r
1608 \r
1609 Dhcp6 = Private->Dhcp6;\r
1610\r
1611 CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
1612 CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
1613\r
1614 Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);\r
1615 if (EFI_ERROR (Status)) {\r
1616 Dhcp6->Stop (Dhcp6);\r
1617 return Status;\r
1618 }\r
1619\r
1620 Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL);\r
1621 if (EFI_ERROR (Status)) {\r
1622 PxeBcUnregisterIp6Address (Private);\r
1623 Dhcp6->Stop (Dhcp6);\r
1624 return Status;\r
1625 }\r
1626\r
1627 AsciiPrint ("\n Station IP address is ");\r
1628 PxeBcShowIp6Addr (&Private->StationIp.v6);\r
1629\r
1630 return EFI_SUCCESS;\r
1631}\r
a3bcde70
HT
1632\r
1633/**\r
1634 EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver\r
1635 to intercept events that occurred in the configuration process.\r
1636\r
1637 @param[in] This The pointer to the EFI DHCPv6 Protocol.\r
1638 @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().\r
1639 @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.\r
1640 @param[in] Dhcp6Event The event that occurs in the current state, which usually means a\r
1641 state transition.\r
1642 @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.\r
1643 @param[out] NewPacket The packet that is used to replace the Packet above.\r
1644\r
1645 @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.\r
1646 @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol\r
1647 driver will continue to wait for more packets.\r
1648 @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.\r
1649\r
1650**/\r
1651EFI_STATUS\r
1652EFIAPI\r
1653PxeBcDhcp6CallBack (\r
1654 IN EFI_DHCP6_PROTOCOL *This,\r
1655 IN VOID *Context,\r
1656 IN EFI_DHCP6_STATE CurrentState,\r
1657 IN EFI_DHCP6_EVENT Dhcp6Event,\r
1658 IN EFI_DHCP6_PACKET *Packet,\r
1659 OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL\r
1660 )\r
1661{\r
1662 PXEBC_PRIVATE_DATA *Private;\r
1663 EFI_PXE_BASE_CODE_MODE *Mode;\r
1664 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;\r
1665 EFI_DHCP6_PACKET *SelectAd;\r
1666 EFI_STATUS Status;\r
1667 BOOLEAN Received;\r
1668\r
1669 if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&\r
1670 (Dhcp6Event != Dhcp6SelectAdvertise) &&\r
1671 (Dhcp6Event != Dhcp6SendSolicit) &&\r
1672 (Dhcp6Event != Dhcp6SendRequest) &&\r
1673 (Dhcp6Event != Dhcp6RcvdReply)) {\r
1674 return EFI_SUCCESS;\r
1675 }\r
1676\r
1677 ASSERT (Packet != NULL);\r
1678\r
1679 Private = (PXEBC_PRIVATE_DATA *) Context;\r
1680 Mode = Private->PxeBc.Mode;\r
1681 Callback = Private->PxeBcCallback;\r
1682\r
1683 //\r
1684 // Callback to user when any traffic ocurred if has.\r
1685 //\r
1686 if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {\r
1687 Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);\r
1688 Status = Callback->Callback (\r
1689 Callback,\r
1690 Private->Function,\r
1691 Received,\r
1692 Packet->Length,\r
1693 (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6\r
1694 );\r
1695 if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {\r
1696 return EFI_ABORTED;\r
1697 }\r
1698 }\r
1699\r
1700 Status = EFI_SUCCESS;\r
1701\r
1702 switch (Dhcp6Event) {\r
1703\r
1704 case Dhcp6SendSolicit:\r
129b8b09 1705 //\r
1706 // Record the first Solicate msg time\r
1707 //\r
1708 if (Private->SolicitTimes == 0) {\r
1709 CalcElapsedTime (Private);\r
1710 Private->SolicitTimes++;\r
1711 }\r
a3bcde70
HT
1712 //\r
1713 // Cache the dhcp discover packet to mode data directly.\r
1714 //\r
1715 CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);\r
1716 break;\r
1717\r
1718 case Dhcp6RcvdAdvertise:\r
1719 Status = EFI_NOT_READY;\r
1720 if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {\r
1721 //\r
1722 // Cache the dhcp offers to OfferBuffer[] for select later, and record\r
1723 // the OfferIndex and OfferCount.\r
1724 //\r
1725 PxeBcCacheDhcp6Offer (Private, Packet);\r
1726 }\r
1727 break;\r
1728\r
1729 case Dhcp6SendRequest:\r
1730 //\r
1731 // Store the request packet as seed packet for discover.\r
1732 //\r
1733 if (Private->Dhcp6Request != NULL) {\r
1734 FreePool (Private->Dhcp6Request);\r
1735 }\r
1736 Private->Dhcp6Request = AllocateZeroPool (Packet->Size);\r
1737 if (Private->Dhcp6Request != NULL) {\r
1738 CopyMem (Private->Dhcp6Request, Packet, Packet->Size);\r
1739 }\r
1740 break;\r
1741\r
1742 case Dhcp6SelectAdvertise:\r
1743 //\r
1744 // Select offer by the default policy or by order, and record the SelectIndex\r
1745 // and SelectProxyType.\r
1746 //\r
1747 PxeBcSelectDhcp6Offer (Private);\r
1748\r
1749 if (Private->SelectIndex == 0) {\r
1750 Status = EFI_ABORTED;\r
1751 } else {\r
1752 ASSERT (NewPacket != NULL);\r
1753 SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;\r
1754 *NewPacket = AllocateZeroPool (SelectAd->Size);\r
1755 ASSERT (*NewPacket != NULL);\r
1756 CopyMem (*NewPacket, SelectAd, SelectAd->Size);\r
1757 }\r
1758 break;\r
1759\r
1760 case Dhcp6RcvdReply:\r
1761 //\r
1762 // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data\r
1763 // without verification.\r
1764 //\r
1765 ASSERT (Private->SelectIndex != 0);\r
1766 PxeBcCopyDhcp6Ack (Private, Packet, FALSE);\r
1767 break;\r
1768\r
1769 default:\r
1770 ASSERT (0);\r
1771 }\r
1772\r
1773 return Status;\r
1774}\r
1775\r
1776\r
1777/**\r
1778 Build and send out the request packet for the bootfile, and parse the reply.\r
1779\r
1780 @param[in] Private The pointer to PxeBc private data.\r
1781 @param[in] Type PxeBc option boot item type.\r
1782 @param[in] Layer The pointer to option boot item layer.\r
1783 @param[in] UseBis Use BIS or not.\r
1784 @param[in] DestIp The pointer to the server address.\r
1785\r
1786 @retval EFI_SUCCESS Successfully discovered the boot file.\r
1787 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
1788 @retval EFI_NOT_FOUND Can't get the PXE reply packet.\r
1789 @retval Others Failed to discover the boot file.\r
1790\r
1791**/\r
1792EFI_STATUS\r
1793PxeBcDhcp6Discover (\r
1794 IN PXEBC_PRIVATE_DATA *Private,\r
1795 IN UINT16 Type,\r
1796 IN UINT16 *Layer,\r
1797 IN BOOLEAN UseBis,\r
1798 IN EFI_IP_ADDRESS *DestIp\r
1799 )\r
1800{\r
1801 EFI_PXE_BASE_CODE_UDP_PORT SrcPort;\r
1802 EFI_PXE_BASE_CODE_UDP_PORT DestPort;\r
1803 EFI_PXE_BASE_CODE_MODE *Mode;\r
1804 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;\r
1805 EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover;\r
1806 UINTN DiscoverLen;\r
1807 EFI_DHCP6_PACKET *Request;\r
1808 UINTN RequestLen;\r
1809 EFI_DHCP6_PACKET *Reply;\r
1810 UINT8 *RequestOpt;\r
1811 UINT8 *DiscoverOpt;\r
1812 UINTN ReadSize;\r
a3bcde70
HT
1813 UINT16 OpCode;\r
1814 UINT16 OpLen;\r
1815 UINT32 Xid;\r
1816 EFI_STATUS Status;\r
1817\r
1818 PxeBc = &Private->PxeBc;\r
1819 Mode = PxeBc->Mode;\r
1820 Request = Private->Dhcp6Request;\r
1821 SrcPort = PXEBC_BS_DISCOVER_PORT;\r
1822 DestPort = PXEBC_BS_DISCOVER_PORT;\r
a3bcde70
HT
1823\r
1824 if (!UseBis && Layer != NULL) {\r
1825 *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;\r
1826 }\r
1827\r
1828 if (Request == NULL) {\r
1829 return EFI_DEVICE_ERROR;\r
1830 }\r
1831\r
1832 Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));\r
1833 if (Discover == NULL) {\r
1834 return EFI_OUT_OF_RESOURCES;\r
1835 }\r
1836\r
1837 //\r
1838 // Build the discover packet by the cached request packet before.\r
1839 //\r
1840 Xid = NET_RANDOM (NetRandomInitSeed ());\r
1841 Discover->TransactionId = HTONL (Xid);\r
1842 Discover->MessageType = Request->Dhcp6.Header.MessageType;\r
1843 RequestOpt = Request->Dhcp6.Option;\r
1844 DiscoverOpt = Discover->DhcpOptions;\r
1845 DiscoverLen = sizeof (EFI_DHCP6_HEADER);\r
1846 RequestLen = DiscoverLen;\r
1847\r
1848 while (RequestLen < Request->Length) {\r
1849 OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);\r
1850 OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);\r
1851 if (OpCode != EFI_DHCP6_IA_TYPE_NA &&\r
1852 OpCode != EFI_DHCP6_IA_TYPE_TA) {\r
1853 //\r
1854 // Copy all the options except IA option.\r
1855 //\r
1856 CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);\r
1857 DiscoverOpt += (OpLen + 4);\r
1858 DiscoverLen += (OpLen + 4);\r
1859 }\r
1860 RequestOpt += (OpLen + 4);\r
1861 RequestLen += (OpLen + 4);\r
1862 }\r
1863\r
1864 Status = PxeBc->UdpWrite (\r
1865 PxeBc,\r
903d1fa9 1866 0,\r
a3bcde70
HT
1867 &Private->ServerIp,\r
1868 &DestPort,\r
1869 NULL,\r
1870 &Private->StationIp,\r
1871 &SrcPort,\r
1872 NULL,\r
1873 NULL,\r
1874 &DiscoverLen,\r
1875 (VOID *) Discover\r
1876 );\r
1877 if (EFI_ERROR (Status)) {\r
1878 return Status;\r
1879 }\r
1880\r
1881 //\r
1882 // Cache the right PXE reply packet here, set valid flag later.\r
1883 // Especially for PXE discover packet, store it into mode data here.\r
1884 //\r
1885 if (Private->IsDoDiscover) {\r
1886 CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);\r
1887 Reply = &Private->PxeReply.Dhcp6.Packet.Ack;\r
1888 } else {\r
1889 Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;\r
1890 }\r
1891 ReadSize = (UINTN) Reply->Size;\r
1892\r
357af285 1893 //\r
1894 // Start Udp6Read instance\r
1895 //\r
1896 Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);\r
1897 if (EFI_ERROR (Status)) {\r
1898 return Status;\r
1899 }\r
1900 \r
a3bcde70
HT
1901 Status = PxeBc->UdpRead (\r
1902 PxeBc,\r
903d1fa9
FS
1903 EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,\r
1904 NULL,\r
a3bcde70
HT
1905 &SrcPort,\r
1906 &Private->ServerIp,\r
1907 &DestPort,\r
1908 NULL,\r
1909 NULL,\r
1910 &ReadSize,\r
1911 (VOID *) &Reply->Dhcp6\r
1912 );\r
357af285 1913 //\r
1914 // Stop Udp6Read instance\r
1915 //\r
1916 Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
a3bcde70
HT
1917 if (EFI_ERROR (Status)) {\r
1918 return Status;\r
1919 }\r
1920\r
1921 return EFI_SUCCESS;\r
1922}\r
1923\r
1924\r
1925/**\r
1926 Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.\r
1927\r
1928 @param[in] Private The pointer to PxeBc private data.\r
1929 @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL\r
1930\r
1931 @retval EFI_SUCCESS The S.A.R.R. process successfully finished.\r
1932 @retval Others Failed to finish the S.A.R.R. process.\r
1933\r
1934**/\r
1935EFI_STATUS\r
1936PxeBcDhcp6Sarr (\r
1937 IN PXEBC_PRIVATE_DATA *Private,\r
1938 IN EFI_DHCP6_PROTOCOL *Dhcp6\r
1939 )\r
1940{\r
1941 EFI_PXE_BASE_CODE_MODE *PxeMode;\r
1942 EFI_DHCP6_CONFIG_DATA Config;\r
1943 EFI_DHCP6_MODE_DATA Mode;\r
1944 EFI_DHCP6_RETRANSMISSION *Retransmit;\r
1945 EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];\r
1946 UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];\r
1947 UINT32 OptCount;\r
1948 EFI_STATUS Status;\r
ed2bfecb 1949 EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
1950 EFI_STATUS TimerStatus;\r
1951 EFI_EVENT Timer;\r
1952 UINT64 GetMappingTimeOut;\r
1953 UINTN DataSize;\r
1954 EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;\r
a3bcde70
HT
1955\r
1956 Status = EFI_SUCCESS;\r
1957 PxeMode = Private->PxeBc.Mode;\r
ed2bfecb 1958 Ip6Cfg = Private->Ip6Cfg;\r
1959 Timer = NULL;\r
a3bcde70
HT
1960\r
1961 //\r
1962 // Build option list for the request packet.\r
1963 //\r
1964 OptCount = PxeBcBuildDhcp6Options (Private, OptList, Buffer);\r
1965 ASSERT (OptCount> 0);\r
1966\r
1967 Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));\r
1968 if (Retransmit == NULL) {\r
1969 return EFI_OUT_OF_RESOURCES;\r
1970 }\r
1971\r
1972 ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));\r
1973 ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));\r
1974\r
1975 Config.OptionCount = OptCount;\r
1976 Config.OptionList = OptList;\r
1977 Config.Dhcp6Callback = PxeBcDhcp6CallBack;\r
1978 Config.CallbackContext = Private;\r
1979 Config.IaInfoEvent = NULL;\r
1980 Config.RapidCommit = FALSE;\r
1981 Config.ReconfigureAccept = FALSE;\r
75dce340 1982 Config.IaDescriptor.IaId = Private->IaId;\r
a3bcde70
HT
1983 Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;\r
1984 Config.SolicitRetransmission = Retransmit;\r
1985 Retransmit->Irt = 4;\r
1986 Retransmit->Mrc = 4;\r
1987 Retransmit->Mrt = 32;\r
1988 Retransmit->Mrd = 60;\r
1989\r
1990 //\r
1991 // Configure the DHCPv6 instance for PXE boot.\r
1992 //\r
1993 Status = Dhcp6->Configure (Dhcp6, &Config);\r
ed2bfecb 1994 FreePool (Retransmit);\r
a3bcde70 1995 if (EFI_ERROR (Status)) {\r
a3bcde70
HT
1996 return Status;\r
1997 }\r
1998\r
1999 //\r
2000 // Initialize the record fields for DHCPv6 offer in private data.\r
2001 //\r
2002 Private->IsProxyRecved = FALSE;\r
2003 Private->OfferNum = 0;\r
2004 Private->SelectIndex = 0;\r
2005 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));\r
2006 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));\r
2007\r
2008\r
2009 //\r
2010 // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
2011 //\r
2012 Status = Dhcp6->Start (Dhcp6);\r
ed2bfecb 2013 if (Status == EFI_NO_MAPPING) {\r
2014 //\r
2015 // IP6 Linklocal address is not available for use, so stop current Dhcp process\r
2016 // and wait for duplicate address detection to finish.\r
2017 //\r
2018 Dhcp6->Stop (Dhcp6);\r
2019\r
2020 //\r
2021 // Get Duplicate Address Detection Transmits count.\r
2022 //\r
2023 DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
2024 Status = Ip6Cfg->GetData (\r
2025 Ip6Cfg,\r
2026 Ip6ConfigDataTypeDupAddrDetectTransmits,\r
2027 &DataSize,\r
2028 &DadXmits\r
2029 );\r
2030 if (EFI_ERROR (Status)) {\r
2031 Dhcp6->Configure (Dhcp6, NULL);\r
2032 return Status;\r
2033 }\r
2034\r
2035 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);\r
2036 if (EFI_ERROR (Status)) {\r
2037 Dhcp6->Configure (Dhcp6, NULL);\r
2038 return Status;\r
2039 }\r
2040\r
2041 GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;\r
2042 Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);\r
2043 if (EFI_ERROR (Status)) {\r
2044 gBS->CloseEvent (Timer);\r
2045 Dhcp6->Configure (Dhcp6, NULL);\r
2046 return Status;\r
2047 }\r
2048\r
2049 do {\r
2050 \r
2051 TimerStatus = gBS->CheckEvent (Timer);\r
2052 if (!EFI_ERROR (TimerStatus)) {\r
2053 Status = Dhcp6->Start (Dhcp6);\r
2054 }\r
2055 } while (TimerStatus == EFI_NOT_READY);\r
2056 \r
2057 gBS->CloseEvent (Timer);\r
2058 }\r
a3bcde70
HT
2059 if (EFI_ERROR (Status)) {\r
2060 if (Status == EFI_ICMP_ERROR) {\r
2061 PxeMode->IcmpErrorReceived = TRUE;\r
2062 }\r
2063 Dhcp6->Configure (Dhcp6, NULL);\r
2064 return Status;\r
2065 }\r
2066\r
2067 //\r
2068 // Get the acquired IPv6 address and store them.\r
2069 //\r
2070 Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);\r
2071 if (EFI_ERROR (Status)) {\r
2072 Dhcp6->Stop (Dhcp6);\r
2073 return Status;\r
2074 }\r
2075\r
2076 ASSERT (Mode.Ia->State == Dhcp6Bound);\r
ae97201c
FS
2077 //\r
2078 // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the\r
2079 // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when\r
2080 // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as\r
2081 // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery \r
2082 // to find a valid router address.\r
2083 //\r
2084 CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));\r
a3bcde70
HT
2085\r
2086 //\r
2087 // Check the selected offer whether BINL retry is needed.\r
2088 //\r
2089 Status = PxeBcHandleDhcp6Offer (Private);\r
2090 if (EFI_ERROR (Status)) {\r
a3bcde70
HT
2091 Dhcp6->Stop (Dhcp6);\r
2092 return Status;\r
2093 }\r
ae97201c 2094 \r
a3bcde70
HT
2095 return EFI_SUCCESS;\r
2096}\r