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