2 iSCSI DHCP6 related configuration routines.
4 Copyright (c) 2009 - 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 from
14 Boot File Uniform Resource Locator (URL) Option.
16 @param[in] RootPath The RootPath string.
17 @param[in] Length Length of the RootPath option payload.
18 @param[in, out] ConfigData The iSCSI session configuration data read from
21 @retval EFI_SUCCESS All required information is extracted from the
23 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
24 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
25 @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
29 IScsiDhcp6ExtractRootPath (
32 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
36 UINT16 IScsiRootPathIdLen
;
38 ISCSI_ROOT_PATH_FIELD Fields
[RP_FIELD_IDX_MAX
];
39 ISCSI_ROOT_PATH_FIELD
*Field
;
42 ISCSI_SESSION_CONFIG_NVDATA
*ConfigNvData
;
46 ConfigNvData
= &ConfigData
->SessionConfigData
;
47 ConfigNvData
->DnsMode
= FALSE
;
49 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
51 IScsiRootPathIdLen
= (UINT16
) AsciiStrLen (ISCSI_ROOT_PATH_ID
);
53 if ((Length
<= IScsiRootPathIdLen
) ||
54 (CompareMem (RootPath
, ISCSI_ROOT_PATH_ID
, IScsiRootPathIdLen
) != 0)) {
58 // Skip the iSCSI RootPath ID "iscsi:".
60 RootPath
= RootPath
+ IScsiRootPathIdLen
;
61 Length
= (UINT16
) (Length
- IScsiRootPathIdLen
);
63 TmpStr
= (CHAR8
*) AllocatePool (Length
+ 1);
65 return EFI_OUT_OF_RESOURCES
;
68 CopyMem (TmpStr
, RootPath
, Length
);
69 TmpStr
[Length
] = '\0';
73 ZeroMem (&Fields
[0], sizeof (Fields
));
76 // Extract SERVERNAME field in the Root Path option.
78 if (TmpStr
[Index
] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER
) {
80 // The servername is expressed as domain name.
82 ConfigNvData
->DnsMode
= TRUE
;
87 Fields
[RP_FIELD_IDX_SERVERNAME
].Str
= &TmpStr
[Index
];
89 if (!ConfigNvData
->DnsMode
) {
90 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER
)&& (Index
< Length
)) {
100 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) && (Index
< Length
)) {
106 TmpStr
[Index
] = '\0';
110 Fields
[RP_FIELD_IDX_SERVERNAME
].Len
= (UINT8
) AsciiStrLen (Fields
[RP_FIELD_IDX_SERVERNAME
].Str
);
113 // Extract others fields in the Root Path option string.
115 for (FieldIndex
= 1; (FieldIndex
< RP_FIELD_IDX_MAX
) && (Index
< Length
); FieldIndex
++) {
117 if (TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) {
118 Fields
[FieldIndex
].Str
= &TmpStr
[Index
];
121 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) && (Index
< Length
)) {
125 if (TmpStr
[Index
] == ISCSI_ROOT_PATH_FIELD_DELIMITER
) {
126 if (FieldIndex
!= RP_FIELD_IDX_TARGETNAME
) {
127 TmpStr
[Index
] = '\0';
131 if (Fields
[FieldIndex
].Str
!= NULL
) {
132 Fields
[FieldIndex
].Len
= (UINT8
) AsciiStrLen (Fields
[FieldIndex
].Str
);
137 if (FieldIndex
!= RP_FIELD_IDX_MAX
) {
138 Status
= EFI_INVALID_PARAMETER
;
142 if ((Fields
[RP_FIELD_IDX_SERVERNAME
].Str
== NULL
) ||
143 (Fields
[RP_FIELD_IDX_TARGETNAME
].Str
== NULL
) ||
144 (Fields
[RP_FIELD_IDX_PROTOCOL
].Len
> 1)
147 Status
= EFI_INVALID_PARAMETER
;
151 // Get the IP address of the target.
153 Field
= &Fields
[RP_FIELD_IDX_SERVERNAME
];
154 if (ConfigNvData
->IpMode
< IP_MODE_AUTOCONFIG
) {
155 IpMode
= ConfigNvData
->IpMode
;
157 IpMode
= ConfigData
->AutoConfigureMode
;
161 // Server name is expressed as domain name, just save it.
163 if (ConfigNvData
->DnsMode
) {
164 if ((Field
->Len
+ 2) > sizeof (ConfigNvData
->TargetUrl
)) {
165 return EFI_INVALID_PARAMETER
;
167 CopyMem (&ConfigNvData
->TargetUrl
, Field
->Str
, Field
->Len
);
168 ConfigNvData
->TargetUrl
[Field
->Len
+ 1] = '\0';
170 ZeroMem(&ConfigNvData
->TargetUrl
, sizeof (ConfigNvData
->TargetUrl
));
171 Status
= IScsiAsciiStrToIp (Field
->Str
, IpMode
, &Ip
);
172 CopyMem (&ConfigNvData
->TargetIp
, &Ip
, sizeof (EFI_IP_ADDRESS
));
174 if (EFI_ERROR (Status
)) {
180 // Check the protocol type.
182 Field
= &Fields
[RP_FIELD_IDX_PROTOCOL
];
183 if ((Field
->Str
!= NULL
) && ((*(Field
->Str
) - '0') != EFI_IP_PROTO_TCP
)) {
184 Status
= EFI_INVALID_PARAMETER
;
188 // Get the port of the iSCSI target.
190 Field
= &Fields
[RP_FIELD_IDX_PORT
];
191 if (Field
->Str
!= NULL
) {
192 ConfigNvData
->TargetPort
= (UINT16
) AsciiStrDecimalToUintn (Field
->Str
);
194 ConfigNvData
->TargetPort
= ISCSI_WELL_KNOWN_PORT
;
199 Field
= &Fields
[RP_FIELD_IDX_LUN
];
200 if (Field
->Str
!= NULL
) {
201 Status
= IScsiAsciiStrToLun (Field
->Str
, ConfigNvData
->BootLun
);
202 if (EFI_ERROR (Status
)) {
206 ZeroMem (ConfigNvData
->BootLun
, sizeof (ConfigNvData
->BootLun
));
209 // Get the target iSCSI Name.
211 Field
= &Fields
[RP_FIELD_IDX_TARGETNAME
];
213 if (AsciiStrLen (Field
->Str
) > ISCSI_NAME_MAX_SIZE
- 1) {
214 Status
= EFI_INVALID_PARAMETER
;
218 // Validate the iSCSI name.
220 Status
= IScsiNormalizeName (Field
->Str
, AsciiStrLen (Field
->Str
));
221 if (EFI_ERROR (Status
)) {
225 AsciiStrCpyS (ConfigNvData
->TargetName
, ISCSI_NAME_MAX_SIZE
, Field
->Str
);
235 EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol
236 instance to intercept events that occurs in the DHCPv6 Information Request
239 @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that
240 is used to configure this callback function.
241 @param[in] Context Pointer to the context that is initialized in
242 the EFI_DHCP6_PROTOCOL.InfoRequest().
243 @param[in] Packet Pointer to Reply packet that has been received.
244 The EFI DHCPv6 Protocol instance is responsible
245 for freeing the buffer.
247 @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish
248 Information Request exchange process.
249 @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue
250 Information Request exchange process.
251 @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort
252 the Information Request exchange process.
253 @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish
254 the Information Request exchange process because some
255 request information are not received.
260 IScsiDhcp6ParseReply (
261 IN EFI_DHCP6_PROTOCOL
*This
,
263 IN EFI_DHCP6_PACKET
*Packet
269 EFI_DHCP6_PACKET_OPTION
*BootFileOpt
;
270 EFI_DHCP6_PACKET_OPTION
**OptionList
;
271 ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
;
277 Status
= This
->Parse (This
, Packet
, &OptionCount
, NULL
);
278 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
279 return EFI_NOT_READY
;
282 OptionList
= AllocateZeroPool (OptionCount
* sizeof (EFI_DHCP6_PACKET_OPTION
*));
283 if (OptionList
== NULL
) {
284 return EFI_NOT_READY
;
287 Status
= This
->Parse (This
, Packet
, &OptionCount
, OptionList
);
288 if (EFI_ERROR (Status
)) {
289 Status
= EFI_NOT_READY
;
293 ConfigData
= (ISCSI_ATTEMPT_CONFIG_NVDATA
*) Context
;
295 for (Index
= 0; Index
< OptionCount
; Index
++) {
296 OptionList
[Index
]->OpCode
= NTOHS (OptionList
[Index
]->OpCode
);
297 OptionList
[Index
]->OpLen
= NTOHS (OptionList
[Index
]->OpLen
);
300 // Get DNS server addresses from this reply packet.
302 if (OptionList
[Index
]->OpCode
== DHCP6_OPT_DNS_SERVERS
) {
304 if (((OptionList
[Index
]->OpLen
& 0xf) != 0) || (OptionList
[Index
]->OpLen
== 0)) {
305 Status
= EFI_UNSUPPORTED
;
309 // Primary DNS server address.
311 CopyMem (&ConfigData
->PrimaryDns
, &OptionList
[Index
]->Data
[0], sizeof (EFI_IPv6_ADDRESS
));
313 if (OptionList
[Index
]->OpLen
> 16) {
315 // Secondary DNS server address
317 CopyMem (&ConfigData
->SecondaryDns
, &OptionList
[Index
]->Data
[16], sizeof (EFI_IPv6_ADDRESS
));
320 } else if (OptionList
[Index
]->OpCode
== DHCP6_OPT_BOOT_FILE_URL
) {
322 // The server sends this option to inform the client about an URL to a boot file.
324 BootFileOpt
= OptionList
[Index
];
325 } else if (OptionList
[Index
]->OpCode
== DHCP6_OPT_BOOT_FILE_PARAM
) {
327 // The server sends this option to inform the client about DHCP6 server address.
329 if (OptionList
[Index
]->OpLen
< 18) {
330 Status
= EFI_UNSUPPORTED
;
334 // Check param-len 1, should be 16 bytes.
336 CopyMem (&ParaLen
, &OptionList
[Index
]->Data
[0], sizeof (UINT16
));
337 if (NTOHS (ParaLen
) != 16) {
338 Status
= EFI_UNSUPPORTED
;
342 CopyMem (&ConfigData
->DhcpServer
, &OptionList
[Index
]->Data
[2], sizeof (EFI_IPv6_ADDRESS
));
346 if (BootFileOpt
== NULL
) {
347 Status
= EFI_UNSUPPORTED
;
352 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option
354 Status
= IScsiDhcp6ExtractRootPath (
355 (CHAR8
*) BootFileOpt
->Data
,
362 FreePool (OptionList
);
368 Parse the DHCP ACK to get the address configuration and DNS information.
370 @param[in] Image The handle of the driver image.
371 @param[in] Controller The handle of the controller;
372 @param[in, out] ConfigData The attempt configuration data.
374 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
375 @retval EFI_NO_MAPPING DHCP failed to acquire address and other
377 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
378 @retval EFI_DEVICE_ERROR Some unexpected error occurred.
379 @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the
381 @retval EFI_NO_MEDIA There was a media error.
387 IN EFI_HANDLE Controller
,
388 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
391 EFI_HANDLE Dhcp6Handle
;
392 EFI_DHCP6_PROTOCOL
*Dhcp6
;
394 EFI_STATUS TimerStatus
;
395 EFI_DHCP6_PACKET_OPTION
*Oro
;
396 EFI_DHCP6_RETRANSMISSION InfoReqReXmit
;
398 EFI_STATUS MediaStatus
;
401 // Check media status before doing DHCP.
403 MediaStatus
= EFI_SUCCESS
;
404 NetLibDetectMediaWaitTimeout (Controller
, ISCSI_CHECK_MEDIA_GET_DHCP_WAITING_TIME
, &MediaStatus
);
405 if (MediaStatus
!= EFI_SUCCESS
) {
406 AsciiPrint ("\n Error: Could not detect network connection.\n");
411 // iSCSI will only request target info from DHCPv6 server.
413 if (!ConfigData
->SessionConfigData
.TargetInfoFromDhcp
) {
423 // Create a DHCP6 child instance and get the protocol.
425 Status
= NetLibCreateServiceChild (
428 &gEfiDhcp6ServiceBindingProtocolGuid
,
431 if (EFI_ERROR (Status
)) {
435 Status
= gBS
->OpenProtocol (
437 &gEfiDhcp6ProtocolGuid
,
441 EFI_OPEN_PROTOCOL_BY_DRIVER
443 if (EFI_ERROR (Status
)) {
447 Oro
= AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION
) + 5);
449 Status
= EFI_OUT_OF_RESOURCES
;
454 // Ask the server to reply with DNS and Boot File URL options by info request.
455 // All members in EFI_DHCP6_PACKET_OPTION are in network order.
457 Oro
->OpCode
= HTONS (DHCP6_OPT_ORO
);
458 Oro
->OpLen
= HTONS (2 * 3);
459 Oro
->Data
[1] = DHCP6_OPT_DNS_SERVERS
;
460 Oro
->Data
[3] = DHCP6_OPT_BOOT_FILE_URL
;
461 Oro
->Data
[5] = DHCP6_OPT_BOOT_FILE_PARAM
;
463 InfoReqReXmit
.Irt
= 4;
464 InfoReqReXmit
.Mrc
= 1;
465 InfoReqReXmit
.Mrt
= 10;
466 InfoReqReXmit
.Mrd
= 30;
468 Status
= Dhcp6
->InfoRequest (
476 IScsiDhcp6ParseReply
,
479 if (Status
== EFI_NO_MAPPING
) {
480 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
481 if (EFI_ERROR (Status
)) {
485 Status
= gBS
->SetTimer (
488 ISCSI_GET_MAPPING_TIMEOUT
491 if (EFI_ERROR (Status
)) {
497 TimerStatus
= gBS
->CheckEvent (Timer
);
499 if (!EFI_ERROR (TimerStatus
)) {
500 Status
= Dhcp6
->InfoRequest (
508 IScsiDhcp6ParseReply
,
513 } while (TimerStatus
== EFI_NOT_READY
);
524 gBS
->CloseEvent (Timer
);
530 &gEfiDhcp6ProtocolGuid
,
536 NetLibDestroyServiceChild (
539 &gEfiDhcp6ServiceBindingProtocolGuid
,