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