]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/IScsiDxe/IScsiDhcp.c
MdeModulePkg/FormDisplay: Make the LineWidth of option consistent
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiDhcp.c
CommitLineData
4c5a5e0c 1/** @file\r
2 iSCSI DHCP4 related configuration routines.\r
3\r
eabc6e59 4Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>\r
4c5a5e0c 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
eabc6e59
ZL
126 //\r
127 // Server name is expressed as domain name, just save it.\r
128 //\r
129 if ((!NET_IS_DIGIT (*(Field->Str))) && (*(Field->Str) != '[')) {\r
130 ConfigNvData->DnsMode = TRUE;\r
131 if (Field->Len > sizeof (ConfigNvData->TargetUrl)) {\r
132 return EFI_INVALID_PARAMETER;\r
133 }\r
134 CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);\r
135 ConfigNvData->TargetUrl[Field->Len + 1] = '\0';\r
136 } else {\r
137 ZeroMem(ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));\r
138 Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);\r
139 CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));\r
4c5a5e0c 140\r
eabc6e59
ZL
141 if (EFI_ERROR (Status)) {\r
142 goto ON_EXIT;\r
143 }\r
4c5a5e0c 144 }\r
145 //\r
146 // Check the protocol type.\r
147 //\r
148 Field = &Fields[RP_FIELD_IDX_PROTOCOL];\r
149 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {\r
150 Status = EFI_INVALID_PARAMETER;\r
151 goto ON_EXIT;\r
152 }\r
153 //\r
154 // Get the port of the iSCSI target.\r
155 //\r
156 Field = &Fields[RP_FIELD_IDX_PORT];\r
157 if (Field->Str != NULL) {\r
158 ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);\r
159 } else {\r
160 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;\r
161 }\r
162 //\r
163 // Get the LUN.\r
164 //\r
165 Field = &Fields[RP_FIELD_IDX_LUN];\r
166 if (Field->Str != NULL) {\r
167 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);\r
168 if (EFI_ERROR (Status)) {\r
169 goto ON_EXIT;\r
170 }\r
171 } else {\r
172 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));\r
173 }\r
174 //\r
175 // Get the target iSCSI Name.\r
176 //\r
177 Field = &Fields[RP_FIELD_IDX_TARGETNAME];\r
178\r
179 if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {\r
180 Status = EFI_INVALID_PARAMETER;\r
181 goto ON_EXIT;\r
182 }\r
183 //\r
184 // Validate the iSCSI name.\r
185 //\r
186 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));\r
187 if (EFI_ERROR (Status)) {\r
188 goto ON_EXIT;\r
189 }\r
190\r
c960bdc2 191 AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str);\r
4c5a5e0c 192\r
193ON_EXIT:\r
194\r
195 FreePool (TmpStr);\r
196\r
197 return Status;\r
198}\r
199\r
200/**\r
201 The callback function registerd to the DHCP4 instance that is used to select\r
202 the qualified DHCP OFFER.\r
203 \r
204 @param[in] This The DHCP4 protocol.\r
205 @param[in] Context The context set when configuring the DHCP4 protocol.\r
206 @param[in] CurrentState The current state of the DHCP4 protocol.\r
207 @param[in] Dhcp4Event The event occurs in the current state.\r
208 @param[in] Packet The DHCP packet that is to be sent or was already received. \r
209 @param[out] NewPacket The packet used to replace the above Packet.\r
210 \r
211 @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted\r
212 in the Dhcp4Event.\r
213 @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.\r
214 @retval Others Other errors as indicated.\r
215\r
216**/\r
217EFI_STATUS\r
218EFIAPI\r
219IScsiDhcpSelectOffer (\r
220 IN EFI_DHCP4_PROTOCOL *This,\r
221 IN VOID *Context,\r
222 IN EFI_DHCP4_STATE CurrentState,\r
223 IN EFI_DHCP4_EVENT Dhcp4Event,\r
224 IN EFI_DHCP4_PACKET *Packet, OPTIONAL\r
225 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL\r
226 )\r
227{\r
228 EFI_STATUS Status;\r
229 UINT32 OptionCount;\r
230 EFI_DHCP4_PACKET_OPTION **OptionList;\r
231 UINT32 Index;\r
232\r
233 if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {\r
234 return EFI_SUCCESS;\r
235 }\r
236\r
237 OptionCount = 0;\r
238\r
239 Status = This->Parse (This, Packet, &OptionCount, NULL);\r
240 if (Status != EFI_BUFFER_TOO_SMALL) {\r
241 return EFI_NOT_READY;\r
242 }\r
243\r
244 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));\r
245 if (OptionList == NULL) {\r
246 return EFI_NOT_READY;\r
247 }\r
248\r
249 Status = This->Parse (This, Packet, &OptionCount, OptionList);\r
250 if (EFI_ERROR (Status)) {\r
251 FreePool (OptionList);\r
252 return EFI_NOT_READY;\r
253 }\r
254\r
255 for (Index = 0; Index < OptionCount; Index++) {\r
142c00c3 256 if (OptionList[Index]->OpCode != DHCP4_TAG_ROOTPATH) {\r
4c5a5e0c 257 continue;\r
258 }\r
259\r
260 Status = IScsiDhcpExtractRootPath (\r
261 (CHAR8 *) &OptionList[Index]->Data[0],\r
262 OptionList[Index]->Length,\r
263 (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context\r
264 );\r
265\r
266 break;\r
267 }\r
268\r
269 if ((Index == OptionCount)) {\r
270 Status = EFI_NOT_READY;\r
271 }\r
272\r
273 FreePool (OptionList);\r
274\r
275 return Status;\r
276}\r
277\r
278/**\r
279 Parse the DHCP ACK to get the address configuration and DNS information.\r
280\r
281 @param[in] Dhcp4 The DHCP4 protocol.\r
282 @param[in, out] ConfigData The session configuration data.\r
283\r
284 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.\r
285 @retval EFI_NO_MAPPING DHCP failed to acquire address and other information.\r
286 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.\r
287 @retval EFI_DEVICE_ERROR Other errors as indicated.\r
288 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
289\r
290**/\r
291EFI_STATUS\r
292IScsiParseDhcpAck (\r
293 IN EFI_DHCP4_PROTOCOL *Dhcp4,\r
294 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
295 )\r
296{\r
297 EFI_STATUS Status;\r
298 EFI_DHCP4_MODE_DATA Dhcp4ModeData;\r
299 UINT32 OptionCount;\r
300 EFI_DHCP4_PACKET_OPTION **OptionList;\r
301 UINT32 Index;\r
302 ISCSI_SESSION_CONFIG_NVDATA *NvData;\r
303\r
304 Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);\r
305 if (EFI_ERROR (Status)) {\r
306 return Status;\r
307 }\r
308\r
309 if (Dhcp4ModeData.State != Dhcp4Bound) {\r
310 return EFI_NO_MAPPING;\r
311 }\r
312\r
313 NvData = &ConfigData->SessionConfigData;\r
314\r
315 CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));\r
316 CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
317 CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
318\r
319 OptionCount = 0;\r
320 OptionList = NULL;\r
321\r
322 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);\r
323 if (Status != EFI_BUFFER_TOO_SMALL) {\r
324 return EFI_DEVICE_ERROR;\r
325 }\r
326\r
327 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));\r
328 if (OptionList == NULL) {\r
329 return EFI_OUT_OF_RESOURCES;\r
330 }\r
331\r
332 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);\r
333 if (EFI_ERROR (Status)) {\r
334 FreePool (OptionList);\r
335 return EFI_DEVICE_ERROR;\r
336 }\r
337\r
338 for (Index = 0; Index < OptionCount; Index++) {\r
339 //\r
340 // Get DNS server addresses and DHCP server address from this offer.\r
341 //\r
142c00c3 342 if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) {\r
4c5a5e0c 343\r
344 if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {\r
345 Status = EFI_INVALID_PARAMETER;\r
346 break;\r
347 }\r
348 //\r
349 // Primary DNS server address.\r
350 //\r
351 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));\r
352\r
353 if (OptionList[Index]->Length > 4) {\r
354 //\r
355 // Secondary DNS server address.\r
356 //\r
357 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));\r
358 }\r
359 } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {\r
360 if (OptionList[Index]->Length != 4) {\r
361 Status = EFI_INVALID_PARAMETER;\r
362 break;\r
363 }\r
364\r
365 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));\r
366 }\r
367 }\r
368\r
369 FreePool (OptionList);\r
370\r
371 return Status;\r
372}\r
373\r
374\r
375/**\r
376 Parse the DHCP ACK to get the address configuration and DNS information.\r
377\r
378 @param[in] Image The handle of the driver image.\r
379 @param[in] Controller The handle of the controller.\r
380 @param[in, out] ConfigData The attempt configuration data.\r
381\r
382 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.\r
383 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
384 @retval EFI_NO_MEDIA There was a media error.\r
385 @retval Others Other errors as indicated.\r
386\r
387**/\r
388EFI_STATUS\r
389IScsiDoDhcp (\r
390 IN EFI_HANDLE Image,\r
391 IN EFI_HANDLE Controller,\r
392 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
393 )\r
394{\r
395 EFI_HANDLE Dhcp4Handle;\r
396 EFI_DHCP4_PROTOCOL *Dhcp4;\r
397 EFI_STATUS Status;\r
398 EFI_DHCP4_PACKET_OPTION *ParaList;\r
399 EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;\r
400 ISCSI_SESSION_CONFIG_NVDATA *NvData;\r
401 BOOLEAN MediaPresent;\r
402\r
403 Dhcp4Handle = NULL;\r
404 Dhcp4 = NULL;\r
405 ParaList = NULL;\r
406\r
407 //\r
408 // Check media status before doing DHCP.\r
409 //\r
410 MediaPresent = TRUE;\r
411 NetLibDetectMedia (Controller, &MediaPresent);\r
412 if (!MediaPresent) {\r
413 return EFI_NO_MEDIA;\r
414 }\r
415\r
416 //\r
417 // Create a DHCP4 child instance and get the protocol.\r
418 //\r
419 Status = NetLibCreateServiceChild (\r
420 Controller,\r
421 Image,\r
422 &gEfiDhcp4ServiceBindingProtocolGuid,\r
423 &Dhcp4Handle\r
424 );\r
425 if (EFI_ERROR (Status)) {\r
426 return Status;\r
427 }\r
428\r
429 Status = gBS->OpenProtocol (\r
430 Dhcp4Handle,\r
431 &gEfiDhcp4ProtocolGuid,\r
432 (VOID **) &Dhcp4,\r
433 Image,\r
434 Controller,\r
435 EFI_OPEN_PROTOCOL_BY_DRIVER\r
436 );\r
437 if (EFI_ERROR (Status)) {\r
438 goto ON_EXIT;\r
439 }\r
440\r
441 NvData = &ConfigData->SessionConfigData;\r
442\r
443 ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);\r
444 if (ParaList == NULL) {\r
445 Status = EFI_OUT_OF_RESOURCES;\r
446 goto ON_EXIT;\r
447 }\r
448\r
449 //\r
450 // Ask the server to reply with Netmask, Router, DNS, and RootPath options.\r
451 //\r
452 ParaList->OpCode = DHCP4_TAG_PARA_LIST;\r
453 ParaList->Length = (UINT8) (NvData->TargetInfoFromDhcp ? 4 : 3);\r
454 ParaList->Data[0] = DHCP4_TAG_NETMASK;\r
455 ParaList->Data[1] = DHCP4_TAG_ROUTER;\r
142c00c3
ZL
456 ParaList->Data[2] = DHCP4_TAG_DNS_SERVER;\r
457 ParaList->Data[3] = DHCP4_TAG_ROOTPATH;\r
4c5a5e0c 458\r
459 ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));\r
460 Dhcp4ConfigData.OptionCount = 1;\r
461 Dhcp4ConfigData.OptionList = &ParaList;\r
462\r
463 if (NvData->TargetInfoFromDhcp) {\r
464 //\r
465 // Use callback to select an offer that contains target information.\r
466 //\r
467 Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;\r
468 Dhcp4ConfigData.CallbackContext = ConfigData;\r
469 }\r
470\r
471 Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);\r
472 if (EFI_ERROR (Status)) {\r
473 goto ON_EXIT;\r
474 }\r
475\r
476 Status = Dhcp4->Start (Dhcp4, NULL);\r
477 if (EFI_ERROR (Status)) {\r
478 goto ON_EXIT;\r
479 }\r
480 //\r
481 // Parse the ACK to get required information.\r
482 //\r
483 Status = IScsiParseDhcpAck (Dhcp4, ConfigData);\r
484\r
485ON_EXIT:\r
486\r
487 if (ParaList != NULL) {\r
488 FreePool (ParaList);\r
489 }\r
490\r
491 if (Dhcp4 != NULL) {\r
492 Dhcp4->Stop (Dhcp4);\r
493 Dhcp4->Configure (Dhcp4, NULL);\r
494\r
495 gBS->CloseProtocol (\r
496 Dhcp4Handle,\r
497 &gEfiDhcp4ProtocolGuid,\r
498 Image,\r
499 Controller\r
500 );\r
501 }\r
502\r
503 NetLibDestroyServiceChild (\r
504 Controller,\r
505 Image,\r
506 &gEfiDhcp4ServiceBindingProtocolGuid,\r
507 Dhcp4Handle\r
508 );\r
509\r
510 return Status;\r
511}\r