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