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