2 iSCSI DHCP4 related configuration routines.
4 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
12 Extract the Root Path option and get the required target information.
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.
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.
26 IScsiDhcpExtractRootPath (
29 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
33 UINT8 IScsiRootPathIdLen
;
35 ISCSI_ROOT_PATH_FIELD Fields
[RP_FIELD_IDX_MAX
];
36 ISCSI_ROOT_PATH_FIELD
*Field
;
39 ISCSI_SESSION_CONFIG_NVDATA
*ConfigNvData
;
43 ConfigNvData
= &ConfigData
->SessionConfigData
;
46 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
48 IScsiRootPathIdLen
= (UINT8
)AsciiStrLen (ISCSI_ROOT_PATH_ID
);
50 if ((Length
<= IScsiRootPathIdLen
) || (CompareMem (RootPath
, ISCSI_ROOT_PATH_ID
, IScsiRootPathIdLen
) != 0)) {
55 // Skip the iSCSI RootPath ID "iscsi:".
57 RootPath
+= IScsiRootPathIdLen
;
58 Length
= (UINT8
)(Length
- IScsiRootPathIdLen
);
60 TmpStr
= (CHAR8
*)AllocatePool (Length
+ 1);
62 return EFI_OUT_OF_RESOURCES
;
65 CopyMem (TmpStr
, RootPath
, Length
);
66 TmpStr
[Length
] = '\0';
69 FieldIndex
= RP_FIELD_IDX_SERVERNAME
;
70 ZeroMem (&Fields
[0], sizeof (Fields
));
73 // Extract the fields in the Root Path option string.
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
];
80 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) && (Index
< Length
)) {
84 if (TmpStr
[Index
] == ISCSI_ROOT_PATH_FIELD_DELIMITER
) {
85 if (FieldIndex
!= RP_FIELD_IDX_TARGETNAME
) {
90 if (Fields
[FieldIndex
].Str
!= NULL
) {
91 Fields
[FieldIndex
].Len
= (UINT8
)AsciiStrLen (Fields
[FieldIndex
].Str
);
96 if (FieldIndex
!= RP_FIELD_IDX_MAX
) {
97 Status
= EFI_INVALID_PARAMETER
;
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)
106 Status
= EFI_INVALID_PARAMETER
;
111 // Get the IP address of the target.
113 Field
= &Fields
[RP_FIELD_IDX_SERVERNAME
];
115 if (ConfigNvData
->IpMode
< IP_MODE_AUTOCONFIG
) {
116 IpMode
= ConfigNvData
->IpMode
;
118 IpMode
= ConfigData
->AutoConfigureMode
;
122 // Server name is expressed as domain name, just save it.
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
;
130 CopyMem (&ConfigNvData
->TargetUrl
, Field
->Str
, Field
->Len
);
131 ConfigNvData
->TargetUrl
[Field
->Len
+ 1] = '\0';
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
));
138 if (EFI_ERROR (Status
)) {
144 // Check the protocol type.
146 Field
= &Fields
[RP_FIELD_IDX_PROTOCOL
];
147 if ((Field
->Str
!= NULL
) && ((*(Field
->Str
) - '0') != EFI_IP_PROTO_TCP
)) {
148 Status
= EFI_INVALID_PARAMETER
;
153 // Get the port of the iSCSI target.
155 Field
= &Fields
[RP_FIELD_IDX_PORT
];
156 if (Field
->Str
!= NULL
) {
157 ConfigNvData
->TargetPort
= (UINT16
)AsciiStrDecimalToUintn (Field
->Str
);
159 ConfigNvData
->TargetPort
= ISCSI_WELL_KNOWN_PORT
;
165 Field
= &Fields
[RP_FIELD_IDX_LUN
];
166 if (Field
->Str
!= NULL
) {
167 Status
= IScsiAsciiStrToLun (Field
->Str
, ConfigNvData
->BootLun
);
168 if (EFI_ERROR (Status
)) {
172 ZeroMem (ConfigNvData
->BootLun
, sizeof (ConfigNvData
->BootLun
));
176 // Get the target iSCSI Name.
178 Field
= &Fields
[RP_FIELD_IDX_TARGETNAME
];
180 if (AsciiStrLen (Field
->Str
) > ISCSI_NAME_MAX_SIZE
- 1) {
181 Status
= EFI_INVALID_PARAMETER
;
186 // Validate the iSCSI name.
188 Status
= IScsiNormalizeName (Field
->Str
, AsciiStrLen (Field
->Str
));
189 if (EFI_ERROR (Status
)) {
193 AsciiStrCpyS (ConfigNvData
->TargetName
, ISCSI_NAME_MAX_SIZE
, Field
->Str
);
203 The callback function registered to the DHCP4 instance that is used to select
204 the qualified DHCP OFFER.
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.
213 @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted
215 @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.
216 @retval Others Other errors as indicated.
221 IScsiDhcpSelectOffer (
222 IN EFI_DHCP4_PROTOCOL
*This
,
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
232 EFI_DHCP4_PACKET_OPTION
**OptionList
;
235 if ((Dhcp4Event
!= Dhcp4RcvdOffer
) && (Dhcp4Event
!= Dhcp4SelectOffer
)) {
241 Status
= This
->Parse (This
, Packet
, &OptionCount
, NULL
);
242 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
243 return EFI_NOT_READY
;
246 OptionList
= AllocatePool (OptionCount
* sizeof (EFI_DHCP4_PACKET_OPTION
*));
247 if (OptionList
== NULL
) {
248 return EFI_NOT_READY
;
251 Status
= This
->Parse (This
, Packet
, &OptionCount
, OptionList
);
252 if (EFI_ERROR (Status
)) {
253 FreePool (OptionList
);
254 return EFI_NOT_READY
;
257 for (Index
= 0; Index
< OptionCount
; Index
++) {
258 if (OptionList
[Index
]->OpCode
!= DHCP4_TAG_ROOTPATH
) {
262 Status
= IScsiDhcpExtractRootPath (
263 (CHAR8
*)&OptionList
[Index
]->Data
[0],
264 OptionList
[Index
]->Length
,
265 (ISCSI_ATTEMPT_CONFIG_NVDATA
*)Context
271 if (Index
== OptionCount
) {
272 Status
= EFI_NOT_READY
;
275 FreePool (OptionList
);
281 Parse the DHCP ACK to get the address configuration and DNS information.
283 @param[in] Dhcp4 The DHCP4 protocol.
284 @param[in, out] ConfigData The session configuration data.
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.
295 IN EFI_DHCP4_PROTOCOL
*Dhcp4
,
296 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
300 EFI_DHCP4_MODE_DATA Dhcp4ModeData
;
302 EFI_DHCP4_PACKET_OPTION
**OptionList
;
304 ISCSI_SESSION_CONFIG_NVDATA
*NvData
;
306 Status
= Dhcp4
->GetModeData (Dhcp4
, &Dhcp4ModeData
);
307 if (EFI_ERROR (Status
)) {
311 if (Dhcp4ModeData
.State
!= Dhcp4Bound
) {
312 return EFI_NO_MAPPING
;
315 NvData
= &ConfigData
->SessionConfigData
;
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
));
324 Status
= Dhcp4
->Parse (Dhcp4
, Dhcp4ModeData
.ReplyPacket
, &OptionCount
, OptionList
);
325 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
326 return EFI_DEVICE_ERROR
;
329 OptionList
= AllocatePool (OptionCount
* sizeof (EFI_DHCP4_PACKET_OPTION
*));
330 if (OptionList
== NULL
) {
331 return EFI_OUT_OF_RESOURCES
;
334 Status
= Dhcp4
->Parse (Dhcp4
, Dhcp4ModeData
.ReplyPacket
, &OptionCount
, OptionList
);
335 if (EFI_ERROR (Status
)) {
336 FreePool (OptionList
);
337 return EFI_DEVICE_ERROR
;
340 for (Index
= 0; Index
< OptionCount
; Index
++) {
342 // Get DNS server addresses and DHCP server address from this offer.
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
;
351 // Primary DNS server address.
353 CopyMem (&ConfigData
->PrimaryDns
, &OptionList
[Index
]->Data
[0], sizeof (EFI_IPv4_ADDRESS
));
355 if (OptionList
[Index
]->Length
> 4) {
357 // Secondary DNS server address.
359 CopyMem (&ConfigData
->SecondaryDns
, &OptionList
[Index
]->Data
[4], sizeof (EFI_IPv4_ADDRESS
));
361 } else if (OptionList
[Index
]->OpCode
== DHCP4_TAG_SERVER_ID
) {
362 if (OptionList
[Index
]->Length
!= 4) {
363 Status
= EFI_INVALID_PARAMETER
;
367 CopyMem (&ConfigData
->DhcpServer
, &OptionList
[Index
]->Data
[0], sizeof (EFI_IPv4_ADDRESS
));
371 FreePool (OptionList
);
377 This function will switch the IP4 configuration policy to Static.
379 @param[in] Ip4Config2 Pointer to the IP4 configuration protocol.
381 @retval EFI_SUCCESS The policy is already configured to static.
382 @retval Others Other error as indicated.
387 IN EFI_IP4_CONFIG2_PROTOCOL
*Ip4Config2
390 EFI_IP4_CONFIG2_POLICY Policy
;
394 DataSize
= sizeof (EFI_IP4_CONFIG2_POLICY
);
395 Status
= Ip4Config2
->GetData (
397 Ip4Config2DataTypePolicy
,
401 if (EFI_ERROR (Status
)) {
405 if (Policy
!= Ip4Config2PolicyStatic
) {
406 Policy
= Ip4Config2PolicyStatic
;
407 Status
= Ip4Config2
->SetData (
409 Ip4Config2DataTypePolicy
,
410 sizeof (EFI_IP4_CONFIG2_POLICY
),
413 if (EFI_ERROR (Status
)) {
422 Parse the DHCP ACK to get the address configuration and DNS information.
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.
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.
437 IN EFI_HANDLE Controller
,
438 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
441 EFI_HANDLE Dhcp4Handle
;
442 EFI_IP4_CONFIG2_PROTOCOL
*Ip4Config2
;
443 EFI_DHCP4_PROTOCOL
*Dhcp4
;
445 EFI_DHCP4_PACKET_OPTION
*ParaList
;
446 EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData
;
447 ISCSI_SESSION_CONFIG_NVDATA
*NvData
;
448 EFI_STATUS MediaStatus
;
456 // Check media status before doing DHCP.
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");
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.
472 Status
= gBS
->HandleProtocol (Controller
, &gEfiIp4Config2ProtocolGuid
, (VOID
**)&Ip4Config2
);
473 if (!EFI_ERROR (Status
)) {
474 Status
= IScsiSetIp4Policy (Ip4Config2
);
475 if (EFI_ERROR (Status
)) {
481 // Create a DHCP4 child instance and get the protocol.
483 Status
= NetLibCreateServiceChild (
486 &gEfiDhcp4ServiceBindingProtocolGuid
,
489 if (EFI_ERROR (Status
)) {
493 Status
= gBS
->OpenProtocol (
495 &gEfiDhcp4ProtocolGuid
,
499 EFI_OPEN_PROTOCOL_BY_DRIVER
501 if (EFI_ERROR (Status
)) {
505 NvData
= &ConfigData
->SessionConfigData
;
507 ParaList
= AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION
) + 3);
508 if (ParaList
== NULL
) {
509 Status
= EFI_OUT_OF_RESOURCES
;
514 // Ask the server to reply with Netmask, Router, DNS, and RootPath options.
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
;
523 ZeroMem (&Dhcp4ConfigData
, sizeof (EFI_DHCP4_CONFIG_DATA
));
524 Dhcp4ConfigData
.OptionCount
= 1;
525 Dhcp4ConfigData
.OptionList
= &ParaList
;
527 if (NvData
->TargetInfoFromDhcp
) {
529 // Use callback to select an offer that contains target information.
531 Dhcp4ConfigData
.Dhcp4Callback
= IScsiDhcpSelectOffer
;
532 Dhcp4ConfigData
.CallbackContext
= ConfigData
;
535 Status
= Dhcp4
->Configure (Dhcp4
, &Dhcp4ConfigData
);
536 if (EFI_ERROR (Status
)) {
540 Status
= Dhcp4
->Start (Dhcp4
, NULL
);
541 if (EFI_ERROR (Status
)) {
546 // Parse the ACK to get required information.
548 Status
= IScsiParseDhcpAck (Dhcp4
, ConfigData
);
552 if (ParaList
!= NULL
) {
558 Dhcp4
->Configure (Dhcp4
, NULL
);
562 &gEfiDhcp4ProtocolGuid
,
568 NetLibDestroyServiceChild (
571 &gEfiDhcp4ServiceBindingProtocolGuid
,