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