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