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