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