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