]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/IScsiDxe/IScsiDhcp.c
Fix a bug about the iSCSI DHCP dependency issue.
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiDhcp.c
CommitLineData
4c5a5e0c 1/** @file\r
2 iSCSI DHCP4 related configuration routines.\r
3\r
4Copyright (c) 2004 - 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.\r
20\r
21 @param[in] RootPath The RootPath.\r
22 @param[in] Length Length of the RootPath option payload.\r
23 @param[in, out] ConfigData The iSCSI attempt configuration data read\r
24 from a nonvolatile device.\r
25\r
26 @retval EFI_SUCCESS All required information is extracted from the RootPath option.\r
27 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.\r
28 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
29 @retval EFI_INVALID_PARAMETER The RootPath is malformatted.\r
30\r
31**/\r
32EFI_STATUS\r
33IScsiDhcpExtractRootPath (\r
34 IN CHAR8 *RootPath,\r
35 IN UINT8 Length,\r
36 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
37 )\r
38{\r
39 EFI_STATUS Status;\r
40 UINT8 IScsiRootPathIdLen;\r
41 CHAR8 *TmpStr;\r
42 ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];\r
43 ISCSI_ROOT_PATH_FIELD *Field;\r
44 UINT32 FieldIndex;\r
45 UINT8 Index;\r
46 ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;\r
47 EFI_IP_ADDRESS Ip;\r
48 UINT8 IpMode;\r
49\r
50 ConfigNvData = &ConfigData->SessionConfigData;\r
51\r
52 //\r
53 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>\r
54 //\r
55 IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID);\r
56\r
57 if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {\r
58 return EFI_NOT_FOUND;\r
59 }\r
60 //\r
61 // Skip the iSCSI RootPath ID "iscsi:".\r
62 //\r
63 RootPath += IScsiRootPathIdLen;\r
64 Length = (UINT8) (Length - IScsiRootPathIdLen);\r
65\r
66 TmpStr = (CHAR8 *) AllocatePool (Length + 1);\r
67 if (TmpStr == NULL) {\r
68 return EFI_OUT_OF_RESOURCES;\r
69 }\r
70\r
71 CopyMem (TmpStr, RootPath, Length);\r
72 TmpStr[Length] = '\0';\r
73\r
74 Index = 0;\r
75 FieldIndex = RP_FIELD_IDX_SERVERNAME;\r
76 ZeroMem (&Fields[0], sizeof (Fields));\r
77\r
78 //\r
79 // Extract the fields in the Root Path option string.\r
80 //\r
81 for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {\r
82 if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {\r
83 Fields[FieldIndex].Str = &TmpStr[Index];\r
84 }\r
85\r
86 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {\r
87 Index++;\r
88 }\r
89\r
90 if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {\r
91 if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {\r
92 TmpStr[Index] = '\0';\r
93 Index++;\r
94 }\r
95\r
96 if (Fields[FieldIndex].Str != NULL) {\r
97 Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);\r
98 }\r
99 }\r
100 }\r
101\r
102 if (FieldIndex != RP_FIELD_IDX_MAX) {\r
103 Status = EFI_INVALID_PARAMETER;\r
104 goto ON_EXIT;\r
105 }\r
106\r
107 if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||\r
108 (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||\r
109 (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)\r
110 ) {\r
111\r
112 Status = EFI_INVALID_PARAMETER;\r
113 goto ON_EXIT;\r
114 }\r
115 //\r
116 // Get the IP address of the target.\r
117 //\r
118 Field = &Fields[RP_FIELD_IDX_SERVERNAME];\r
119\r
120 if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {\r
121 IpMode = ConfigNvData->IpMode;\r
122 } else {\r
123 IpMode = ConfigData->AutoConfigureMode;\r
124 }\r
125\r
126 Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);\r
127 CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));\r
128\r
129 if (EFI_ERROR (Status)) {\r
130 goto ON_EXIT;\r
131 }\r
132 //\r
133 // Check the protocol type.\r
134 //\r
135 Field = &Fields[RP_FIELD_IDX_PROTOCOL];\r
136 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {\r
137 Status = EFI_INVALID_PARAMETER;\r
138 goto ON_EXIT;\r
139 }\r
140 //\r
141 // Get the port of the iSCSI target.\r
142 //\r
143 Field = &Fields[RP_FIELD_IDX_PORT];\r
144 if (Field->Str != NULL) {\r
145 ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);\r
146 } else {\r
147 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;\r
148 }\r
149 //\r
150 // Get the LUN.\r
151 //\r
152 Field = &Fields[RP_FIELD_IDX_LUN];\r
153 if (Field->Str != NULL) {\r
154 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);\r
155 if (EFI_ERROR (Status)) {\r
156 goto ON_EXIT;\r
157 }\r
158 } else {\r
159 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));\r
160 }\r
161 //\r
162 // Get the target iSCSI Name.\r
163 //\r
164 Field = &Fields[RP_FIELD_IDX_TARGETNAME];\r
165\r
166 if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {\r
167 Status = EFI_INVALID_PARAMETER;\r
168 goto ON_EXIT;\r
169 }\r
170 //\r
171 // Validate the iSCSI name.\r
172 //\r
173 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));\r
174 if (EFI_ERROR (Status)) {\r
175 goto ON_EXIT;\r
176 }\r
177\r
178 AsciiStrCpy (ConfigNvData->TargetName, Field->Str);\r
179\r
180ON_EXIT:\r
181\r
182 FreePool (TmpStr);\r
183\r
184 return Status;\r
185}\r
186\r
187/**\r
188 The callback function registerd to the DHCP4 instance that is used to select\r
189 the qualified DHCP OFFER.\r
190 \r
191 @param[in] This The DHCP4 protocol.\r
192 @param[in] Context The context set when configuring the DHCP4 protocol.\r
193 @param[in] CurrentState The current state of the DHCP4 protocol.\r
194 @param[in] Dhcp4Event The event occurs in the current state.\r
195 @param[in] Packet The DHCP packet that is to be sent or was already received. \r
196 @param[out] NewPacket The packet used to replace the above Packet.\r
197 \r
198 @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted\r
199 in the Dhcp4Event.\r
200 @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.\r
201 @retval Others Other errors as indicated.\r
202\r
203**/\r
204EFI_STATUS\r
205EFIAPI\r
206IScsiDhcpSelectOffer (\r
207 IN EFI_DHCP4_PROTOCOL *This,\r
208 IN VOID *Context,\r
209 IN EFI_DHCP4_STATE CurrentState,\r
210 IN EFI_DHCP4_EVENT Dhcp4Event,\r
211 IN EFI_DHCP4_PACKET *Packet, OPTIONAL\r
212 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL\r
213 )\r
214{\r
215 EFI_STATUS Status;\r
216 UINT32 OptionCount;\r
217 EFI_DHCP4_PACKET_OPTION **OptionList;\r
218 UINT32 Index;\r
219\r
220 if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {\r
221 return EFI_SUCCESS;\r
222 }\r
223\r
224 OptionCount = 0;\r
225\r
226 Status = This->Parse (This, Packet, &OptionCount, NULL);\r
227 if (Status != EFI_BUFFER_TOO_SMALL) {\r
228 return EFI_NOT_READY;\r
229 }\r
230\r
231 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));\r
232 if (OptionList == NULL) {\r
233 return EFI_NOT_READY;\r
234 }\r
235\r
236 Status = This->Parse (This, Packet, &OptionCount, OptionList);\r
237 if (EFI_ERROR (Status)) {\r
238 FreePool (OptionList);\r
239 return EFI_NOT_READY;\r
240 }\r
241\r
242 for (Index = 0; Index < OptionCount; Index++) {\r
243 if (OptionList[Index]->OpCode != DHCP4_TAG_ROOT_PATH) {\r
244 continue;\r
245 }\r
246\r
247 Status = IScsiDhcpExtractRootPath (\r
248 (CHAR8 *) &OptionList[Index]->Data[0],\r
249 OptionList[Index]->Length,\r
250 (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context\r
251 );\r
252\r
253 break;\r
254 }\r
255\r
256 if ((Index == OptionCount)) {\r
257 Status = EFI_NOT_READY;\r
258 }\r
259\r
260 FreePool (OptionList);\r
261\r
262 return Status;\r
263}\r
264\r
265/**\r
266 Parse the DHCP ACK to get the address configuration and DNS information.\r
267\r
268 @param[in] Dhcp4 The DHCP4 protocol.\r
269 @param[in, out] ConfigData The session configuration data.\r
270\r
271 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.\r
272 @retval EFI_NO_MAPPING DHCP failed to acquire address and other information.\r
273 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.\r
274 @retval EFI_DEVICE_ERROR Other errors as indicated.\r
275 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
276\r
277**/\r
278EFI_STATUS\r
279IScsiParseDhcpAck (\r
280 IN EFI_DHCP4_PROTOCOL *Dhcp4,\r
281 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
282 )\r
283{\r
284 EFI_STATUS Status;\r
285 EFI_DHCP4_MODE_DATA Dhcp4ModeData;\r
286 UINT32 OptionCount;\r
287 EFI_DHCP4_PACKET_OPTION **OptionList;\r
288 UINT32 Index;\r
289 ISCSI_SESSION_CONFIG_NVDATA *NvData;\r
290\r
291 Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);\r
292 if (EFI_ERROR (Status)) {\r
293 return Status;\r
294 }\r
295\r
296 if (Dhcp4ModeData.State != Dhcp4Bound) {\r
297 return EFI_NO_MAPPING;\r
298 }\r
299\r
300 NvData = &ConfigData->SessionConfigData;\r
301\r
302 CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));\r
303 CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
304 CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
305\r
306 OptionCount = 0;\r
307 OptionList = NULL;\r
308\r
309 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);\r
310 if (Status != EFI_BUFFER_TOO_SMALL) {\r
311 return EFI_DEVICE_ERROR;\r
312 }\r
313\r
314 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));\r
315 if (OptionList == NULL) {\r
316 return EFI_OUT_OF_RESOURCES;\r
317 }\r
318\r
319 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);\r
320 if (EFI_ERROR (Status)) {\r
321 FreePool (OptionList);\r
322 return EFI_DEVICE_ERROR;\r
323 }\r
324\r
325 for (Index = 0; Index < OptionCount; Index++) {\r
326 //\r
327 // Get DNS server addresses and DHCP server address from this offer.\r
328 //\r
329 if (OptionList[Index]->OpCode == DHCP4_TAG_DNS) {\r
330\r
331 if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {\r
332 Status = EFI_INVALID_PARAMETER;\r
333 break;\r
334 }\r
335 //\r
336 // Primary DNS server address.\r
337 //\r
338 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));\r
339\r
340 if (OptionList[Index]->Length > 4) {\r
341 //\r
342 // Secondary DNS server address.\r
343 //\r
344 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));\r
345 }\r
346 } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {\r
347 if (OptionList[Index]->Length != 4) {\r
348 Status = EFI_INVALID_PARAMETER;\r
349 break;\r
350 }\r
351\r
352 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));\r
353 }\r
354 }\r
355\r
356 FreePool (OptionList);\r
357\r
358 return Status;\r
359}\r
360\r
361\r
362/**\r
363 Parse the DHCP ACK to get the address configuration and DNS information.\r
364\r
365 @param[in] Image The handle of the driver image.\r
366 @param[in] Controller The handle of the controller.\r
367 @param[in, out] ConfigData The attempt configuration data.\r
368\r
369 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.\r
370 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
371 @retval EFI_NO_MEDIA There was a media error.\r
372 @retval Others Other errors as indicated.\r
373\r
374**/\r
375EFI_STATUS\r
376IScsiDoDhcp (\r
377 IN EFI_HANDLE Image,\r
378 IN EFI_HANDLE Controller,\r
379 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
380 )\r
381{\r
382 EFI_HANDLE Dhcp4Handle;\r
383 EFI_DHCP4_PROTOCOL *Dhcp4;\r
384 EFI_STATUS Status;\r
385 EFI_DHCP4_PACKET_OPTION *ParaList;\r
386 EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;\r
387 ISCSI_SESSION_CONFIG_NVDATA *NvData;\r
388 BOOLEAN MediaPresent;\r
389\r
390 Dhcp4Handle = NULL;\r
391 Dhcp4 = NULL;\r
392 ParaList = NULL;\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 // Create a DHCP4 child instance and get the protocol.\r
405 //\r
406 Status = NetLibCreateServiceChild (\r
407 Controller,\r
408 Image,\r
409 &gEfiDhcp4ServiceBindingProtocolGuid,\r
410 &Dhcp4Handle\r
411 );\r
412 if (EFI_ERROR (Status)) {\r
413 return Status;\r
414 }\r
415\r
416 Status = gBS->OpenProtocol (\r
417 Dhcp4Handle,\r
418 &gEfiDhcp4ProtocolGuid,\r
419 (VOID **) &Dhcp4,\r
420 Image,\r
421 Controller,\r
422 EFI_OPEN_PROTOCOL_BY_DRIVER\r
423 );\r
424 if (EFI_ERROR (Status)) {\r
425 goto ON_EXIT;\r
426 }\r
427\r
428 NvData = &ConfigData->SessionConfigData;\r
429\r
430 ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);\r
431 if (ParaList == NULL) {\r
432 Status = EFI_OUT_OF_RESOURCES;\r
433 goto ON_EXIT;\r
434 }\r
435\r
436 //\r
437 // Ask the server to reply with Netmask, Router, DNS, and RootPath options.\r
438 //\r
439 ParaList->OpCode = DHCP4_TAG_PARA_LIST;\r
440 ParaList->Length = (UINT8) (NvData->TargetInfoFromDhcp ? 4 : 3);\r
441 ParaList->Data[0] = DHCP4_TAG_NETMASK;\r
442 ParaList->Data[1] = DHCP4_TAG_ROUTER;\r
443 ParaList->Data[2] = DHCP4_TAG_DNS;\r
444 ParaList->Data[3] = DHCP4_TAG_ROOT_PATH;\r
445\r
446 ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));\r
447 Dhcp4ConfigData.OptionCount = 1;\r
448 Dhcp4ConfigData.OptionList = &ParaList;\r
449\r
450 if (NvData->TargetInfoFromDhcp) {\r
451 //\r
452 // Use callback to select an offer that contains target information.\r
453 //\r
454 Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;\r
455 Dhcp4ConfigData.CallbackContext = ConfigData;\r
456 }\r
457\r
458 Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);\r
459 if (EFI_ERROR (Status)) {\r
460 goto ON_EXIT;\r
461 }\r
462\r
463 Status = Dhcp4->Start (Dhcp4, NULL);\r
464 if (EFI_ERROR (Status)) {\r
465 goto ON_EXIT;\r
466 }\r
467 //\r
468 // Parse the ACK to get required information.\r
469 //\r
470 Status = IScsiParseDhcpAck (Dhcp4, ConfigData);\r
471\r
472ON_EXIT:\r
473\r
474 if (ParaList != NULL) {\r
475 FreePool (ParaList);\r
476 }\r
477\r
478 if (Dhcp4 != NULL) {\r
479 Dhcp4->Stop (Dhcp4);\r
480 Dhcp4->Configure (Dhcp4, NULL);\r
481\r
482 gBS->CloseProtocol (\r
483 Dhcp4Handle,\r
484 &gEfiDhcp4ProtocolGuid,\r
485 Image,\r
486 Controller\r
487 );\r
488 }\r
489\r
490 NetLibDestroyServiceChild (\r
491 Controller,\r
492 Image,\r
493 &gEfiDhcp4ServiceBindingProtocolGuid,\r
494 Dhcp4Handle\r
495 );\r
496\r
497 return Status;\r
498}\r