]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/IScsiDxe/IScsiDhcp6.c
Update for NetworkPkg.
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiDhcp6.c
CommitLineData
4c5a5e0c 1/** @file\r
2 iSCSI DHCP6 related configuration routines.\r
3\r
4Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
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
206 AsciiStrCpy (ConfigNvData->TargetName, Field->Str);\r
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
253 \r
254 OptionCount = 0;\r
255 BootFileOpt = NULL;\r
256 \r
257 Status = This->Parse (This, Packet, &OptionCount, NULL);\r
258 if (Status != EFI_BUFFER_TOO_SMALL) {\r
259 return EFI_NOT_READY;\r
260 }\r
261\r
262 OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));\r
263 if (OptionList == NULL) {\r
264 return EFI_NOT_READY;\r
265 }\r
266\r
267 Status = This->Parse (This, Packet, &OptionCount, OptionList);\r
268 if (EFI_ERROR (Status)) {\r
269 Status = EFI_NOT_READY;\r
270 goto Exit;\r
271 }\r
272\r
273 ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context;\r
274\r
275 for (Index = 0; Index < OptionCount; Index++) {\r
276 OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);\r
277 OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen);\r
278\r
279 //\r
280 // Get DNS server addresses from this reply packet.\r
281 //\r
282 if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) {\r
283\r
284 if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {\r
285 Status = EFI_INVALID_PARAMETER;\r
286 goto Exit;\r
287 }\r
288 //\r
289 // Primary DNS server address.\r
290 //\r
291 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS));\r
292\r
293 if (OptionList[Index]->OpLen > 16) {\r
294 //\r
295 // Secondary DNS server address\r
296 //\r
297 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS));\r
298 }\r
299\r
300 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) {\r
301 //\r
302 // The server sends this option to inform the client about an URL to a boot file.\r
303 //\r
304 BootFileOpt = OptionList[Index];\r
305 }\r
306 }\r
307\r
308 if (BootFileOpt == NULL) {\r
309 Status = EFI_UNSUPPORTED;\r
310 goto Exit;\r
311 }\r
312 \r
313 //\r
314 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option\r
315 //\r
316 Status = IScsiDhcp6ExtractRootPath (\r
317 (CHAR8 *) BootFileOpt->Data,\r
318 BootFileOpt->OpLen,\r
319 ConfigData\r
320 );\r
321\r
322Exit:\r
323\r
324 FreePool (OptionList);\r
325 return Status;\r
326}\r
327\r
328\r
329/**\r
330 Parse the DHCP ACK to get the address configuration and DNS information.\r
331\r
332 @param[in] Image The handle of the driver image.\r
333 @param[in] Controller The handle of the controller;\r
334 @param[in, out] ConfigData The attempt configuration data.\r
335\r
336 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.\r
337 @retval EFI_NO_MAPPING DHCP failed to acquire address and other\r
338 information.\r
339 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.\r
340 @retval EFI_DEVICE_ERROR Some unexpected error occurred.\r
341 @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the\r
342 operation.\r
343 @retval EFI_NO_MEDIA There was a media error.\r
344\r
345**/\r
346EFI_STATUS\r
347IScsiDoDhcp6 (\r
348 IN EFI_HANDLE Image,\r
349 IN EFI_HANDLE Controller,\r
350 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
351 )\r
352{\r
353 EFI_HANDLE Dhcp6Handle;\r
354 EFI_DHCP6_PROTOCOL *Dhcp6;\r
355 EFI_STATUS Status;\r
356 EFI_STATUS TimerStatus;\r
357 EFI_DHCP6_PACKET_OPTION *Oro;\r
358 EFI_DHCP6_RETRANSMISSION InfoReqReXmit;\r
359 EFI_EVENT Timer;\r
360 BOOLEAN MediaPresent;\r
361\r
362 //\r
363 // Check media status before doing DHCP.\r
364 //\r
365 MediaPresent = TRUE;\r
366 NetLibDetectMedia (Controller, &MediaPresent);\r
367 if (!MediaPresent) {\r
368 return EFI_NO_MEDIA;\r
369 }\r
370\r
371 //\r
372 // iSCSI will only request target info from DHCPv6 server.\r
373 //\r
374 if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) {\r
375 return EFI_SUCCESS;\r
376 }\r
377\r
378 Dhcp6Handle = NULL;\r
379 Dhcp6 = NULL;\r
380 Oro = NULL;\r
381 Timer = NULL;\r
382\r
383 //\r
384 // Create a DHCP6 child instance and get the protocol.\r
385 //\r
386 Status = NetLibCreateServiceChild (\r
387 Controller,\r
388 Image,\r
389 &gEfiDhcp6ServiceBindingProtocolGuid,\r
390 &Dhcp6Handle\r
391 );\r
392 if (EFI_ERROR (Status)) {\r
393 return Status;\r
394 }\r
395\r
396 Status = gBS->OpenProtocol (\r
397 Dhcp6Handle,\r
398 &gEfiDhcp6ProtocolGuid,\r
399 (VOID **) &Dhcp6,\r
400 Image,\r
401 Controller,\r
402 EFI_OPEN_PROTOCOL_BY_DRIVER\r
403 );\r
404 if (EFI_ERROR (Status)) {\r
405 goto ON_EXIT;\r
406 }\r
407\r
408 Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 3);\r
409 if (Oro == NULL) {\r
410 Status = EFI_OUT_OF_RESOURCES;\r
411 goto ON_EXIT;\r
412 }\r
413\r
414 //\r
415 // Ask the server to reply with DNS and Boot File URL options by info request.\r
416 // All members in EFI_DHCP6_PACKET_OPTION are in network order.\r
417 //\r
418 Oro->OpCode = HTONS (DHCP6_OPT_REQUEST_OPTION);\r
419 Oro->OpLen = HTONS (2 * 2);\r
420 Oro->Data[1] = DHCP6_OPT_DNS_SERVERS;\r
421 Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL;\r
422\r
423 InfoReqReXmit.Irt = 4;\r
424 InfoReqReXmit.Mrc = 1;\r
425 InfoReqReXmit.Mrt = 10;\r
426 InfoReqReXmit.Mrd = 30;\r
427\r
428 Status = Dhcp6->InfoRequest (\r
429 Dhcp6,\r
430 TRUE,\r
431 Oro,\r
432 0,\r
433 NULL,\r
434 &InfoReqReXmit,\r
435 NULL,\r
436 IScsiDhcp6ParseReply,\r
437 ConfigData\r
438 );\r
439 if (Status == EFI_NO_MAPPING) {\r
440 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);\r
441 if (EFI_ERROR (Status)) {\r
442 goto ON_EXIT;\r
443 }\r
444\r
445 Status = gBS->SetTimer (\r
446 Timer,\r
447 TimerRelative,\r
448 ISCSI_GET_MAPPING_TIMEOUT\r
449 );\r
450\r
451 if (EFI_ERROR (Status)) {\r
452 goto ON_EXIT;\r
453 }\r
454\r
455 do {\r
456\r
457 TimerStatus = gBS->CheckEvent (Timer);\r
458\r
459 if (!EFI_ERROR (TimerStatus)) {\r
460 Status = Dhcp6->InfoRequest (\r
461 Dhcp6,\r
462 TRUE,\r
463 Oro,\r
464 0,\r
465 NULL,\r
466 &InfoReqReXmit,\r
467 NULL,\r
468 IScsiDhcp6ParseReply,\r
469 ConfigData\r
470 );\r
471 }\r
472\r
473 } while (TimerStatus == EFI_NOT_READY);\r
474\r
475 }\r
476\r
477ON_EXIT:\r
478\r
479 if (Oro != NULL) {\r
480 FreePool (Oro);\r
481 } \r
482\r
483 if (Timer != NULL) {\r
484 gBS->CloseEvent (Timer);\r
485 }\r
486\r
487 if (Dhcp6 != NULL) {\r
488 gBS->CloseProtocol (\r
489 Dhcp6Handle,\r
490 &gEfiDhcp6ProtocolGuid,\r
491 Image,\r
492 Controller\r
493 );\r
494 }\r
495\r
496 NetLibDestroyServiceChild (\r
497 Controller,\r
498 Image,\r
499 &gEfiDhcp6ServiceBindingProtocolGuid,\r
500 Dhcp6Handle\r
501 );\r
502\r
503 return Status;\r
504}\r
505\r