]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/IScsiDxe/IScsiDhcp6.c
ArmPkg: only attempt buildin MmCommunicationDxe for AArch64
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiDhcp6.c
CommitLineData
4c5a5e0c 1/** @file\r
2 iSCSI DHCP6 related configuration routines.\r
3\r
f75a7f56 4Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>\r
ecf98fbc 5SPDX-License-Identifier: BSD-2-Clause-Patent\r
4c5a5e0c 6\r
7**/\r
8\r
9#include "IScsiImpl.h"\r
10\r
11\r
12/**\r
13 Extract the Root Path option and get the required target information from\r
14 Boot File Uniform Resource Locator (URL) Option.\r
15\r
16 @param[in] RootPath The RootPath string.\r
17 @param[in] Length Length of the RootPath option payload.\r
18 @param[in, out] ConfigData The iSCSI session configuration data read from\r
19 nonvolatile device.\r
20\r
21 @retval EFI_SUCCESS All required information is extracted from the\r
22 RootPath option.\r
23 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.\r
24 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
25 @retval EFI_INVALID_PARAMETER The RootPath is malformatted.\r
26\r
27**/\r
28EFI_STATUS\r
29IScsiDhcp6ExtractRootPath (\r
30 IN CHAR8 *RootPath,\r
31 IN UINT16 Length,\r
32 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
33 )\r
34{\r
35 EFI_STATUS Status;\r
36 UINT16 IScsiRootPathIdLen;\r
37 CHAR8 *TmpStr;\r
38 ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];\r
39 ISCSI_ROOT_PATH_FIELD *Field;\r
40 UINT32 FieldIndex;\r
41 UINT8 Index;\r
42 ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;\r
43 EFI_IP_ADDRESS Ip;\r
f75a7f56 44 UINT8 IpMode;\r
4c5a5e0c 45\r
46 ConfigNvData = &ConfigData->SessionConfigData;\r
91cdd20f 47 ConfigNvData->DnsMode = FALSE;\r
4c5a5e0c 48 //\r
49 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>\r
50 //\r
51 IScsiRootPathIdLen = (UINT16) AsciiStrLen (ISCSI_ROOT_PATH_ID);\r
52\r
53 if ((Length <= IScsiRootPathIdLen) ||\r
54 (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {\r
55 return EFI_NOT_FOUND;\r
56 }\r
57 //\r
58 // Skip the iSCSI RootPath ID "iscsi:".\r
59 //\r
60 RootPath = RootPath + IScsiRootPathIdLen;\r
61 Length = (UINT16) (Length - IScsiRootPathIdLen);\r
62\r
63 TmpStr = (CHAR8 *) AllocatePool (Length + 1);\r
64 if (TmpStr == NULL) {\r
65 return EFI_OUT_OF_RESOURCES;\r
66 }\r
67\r
68 CopyMem (TmpStr, RootPath, Length);\r
69 TmpStr[Length] = '\0';\r
70\r
71 Index = 0;\r
72 FieldIndex = 0;\r
73 ZeroMem (&Fields[0], sizeof (Fields));\r
74\r
75 //\r
76 // Extract SERVERNAME field in the Root Path option.\r
77 //\r
78 if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) {\r
91cdd20f
ZL
79 //\r
80 // The servername is expressed as domain name.\r
81 //\r
82 ConfigNvData->DnsMode = TRUE;\r
4c5a5e0c 83 } else {\r
84 Index++;\r
85 }\r
86\r
87 Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index];\r
88\r
91cdd20f
ZL
89 if (!ConfigNvData->DnsMode) {\r
90 while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER)&& (Index < Length)) {\r
91 Index++;\r
92 }\r
4c5a5e0c 93\r
91cdd20f
ZL
94 //\r
95 // Skip ']' and ':'.\r
96 //\r
97 TmpStr[Index] = '\0';\r
98 Index += 2;\r
99 } else {\r
100 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {\r
101 Index++;\r
102 }\r
103 //\r
104 // Skip ':'.\r
105 //\r
106 TmpStr[Index] = '\0';\r
107 Index += 1;\r
108 }\r
4c5a5e0c 109\r
110 Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8) AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str);\r
111\r
112 //\r
113 // Extract others fields in the Root Path option string.\r
114 //\r
115 for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {\r
116\r
117 if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {\r
118 Fields[FieldIndex].Str = &TmpStr[Index];\r
119 }\r
120\r
121 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {\r
122 Index++;\r
123 }\r
124\r
125 if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {\r
126 if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {\r
127 TmpStr[Index] = '\0';\r
128 Index++;\r
129 }\r
130\r
131 if (Fields[FieldIndex].Str != NULL) {\r
132 Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);\r
133 }\r
134 }\r
135 }\r
136\r
137 if (FieldIndex != RP_FIELD_IDX_MAX) {\r
138 Status = EFI_INVALID_PARAMETER;\r
139 goto ON_EXIT;\r
140 }\r
141\r
142 if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||\r
143 (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||\r
144 (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)\r
145 ) {\r
146\r
147 Status = EFI_INVALID_PARAMETER;\r
148 goto ON_EXIT;\r
149 }\r
150 //\r
151 // Get the IP address of the target.\r
152 //\r
f75a7f56 153 Field = &Fields[RP_FIELD_IDX_SERVERNAME];\r
4c5a5e0c 154 if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {\r
155 IpMode = ConfigNvData->IpMode;\r
156 } else {\r
157 IpMode = ConfigData->AutoConfigureMode;\r
158 }\r
159\r
eabc6e59
ZL
160 //\r
161 // Server name is expressed as domain name, just save it.\r
162 //\r
91cdd20f 163 if (ConfigNvData->DnsMode) {\r
0f1946b6 164 if ((Field->Len + 2) > sizeof (ConfigNvData->TargetUrl)) {\r
eabc6e59
ZL
165 return EFI_INVALID_PARAMETER;\r
166 }\r
167 CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);\r
168 ConfigNvData->TargetUrl[Field->Len + 1] = '\0';\r
169 } else {\r
170 ZeroMem(&ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));\r
171 Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);\r
172 CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));\r
4c5a5e0c 173\r
eabc6e59
ZL
174 if (EFI_ERROR (Status)) {\r
175 goto ON_EXIT;\r
176 }\r
4c5a5e0c 177 }\r
eabc6e59 178\r
4c5a5e0c 179 //\r
180 // Check the protocol type.\r
181 //\r
182 Field = &Fields[RP_FIELD_IDX_PROTOCOL];\r
183 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {\r
184 Status = EFI_INVALID_PARAMETER;\r
185 goto ON_EXIT;\r
186 }\r
187 //\r
188 // Get the port of the iSCSI target.\r
189 //\r
190 Field = &Fields[RP_FIELD_IDX_PORT];\r
191 if (Field->Str != NULL) {\r
192 ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);\r
193 } else {\r
194 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;\r
195 }\r
196 //\r
197 // Get the LUN.\r
198 //\r
199 Field = &Fields[RP_FIELD_IDX_LUN];\r
200 if (Field->Str != NULL) {\r
201 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);\r
202 if (EFI_ERROR (Status)) {\r
203 goto ON_EXIT;\r
204 }\r
205 } else {\r
206 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));\r
207 }\r
208 //\r
209 // Get the target iSCSI Name.\r
210 //\r
211 Field = &Fields[RP_FIELD_IDX_TARGETNAME];\r
212\r
213 if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {\r
214 Status = EFI_INVALID_PARAMETER;\r
215 goto ON_EXIT;\r
216 }\r
217 //\r
218 // Validate the iSCSI name.\r
219 //\r
220 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));\r
221 if (EFI_ERROR (Status)) {\r
222 goto ON_EXIT;\r
223 }\r
224\r
c960bdc2 225 AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);\r
4c5a5e0c 226\r
227ON_EXIT:\r
228\r
229 FreePool (TmpStr);\r
230\r
231 return Status;\r
232}\r
233\r
234/**\r
f75a7f56 235 EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol\r
4c5a5e0c 236 instance to intercept events that occurs in the DHCPv6 Information Request\r
237 exchange process.\r
238\r
f75a7f56 239 @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that\r
4c5a5e0c 240 is used to configure this callback function.\r
241 @param[in] Context Pointer to the context that is initialized in\r
242 the EFI_DHCP6_PROTOCOL.InfoRequest().\r
243 @param[in] Packet Pointer to Reply packet that has been received.\r
244 The EFI DHCPv6 Protocol instance is responsible\r
245 for freeing the buffer.\r
246\r
247 @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish\r
248 Information Request exchange process.\r
249 @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue\r
250 Information Request exchange process.\r
251 @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort\r
252 the Information Request exchange process.\r
253 @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish\r
254 the Information Request exchange process because some\r
255 request information are not received.\r
256\r
257**/\r
258EFI_STATUS\r
259EFIAPI\r
260IScsiDhcp6ParseReply (\r
261 IN EFI_DHCP6_PROTOCOL *This,\r
262 IN VOID *Context,\r
263 IN EFI_DHCP6_PACKET *Packet\r
264 )\r
265{\r
266 EFI_STATUS Status;\r
267 UINT32 Index;\r
268 UINT32 OptionCount;\r
269 EFI_DHCP6_PACKET_OPTION *BootFileOpt;\r
270 EFI_DHCP6_PACKET_OPTION **OptionList;\r
271 ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData;\r
216f7970 272 UINT16 ParaLen;\r
f75a7f56 273\r
4c5a5e0c 274 OptionCount = 0;\r
275 BootFileOpt = NULL;\r
f75a7f56 276\r
4c5a5e0c 277 Status = This->Parse (This, Packet, &OptionCount, NULL);\r
278 if (Status != EFI_BUFFER_TOO_SMALL) {\r
279 return EFI_NOT_READY;\r
280 }\r
281\r
282 OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));\r
283 if (OptionList == NULL) {\r
284 return EFI_NOT_READY;\r
285 }\r
286\r
287 Status = This->Parse (This, Packet, &OptionCount, OptionList);\r
288 if (EFI_ERROR (Status)) {\r
289 Status = EFI_NOT_READY;\r
290 goto Exit;\r
291 }\r
292\r
293 ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context;\r
294\r
295 for (Index = 0; Index < OptionCount; Index++) {\r
296 OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);\r
297 OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen);\r
298\r
299 //\r
300 // Get DNS server addresses from this reply packet.\r
301 //\r
302 if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) {\r
303\r
304 if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {\r
216f7970 305 Status = EFI_UNSUPPORTED;\r
4c5a5e0c 306 goto Exit;\r
307 }\r
308 //\r
309 // Primary DNS server address.\r
310 //\r
311 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS));\r
312\r
313 if (OptionList[Index]->OpLen > 16) {\r
314 //\r
315 // Secondary DNS server address\r
316 //\r
317 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS));\r
318 }\r
319\r
320 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) {\r
321 //\r
322 // The server sends this option to inform the client about an URL to a boot file.\r
323 //\r
324 BootFileOpt = OptionList[Index];\r
142c00c3 325 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_PARAM) {\r
216f7970 326 //\r
327 // The server sends this option to inform the client about DHCP6 server address.\r
328 //\r
329 if (OptionList[Index]->OpLen < 18) {\r
330 Status = EFI_UNSUPPORTED;\r
331 goto Exit;\r
332 }\r
333 //\r
334 // Check param-len 1, should be 16 bytes.\r
335 //\r
336 CopyMem (&ParaLen, &OptionList[Index]->Data[0], sizeof (UINT16));\r
337 if (NTOHS (ParaLen) != 16) {\r
338 Status = EFI_UNSUPPORTED;\r
339 goto Exit;\r
340 }\r
341\r
342 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[2], sizeof (EFI_IPv6_ADDRESS));\r
4c5a5e0c 343 }\r
344 }\r
345\r
346 if (BootFileOpt == NULL) {\r
347 Status = EFI_UNSUPPORTED;\r
348 goto Exit;\r
349 }\r
f75a7f56 350\r
4c5a5e0c 351 //\r
352 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option\r
353 //\r
354 Status = IScsiDhcp6ExtractRootPath (\r
355 (CHAR8 *) BootFileOpt->Data,\r
356 BootFileOpt->OpLen,\r
357 ConfigData\r
358 );\r
359\r
360Exit:\r
361\r
362 FreePool (OptionList);\r
363 return Status;\r
364}\r
365\r
366\r
367/**\r
368 Parse the DHCP ACK to get the address configuration and DNS information.\r
369\r
370 @param[in] Image The handle of the driver image.\r
371 @param[in] Controller The handle of the controller;\r
372 @param[in, out] ConfigData The attempt configuration data.\r
373\r
374 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.\r
375 @retval EFI_NO_MAPPING DHCP failed to acquire address and other\r
376 information.\r
377 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.\r
378 @retval EFI_DEVICE_ERROR Some unexpected error occurred.\r
379 @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the\r
380 operation.\r
381 @retval EFI_NO_MEDIA There was a media error.\r
382\r
383**/\r
384EFI_STATUS\r
385IScsiDoDhcp6 (\r
386 IN EFI_HANDLE Image,\r
387 IN EFI_HANDLE Controller,\r
388 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
389 )\r
390{\r
391 EFI_HANDLE Dhcp6Handle;\r
392 EFI_DHCP6_PROTOCOL *Dhcp6;\r
393 EFI_STATUS Status;\r
394 EFI_STATUS TimerStatus;\r
395 EFI_DHCP6_PACKET_OPTION *Oro;\r
396 EFI_DHCP6_RETRANSMISSION InfoReqReXmit;\r
397 EFI_EVENT Timer;\r
152f2d5e 398 EFI_STATUS MediaStatus;\r
4c5a5e0c 399\r
400 //\r
401 // Check media status before doing DHCP.\r
402 //\r
152f2d5e 403 MediaStatus = EFI_SUCCESS;\r
404 NetLibDetectMediaWaitTimeout (Controller, ISCSI_CHECK_MEDIA_GET_DHCP_WAITING_TIME, &MediaStatus);\r
405 if (MediaStatus != EFI_SUCCESS) {\r
406 AsciiPrint ("\n Error: Could not detect network connection.\n");\r
4c5a5e0c 407 return EFI_NO_MEDIA;\r
408 }\r
409\r
410 //\r
411 // iSCSI will only request target info from DHCPv6 server.\r
412 //\r
413 if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) {\r
414 return EFI_SUCCESS;\r
415 }\r
416\r
417 Dhcp6Handle = NULL;\r
418 Dhcp6 = NULL;\r
419 Oro = NULL;\r
420 Timer = NULL;\r
421\r
422 //\r
423 // Create a DHCP6 child instance and get the protocol.\r
424 //\r
425 Status = NetLibCreateServiceChild (\r
426 Controller,\r
427 Image,\r
428 &gEfiDhcp6ServiceBindingProtocolGuid,\r
429 &Dhcp6Handle\r
430 );\r
431 if (EFI_ERROR (Status)) {\r
432 return Status;\r
433 }\r
434\r
435 Status = gBS->OpenProtocol (\r
436 Dhcp6Handle,\r
437 &gEfiDhcp6ProtocolGuid,\r
438 (VOID **) &Dhcp6,\r
439 Image,\r
440 Controller,\r
441 EFI_OPEN_PROTOCOL_BY_DRIVER\r
442 );\r
443 if (EFI_ERROR (Status)) {\r
444 goto ON_EXIT;\r
445 }\r
446\r
216f7970 447 Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 5);\r
4c5a5e0c 448 if (Oro == NULL) {\r
449 Status = EFI_OUT_OF_RESOURCES;\r
450 goto ON_EXIT;\r
451 }\r
452\r
453 //\r
454 // Ask the server to reply with DNS and Boot File URL options by info request.\r
455 // All members in EFI_DHCP6_PACKET_OPTION are in network order.\r
456 //\r
142c00c3 457 Oro->OpCode = HTONS (DHCP6_OPT_ORO);\r
216f7970 458 Oro->OpLen = HTONS (2 * 3);\r
4c5a5e0c 459 Oro->Data[1] = DHCP6_OPT_DNS_SERVERS;\r
460 Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL;\r
142c00c3 461 Oro->Data[5] = DHCP6_OPT_BOOT_FILE_PARAM;\r
4c5a5e0c 462\r
463 InfoReqReXmit.Irt = 4;\r
464 InfoReqReXmit.Mrc = 1;\r
465 InfoReqReXmit.Mrt = 10;\r
466 InfoReqReXmit.Mrd = 30;\r
467\r
468 Status = Dhcp6->InfoRequest (\r
469 Dhcp6,\r
470 TRUE,\r
471 Oro,\r
472 0,\r
473 NULL,\r
474 &InfoReqReXmit,\r
475 NULL,\r
476 IScsiDhcp6ParseReply,\r
477 ConfigData\r
478 );\r
479 if (Status == EFI_NO_MAPPING) {\r
480 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);\r
481 if (EFI_ERROR (Status)) {\r
482 goto ON_EXIT;\r
483 }\r
484\r
485 Status = gBS->SetTimer (\r
486 Timer,\r
487 TimerRelative,\r
488 ISCSI_GET_MAPPING_TIMEOUT\r
489 );\r
490\r
491 if (EFI_ERROR (Status)) {\r
492 goto ON_EXIT;\r
493 }\r
494\r
495 do {\r
496\r
497 TimerStatus = gBS->CheckEvent (Timer);\r
498\r
499 if (!EFI_ERROR (TimerStatus)) {\r
500 Status = Dhcp6->InfoRequest (\r
501 Dhcp6,\r
502 TRUE,\r
503 Oro,\r
504 0,\r
505 NULL,\r
506 &InfoReqReXmit,\r
507 NULL,\r
508 IScsiDhcp6ParseReply,\r
509 ConfigData\r
510 );\r
511 }\r
512\r
513 } while (TimerStatus == EFI_NOT_READY);\r
514\r
515 }\r
516\r
517ON_EXIT:\r
518\r
519 if (Oro != NULL) {\r
520 FreePool (Oro);\r
f75a7f56 521 }\r
4c5a5e0c 522\r
523 if (Timer != NULL) {\r
524 gBS->CloseEvent (Timer);\r
525 }\r
526\r
527 if (Dhcp6 != NULL) {\r
528 gBS->CloseProtocol (\r
529 Dhcp6Handle,\r
530 &gEfiDhcp6ProtocolGuid,\r
531 Image,\r
532 Controller\r
533 );\r
534 }\r
535\r
536 NetLibDestroyServiceChild (\r
537 Controller,\r
538 Image,\r
539 &gEfiDhcp6ServiceBindingProtocolGuid,\r
540 Dhcp6Handle\r
541 );\r
542\r
543 return Status;\r
544}\r
545\r