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