]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/IScsiDxe/IScsiDhcp.c
unified "iSCSI" in all comments
[mirror_edk2.git] / MdeModulePkg / Universal / Network / IScsiDxe / IScsiDhcp.c
1 /** @file
2 iSCSI DHCP related configuration routines.
3
4 Copyright (c) 2004 - 2007, 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 RP_FIELD_IDX 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 gBS->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 IScsiDhcpSelectOffer (
188 IN EFI_DHCP4_PROTOCOL * This,
189 IN VOID *Context,
190 IN EFI_DHCP4_STATE CurrentState,
191 IN EFI_DHCP4_EVENT Dhcp4Event,
192 IN EFI_DHCP4_PACKET * Packet, OPTIONAL
193 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
194 )
195 {
196 EFI_STATUS Status;
197 UINT32 OptionCount;
198 EFI_DHCP4_PACKET_OPTION **OptionList;
199 UINT32 Index;
200
201 if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
202 return EFI_SUCCESS;
203 }
204
205 OptionCount = 0;
206
207 Status = This->Parse (This, Packet, &OptionCount, NULL);
208 if (Status != EFI_BUFFER_TOO_SMALL) {
209 return EFI_NOT_READY;
210 }
211
212 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
213 if (OptionList == NULL) {
214 return EFI_NOT_READY;
215 }
216
217 Status = This->Parse (This, Packet, &OptionCount, OptionList);
218 if (EFI_ERROR (Status)) {
219 gBS->FreePool (OptionList);
220 return EFI_NOT_READY;
221 }
222
223 for (Index = 0; Index < OptionCount; Index++) {
224 if (OptionList[Index]->OpCode != DHCP4_TAG_ROOT_PATH) {
225 continue;
226 }
227
228 Status = IScsiDhcpExtractRootPath (
229 (CHAR8 *) &OptionList[Index]->Data[0],
230 OptionList[Index]->Length,
231 (ISCSI_SESSION_CONFIG_NVDATA *) Context
232 );
233
234 break;
235 }
236
237 if ((Index == OptionCount)) {
238 Status = EFI_NOT_READY;
239 }
240
241 gBS->FreePool (OptionList);
242
243 return Status;
244 }
245
246 /**
247 Parse the DHCP ACK to get the address configuration and DNS information.
248
249 @param[in] Dhcp4 The DHCP4 protocol.
250 @param[in, out] ConfigData The session configuration data.
251
252 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
253 @retval EFI_NO_MAPPING DHCP failed to acquire address and other information.
254 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is mal-formatted.
255 @retval EFI_DEVICE_ERROR Other errors as indicated.
256 **/
257 EFI_STATUS
258 IScsiParseDhcpAck (
259 IN EFI_DHCP4_PROTOCOL *Dhcp4,
260 IN OUT ISCSI_SESSION_CONFIG_DATA *ConfigData
261 )
262 {
263 EFI_STATUS Status;
264 EFI_DHCP4_MODE_DATA Dhcp4ModeData;
265 UINT32 OptionCount;
266 EFI_DHCP4_PACKET_OPTION **OptionList;
267 UINT32 Index;
268
269 Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
270 if (EFI_ERROR (Status)) {
271 return Status;
272 }
273
274 if (Dhcp4ModeData.State != Dhcp4Bound) {
275 return EFI_NO_MAPPING;
276 }
277
278 CopyMem (&ConfigData->NvData.LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
279 CopyMem (&ConfigData->NvData.SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
280 CopyMem (&ConfigData->NvData.Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
281
282 OptionCount = 0;
283 OptionList = NULL;
284
285 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
286 if (Status != EFI_BUFFER_TOO_SMALL) {
287 return EFI_DEVICE_ERROR;
288 }
289
290 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
291 if (OptionList == NULL) {
292 return EFI_OUT_OF_RESOURCES;
293 }
294
295 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
296 if (EFI_ERROR (Status)) {
297 gBS->FreePool (OptionList);
298 return EFI_DEVICE_ERROR;
299 }
300
301 for (Index = 0; Index < OptionCount; Index++) {
302 //
303 // Get DNS server addresses and DHCP server address from this offer.
304 //
305 if (OptionList[Index]->OpCode == DHCP4_TAG_DNS) {
306
307 if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
308 Status = EFI_INVALID_PARAMETER;
309 break;
310 }
311 //
312 // Primary DNS server address.
313 //
314 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
315
316 if (OptionList[Index]->Length > 4) {
317 //
318 // Secondary DNS server address
319 //
320 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));
321 }
322 } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {
323 if (OptionList[Index]->Length != 4) {
324 Status = EFI_INVALID_PARAMETER;
325 break;
326 }
327
328 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
329 }
330 }
331
332 gBS->FreePool (OptionList);
333
334 return Status;
335 }
336
337 /**
338 Parse the DHCP ACK to get the address configuration and DNS information.
339
340 @param[in] Image The handle of the driver image.
341 @param[in] Controller The handle of the controller;
342 @param[in, out] ConfigData The session configuration data.
343
344 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
345 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
346 @retval Others Other errors as indicated.
347 **/
348 EFI_STATUS
349 IScsiDoDhcp (
350 IN EFI_HANDLE Image,
351 IN EFI_HANDLE Controller,
352 IN OUT ISCSI_SESSION_CONFIG_DATA *ConfigData
353 )
354 {
355 EFI_HANDLE Dhcp4Handle;
356 EFI_DHCP4_PROTOCOL *Dhcp4;
357 EFI_STATUS Status;
358 EFI_DHCP4_PACKET_OPTION *ParaList;
359 EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;
360
361 Dhcp4Handle = NULL;
362 Dhcp4 = NULL;
363 ParaList = NULL;
364
365 //
366 // Create a DHCP4 child instance and get the protocol.
367 //
368 Status = NetLibCreateServiceChild (
369 Controller,
370 Image,
371 &gEfiDhcp4ServiceBindingProtocolGuid,
372 &Dhcp4Handle
373 );
374 if (EFI_ERROR (Status)) {
375 return Status;
376 }
377
378 Status = gBS->OpenProtocol (
379 Dhcp4Handle,
380 &gEfiDhcp4ProtocolGuid,
381 (VOID **)&Dhcp4,
382 Image,
383 Controller,
384 EFI_OPEN_PROTOCOL_BY_DRIVER
385 );
386 if (EFI_ERROR (Status)) {
387 goto ON_EXIT;
388 }
389
390 ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);
391 if (ParaList == NULL) {
392 Status = EFI_OUT_OF_RESOURCES;
393 goto ON_EXIT;
394 }
395 //
396 // Ask the server to reply with Netmask, Router, DNS and RootPath options.
397 //
398 ParaList->OpCode = DHCP4_TAG_PARA_LIST;
399 ParaList->Length = (UINT8) (ConfigData->NvData.TargetInfoFromDhcp ? 4 : 3);
400 ParaList->Data[0] = DHCP4_TAG_NETMASK;
401 ParaList->Data[1] = DHCP4_TAG_ROUTER;
402 ParaList->Data[2] = DHCP4_TAG_DNS;
403 ParaList->Data[3] = DHCP4_TAG_ROOT_PATH;
404
405 ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));
406 Dhcp4ConfigData.OptionCount = 1;
407 Dhcp4ConfigData.OptionList = &ParaList;
408
409 if (ConfigData->NvData.TargetInfoFromDhcp) {
410 //
411 // Use callback to select an offer which contains target information.
412 //
413 Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;
414 Dhcp4ConfigData.CallbackContext = &ConfigData->NvData;
415 }
416
417 Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);
418 if (EFI_ERROR (Status)) {
419 goto ON_EXIT;
420 }
421
422 Status = Dhcp4->Start (Dhcp4, NULL);
423 if (EFI_ERROR (Status)) {
424 goto ON_EXIT;
425 }
426 //
427 // Parse the ACK to get required information.
428 //
429 Status = IScsiParseDhcpAck (Dhcp4, ConfigData);
430
431 ON_EXIT:
432
433 if (ParaList != NULL) {
434 gBS->FreePool (ParaList);
435 }
436
437 if (Dhcp4 != NULL) {
438 Dhcp4->Stop (Dhcp4);
439 Dhcp4->Configure (Dhcp4, NULL);
440
441 gBS->CloseProtocol (
442 Dhcp4Handle,
443 &gEfiDhcp4ProtocolGuid,
444 Image,
445 Controller
446 );
447 }
448
449 NetLibDestroyServiceChild (
450 Controller,
451 Image,
452 &gEfiDhcp4ServiceBindingProtocolGuid,
453 Dhcp4Handle
454 );
455
456 return Status;
457 }