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