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
13 Extract the Root Path option and get the required target information.
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.
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.
27 IScsiDhcpExtractRootPath (
30 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
34 UINT8 IScsiRootPathIdLen
;
36 ISCSI_ROOT_PATH_FIELD Fields
[RP_FIELD_IDX_MAX
];
37 ISCSI_ROOT_PATH_FIELD
*Field
;
40 ISCSI_SESSION_CONFIG_NVDATA
*ConfigNvData
;
44 ConfigNvData
= &ConfigData
->SessionConfigData
;
47 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
49 IScsiRootPathIdLen
= (UINT8
) AsciiStrLen (ISCSI_ROOT_PATH_ID
);
51 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
;
110 // Get the IP address of the target.
112 Field
= &Fields
[RP_FIELD_IDX_SERVERNAME
];
114 if (ConfigNvData
->IpMode
< IP_MODE_AUTOCONFIG
) {
115 IpMode
= ConfigNvData
->IpMode
;
117 IpMode
= ConfigData
->AutoConfigureMode
;
121 // Server name is expressed as domain name, just save it.
123 if ((!NET_IS_DIGIT (*(Field
->Str
))) && (*(Field
->Str
) != '[')) {
124 ConfigNvData
->DnsMode
= TRUE
;
125 if ((Field
->Len
+ 2) > sizeof (ConfigNvData
->TargetUrl
)) {
126 return EFI_INVALID_PARAMETER
;
128 CopyMem (&ConfigNvData
->TargetUrl
, Field
->Str
, Field
->Len
);
129 ConfigNvData
->TargetUrl
[Field
->Len
+ 1] = '\0';
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
));
136 if (EFI_ERROR (Status
)) {
141 // Check the protocol type.
143 Field
= &Fields
[RP_FIELD_IDX_PROTOCOL
];
144 if ((Field
->Str
!= NULL
) && ((*(Field
->Str
) - '0') != EFI_IP_PROTO_TCP
)) {
145 Status
= EFI_INVALID_PARAMETER
;
149 // Get the port of the iSCSI target.
151 Field
= &Fields
[RP_FIELD_IDX_PORT
];
152 if (Field
->Str
!= NULL
) {
153 ConfigNvData
->TargetPort
= (UINT16
) AsciiStrDecimalToUintn (Field
->Str
);
155 ConfigNvData
->TargetPort
= ISCSI_WELL_KNOWN_PORT
;
160 Field
= &Fields
[RP_FIELD_IDX_LUN
];
161 if (Field
->Str
!= NULL
) {
162 Status
= IScsiAsciiStrToLun (Field
->Str
, ConfigNvData
->BootLun
);
163 if (EFI_ERROR (Status
)) {
167 ZeroMem (ConfigNvData
->BootLun
, sizeof (ConfigNvData
->BootLun
));
170 // Get the target iSCSI Name.
172 Field
= &Fields
[RP_FIELD_IDX_TARGETNAME
];
174 if (AsciiStrLen (Field
->Str
) > ISCSI_NAME_MAX_SIZE
- 1) {
175 Status
= EFI_INVALID_PARAMETER
;
179 // Validate the iSCSI name.
181 Status
= IScsiNormalizeName (Field
->Str
, AsciiStrLen (Field
->Str
));
182 if (EFI_ERROR (Status
)) {
186 AsciiStrCpyS (ConfigNvData
->TargetName
, ISCSI_NAME_MAX_SIZE
, Field
->Str
);
196 The callback function registered to the DHCP4 instance that is used to select
197 the qualified DHCP OFFER.
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.
206 @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted
208 @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.
209 @retval Others Other errors as indicated.
214 IScsiDhcpSelectOffer (
215 IN EFI_DHCP4_PROTOCOL
*This
,
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
225 EFI_DHCP4_PACKET_OPTION
**OptionList
;
228 if ((Dhcp4Event
!= Dhcp4RcvdOffer
) && (Dhcp4Event
!= Dhcp4SelectOffer
)) {
234 Status
= This
->Parse (This
, Packet
, &OptionCount
, NULL
);
235 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
236 return EFI_NOT_READY
;
239 OptionList
= AllocatePool (OptionCount
* sizeof (EFI_DHCP4_PACKET_OPTION
*));
240 if (OptionList
== NULL
) {
241 return EFI_NOT_READY
;
244 Status
= This
->Parse (This
, Packet
, &OptionCount
, OptionList
);
245 if (EFI_ERROR (Status
)) {
246 FreePool (OptionList
);
247 return EFI_NOT_READY
;
250 for (Index
= 0; Index
< OptionCount
; Index
++) {
251 if (OptionList
[Index
]->OpCode
!= DHCP4_TAG_ROOTPATH
) {
255 Status
= IScsiDhcpExtractRootPath (
256 (CHAR8
*) &OptionList
[Index
]->Data
[0],
257 OptionList
[Index
]->Length
,
258 (ISCSI_ATTEMPT_CONFIG_NVDATA
*) Context
264 if (Index
== OptionCount
) {
265 Status
= EFI_NOT_READY
;
268 FreePool (OptionList
);
274 Parse the DHCP ACK to get the address configuration and DNS information.
276 @param[in] Dhcp4 The DHCP4 protocol.
277 @param[in, out] ConfigData The session configuration data.
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.
288 IN EFI_DHCP4_PROTOCOL
*Dhcp4
,
289 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
293 EFI_DHCP4_MODE_DATA Dhcp4ModeData
;
295 EFI_DHCP4_PACKET_OPTION
**OptionList
;
297 ISCSI_SESSION_CONFIG_NVDATA
*NvData
;
299 Status
= Dhcp4
->GetModeData (Dhcp4
, &Dhcp4ModeData
);
300 if (EFI_ERROR (Status
)) {
304 if (Dhcp4ModeData
.State
!= Dhcp4Bound
) {
305 return EFI_NO_MAPPING
;
308 NvData
= &ConfigData
->SessionConfigData
;
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
));
317 Status
= Dhcp4
->Parse (Dhcp4
, Dhcp4ModeData
.ReplyPacket
, &OptionCount
, OptionList
);
318 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
319 return EFI_DEVICE_ERROR
;
322 OptionList
= AllocatePool (OptionCount
* sizeof (EFI_DHCP4_PACKET_OPTION
*));
323 if (OptionList
== NULL
) {
324 return EFI_OUT_OF_RESOURCES
;
327 Status
= Dhcp4
->Parse (Dhcp4
, Dhcp4ModeData
.ReplyPacket
, &OptionCount
, OptionList
);
328 if (EFI_ERROR (Status
)) {
329 FreePool (OptionList
);
330 return EFI_DEVICE_ERROR
;
333 for (Index
= 0; Index
< OptionCount
; Index
++) {
335 // Get DNS server addresses and DHCP server address from this offer.
337 if (OptionList
[Index
]->OpCode
== DHCP4_TAG_DNS_SERVER
) {
339 if (((OptionList
[Index
]->Length
& 0x3) != 0) || (OptionList
[Index
]->Length
== 0)) {
340 Status
= EFI_INVALID_PARAMETER
;
344 // Primary DNS server address.
346 CopyMem (&ConfigData
->PrimaryDns
, &OptionList
[Index
]->Data
[0], sizeof (EFI_IPv4_ADDRESS
));
348 if (OptionList
[Index
]->Length
> 4) {
350 // Secondary DNS server address.
352 CopyMem (&ConfigData
->SecondaryDns
, &OptionList
[Index
]->Data
[4], sizeof (EFI_IPv4_ADDRESS
));
354 } else if (OptionList
[Index
]->OpCode
== DHCP4_TAG_SERVER_ID
) {
355 if (OptionList
[Index
]->Length
!= 4) {
356 Status
= EFI_INVALID_PARAMETER
;
360 CopyMem (&ConfigData
->DhcpServer
, &OptionList
[Index
]->Data
[0], sizeof (EFI_IPv4_ADDRESS
));
364 FreePool (OptionList
);
370 This function will switch the IP4 configuration policy to Static.
372 @param[in] Ip4Config2 Pointer to the IP4 configuration protocol.
374 @retval EFI_SUCCESS The policy is already configured to static.
375 @retval Others Other error as indicated.
380 IN EFI_IP4_CONFIG2_PROTOCOL
*Ip4Config2
383 EFI_IP4_CONFIG2_POLICY Policy
;
387 DataSize
= sizeof (EFI_IP4_CONFIG2_POLICY
);
388 Status
= Ip4Config2
->GetData (
390 Ip4Config2DataTypePolicy
,
394 if (EFI_ERROR (Status
)) {
398 if (Policy
!= Ip4Config2PolicyStatic
) {
399 Policy
= Ip4Config2PolicyStatic
;
400 Status
= Ip4Config2
->SetData (
402 Ip4Config2DataTypePolicy
,
403 sizeof (EFI_IP4_CONFIG2_POLICY
),
406 if (EFI_ERROR (Status
)) {
415 Parse the DHCP ACK to get the address configuration and DNS information.
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.
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.
430 IN EFI_HANDLE Controller
,
431 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
434 EFI_HANDLE Dhcp4Handle
;
435 EFI_IP4_CONFIG2_PROTOCOL
*Ip4Config2
;
436 EFI_DHCP4_PROTOCOL
*Dhcp4
;
438 EFI_DHCP4_PACKET_OPTION
*ParaList
;
439 EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData
;
440 ISCSI_SESSION_CONFIG_NVDATA
*NvData
;
441 EFI_STATUS MediaStatus
;
449 // Check media status before doing DHCP.
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");
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 its policy to static.
465 Status
= gBS
->HandleProtocol (Controller
, &gEfiIp4Config2ProtocolGuid
, (VOID
**) &Ip4Config2
);
466 if (!EFI_ERROR (Status
)) {
467 Status
= IScsiSetIp4Policy (Ip4Config2
);
468 if (EFI_ERROR (Status
)) {
474 // Create a DHCP4 child instance and get the protocol.
476 Status
= NetLibCreateServiceChild (
479 &gEfiDhcp4ServiceBindingProtocolGuid
,
482 if (EFI_ERROR (Status
)) {
486 Status
= gBS
->OpenProtocol (
488 &gEfiDhcp4ProtocolGuid
,
492 EFI_OPEN_PROTOCOL_BY_DRIVER
494 if (EFI_ERROR (Status
)) {
498 NvData
= &ConfigData
->SessionConfigData
;
500 ParaList
= AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION
) + 3);
501 if (ParaList
== NULL
) {
502 Status
= EFI_OUT_OF_RESOURCES
;
507 // Ask the server to reply with Netmask, Router, DNS, and RootPath options.
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
;
516 ZeroMem (&Dhcp4ConfigData
, sizeof (EFI_DHCP4_CONFIG_DATA
));
517 Dhcp4ConfigData
.OptionCount
= 1;
518 Dhcp4ConfigData
.OptionList
= &ParaList
;
520 if (NvData
->TargetInfoFromDhcp
) {
522 // Use callback to select an offer that contains target information.
524 Dhcp4ConfigData
.Dhcp4Callback
= IScsiDhcpSelectOffer
;
525 Dhcp4ConfigData
.CallbackContext
= ConfigData
;
528 Status
= Dhcp4
->Configure (Dhcp4
, &Dhcp4ConfigData
);
529 if (EFI_ERROR (Status
)) {
533 Status
= Dhcp4
->Start (Dhcp4
, NULL
);
534 if (EFI_ERROR (Status
)) {
538 // Parse the ACK to get required information.
540 Status
= IScsiParseDhcpAck (Dhcp4
, ConfigData
);
544 if (ParaList
!= NULL
) {
550 Dhcp4
->Configure (Dhcp4
, NULL
);
554 &gEfiDhcp4ProtocolGuid
,
560 NetLibDestroyServiceChild (
563 &gEfiDhcp4ServiceBindingProtocolGuid
,