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