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