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