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
12 Extract the Root Path option and get the required target information from
13 Boot File Uniform Resource Locator (URL) Option.
15 @param[in] RootPath The RootPath string.
16 @param[in] Length Length of the RootPath option payload.
17 @param[in, out] ConfigData The iSCSI session configuration data read from
20 @retval EFI_SUCCESS All required information is extracted from the
22 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
23 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
24 @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
28 IScsiDhcp6ExtractRootPath (
31 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
35 UINT16 IScsiRootPathIdLen
;
37 ISCSI_ROOT_PATH_FIELD Fields
[RP_FIELD_IDX_MAX
];
38 ISCSI_ROOT_PATH_FIELD
*Field
;
41 ISCSI_SESSION_CONFIG_NVDATA
*ConfigNvData
;
45 ConfigNvData
= &ConfigData
->SessionConfigData
;
46 ConfigNvData
->DnsMode
= FALSE
;
48 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
50 IScsiRootPathIdLen
= (UINT16
)AsciiStrLen (ISCSI_ROOT_PATH_ID
);
52 if ((Length
<= IScsiRootPathIdLen
) ||
53 (CompareMem (RootPath
, ISCSI_ROOT_PATH_ID
, IScsiRootPathIdLen
) != 0))
59 // Skip the iSCSI RootPath ID "iscsi:".
61 RootPath
= RootPath
+ IScsiRootPathIdLen
;
62 Length
= (UINT16
)(Length
- IScsiRootPathIdLen
);
64 TmpStr
= (CHAR8
*)AllocatePool (Length
+ 1);
66 return EFI_OUT_OF_RESOURCES
;
69 CopyMem (TmpStr
, RootPath
, Length
);
70 TmpStr
[Length
] = '\0';
74 ZeroMem (&Fields
[0], sizeof (Fields
));
77 // Extract SERVERNAME field in the Root Path option.
79 if (TmpStr
[Index
] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER
) {
81 // The servername is expressed as domain name.
83 ConfigNvData
->DnsMode
= TRUE
;
88 Fields
[RP_FIELD_IDX_SERVERNAME
].Str
= &TmpStr
[Index
];
90 if (!ConfigNvData
->DnsMode
) {
91 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER
) && (Index
< Length
)) {
101 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) && (Index
< Length
)) {
108 TmpStr
[Index
] = '\0';
112 Fields
[RP_FIELD_IDX_SERVERNAME
].Len
= (UINT8
)AsciiStrLen (Fields
[RP_FIELD_IDX_SERVERNAME
].Str
);
115 // Extract others fields in the Root Path option string.
117 for (FieldIndex
= 1; (FieldIndex
< RP_FIELD_IDX_MAX
) && (Index
< Length
); FieldIndex
++) {
118 if (TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) {
119 Fields
[FieldIndex
].Str
= &TmpStr
[Index
];
122 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) && (Index
< Length
)) {
126 if (TmpStr
[Index
] == ISCSI_ROOT_PATH_FIELD_DELIMITER
) {
127 if (FieldIndex
!= RP_FIELD_IDX_TARGETNAME
) {
128 TmpStr
[Index
] = '\0';
132 if (Fields
[FieldIndex
].Str
!= NULL
) {
133 Fields
[FieldIndex
].Len
= (UINT8
)AsciiStrLen (Fields
[FieldIndex
].Str
);
138 if (FieldIndex
!= RP_FIELD_IDX_MAX
) {
139 Status
= EFI_INVALID_PARAMETER
;
143 if ((Fields
[RP_FIELD_IDX_SERVERNAME
].Str
== NULL
) ||
144 (Fields
[RP_FIELD_IDX_TARGETNAME
].Str
== NULL
) ||
145 (Fields
[RP_FIELD_IDX_PROTOCOL
].Len
> 1)
148 Status
= EFI_INVALID_PARAMETER
;
153 // Get the IP address of the target.
155 Field
= &Fields
[RP_FIELD_IDX_SERVERNAME
];
156 if (ConfigNvData
->IpMode
< IP_MODE_AUTOCONFIG
) {
157 IpMode
= ConfigNvData
->IpMode
;
159 IpMode
= ConfigData
->AutoConfigureMode
;
163 // Server name is expressed as domain name, just save it.
165 if (ConfigNvData
->DnsMode
) {
166 if ((Field
->Len
+ 2) > sizeof (ConfigNvData
->TargetUrl
)) {
167 return EFI_INVALID_PARAMETER
;
170 CopyMem (&ConfigNvData
->TargetUrl
, Field
->Str
, Field
->Len
);
171 ConfigNvData
->TargetUrl
[Field
->Len
+ 1] = '\0';
173 ZeroMem (&ConfigNvData
->TargetUrl
, sizeof (ConfigNvData
->TargetUrl
));
174 Status
= IScsiAsciiStrToIp (Field
->Str
, IpMode
, &Ip
);
175 CopyMem (&ConfigNvData
->TargetIp
, &Ip
, sizeof (EFI_IP_ADDRESS
));
177 if (EFI_ERROR (Status
)) {
183 // Check the protocol type.
185 Field
= &Fields
[RP_FIELD_IDX_PROTOCOL
];
186 if ((Field
->Str
!= NULL
) && ((*(Field
->Str
) - '0') != EFI_IP_PROTO_TCP
)) {
187 Status
= EFI_INVALID_PARAMETER
;
192 // Get the port of the iSCSI target.
194 Field
= &Fields
[RP_FIELD_IDX_PORT
];
195 if (Field
->Str
!= NULL
) {
196 ConfigNvData
->TargetPort
= (UINT16
)AsciiStrDecimalToUintn (Field
->Str
);
198 ConfigNvData
->TargetPort
= ISCSI_WELL_KNOWN_PORT
;
204 Field
= &Fields
[RP_FIELD_IDX_LUN
];
205 if (Field
->Str
!= NULL
) {
206 Status
= IScsiAsciiStrToLun (Field
->Str
, ConfigNvData
->BootLun
);
207 if (EFI_ERROR (Status
)) {
211 ZeroMem (ConfigNvData
->BootLun
, sizeof (ConfigNvData
->BootLun
));
215 // Get the target iSCSI Name.
217 Field
= &Fields
[RP_FIELD_IDX_TARGETNAME
];
219 if (AsciiStrLen (Field
->Str
) > ISCSI_NAME_MAX_SIZE
- 1) {
220 Status
= EFI_INVALID_PARAMETER
;
225 // Validate the iSCSI name.
227 Status
= IScsiNormalizeName (Field
->Str
, AsciiStrLen (Field
->Str
));
228 if (EFI_ERROR (Status
)) {
232 AsciiStrCpyS (ConfigNvData
->TargetName
, ISCSI_NAME_MAX_SIZE
, Field
->Str
);
242 EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol
243 instance to intercept events that occurs in the DHCPv6 Information Request
246 @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that
247 is used to configure this callback function.
248 @param[in] Context Pointer to the context that is initialized in
249 the EFI_DHCP6_PROTOCOL.InfoRequest().
250 @param[in] Packet Pointer to Reply packet that has been received.
251 The EFI DHCPv6 Protocol instance is responsible
252 for freeing the buffer.
254 @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish
255 Information Request exchange process.
256 @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue
257 Information Request exchange process.
258 @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort
259 the Information Request exchange process.
260 @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish
261 the Information Request exchange process because some
262 request information are not received.
267 IScsiDhcp6ParseReply (
268 IN EFI_DHCP6_PROTOCOL
*This
,
270 IN EFI_DHCP6_PACKET
*Packet
276 EFI_DHCP6_PACKET_OPTION
*BootFileOpt
;
277 EFI_DHCP6_PACKET_OPTION
**OptionList
;
278 ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
;
284 Status
= This
->Parse (This
, Packet
, &OptionCount
, NULL
);
285 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
286 return EFI_NOT_READY
;
289 OptionList
= AllocateZeroPool (OptionCount
* sizeof (EFI_DHCP6_PACKET_OPTION
*));
290 if (OptionList
== NULL
) {
291 return EFI_NOT_READY
;
294 Status
= This
->Parse (This
, Packet
, &OptionCount
, OptionList
);
295 if (EFI_ERROR (Status
)) {
296 Status
= EFI_NOT_READY
;
300 ConfigData
= (ISCSI_ATTEMPT_CONFIG_NVDATA
*)Context
;
302 for (Index
= 0; Index
< OptionCount
; Index
++) {
303 OptionList
[Index
]->OpCode
= NTOHS (OptionList
[Index
]->OpCode
);
304 OptionList
[Index
]->OpLen
= NTOHS (OptionList
[Index
]->OpLen
);
307 // Get DNS server addresses from this reply packet.
309 if (OptionList
[Index
]->OpCode
== DHCP6_OPT_DNS_SERVERS
) {
310 if (((OptionList
[Index
]->OpLen
& 0xf) != 0) || (OptionList
[Index
]->OpLen
== 0)) {
311 Status
= EFI_UNSUPPORTED
;
316 // Primary DNS server address.
318 CopyMem (&ConfigData
->PrimaryDns
, &OptionList
[Index
]->Data
[0], sizeof (EFI_IPv6_ADDRESS
));
320 if (OptionList
[Index
]->OpLen
> 16) {
322 // Secondary DNS server address
324 CopyMem (&ConfigData
->SecondaryDns
, &OptionList
[Index
]->Data
[16], sizeof (EFI_IPv6_ADDRESS
));
326 } else if (OptionList
[Index
]->OpCode
== DHCP6_OPT_BOOT_FILE_URL
) {
328 // The server sends this option to inform the client about an URL to a boot file.
330 BootFileOpt
= OptionList
[Index
];
331 } else if (OptionList
[Index
]->OpCode
== DHCP6_OPT_BOOT_FILE_PARAM
) {
333 // The server sends this option to inform the client about DHCP6 server address.
335 if (OptionList
[Index
]->OpLen
< 18) {
336 Status
= EFI_UNSUPPORTED
;
341 // Check param-len 1, should be 16 bytes.
343 CopyMem (&ParaLen
, &OptionList
[Index
]->Data
[0], sizeof (UINT16
));
344 if (NTOHS (ParaLen
) != 16) {
345 Status
= EFI_UNSUPPORTED
;
349 CopyMem (&ConfigData
->DhcpServer
, &OptionList
[Index
]->Data
[2], sizeof (EFI_IPv6_ADDRESS
));
353 if (BootFileOpt
== NULL
) {
354 Status
= EFI_UNSUPPORTED
;
359 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option
361 Status
= IScsiDhcp6ExtractRootPath (
362 (CHAR8
*)BootFileOpt
->Data
,
369 FreePool (OptionList
);
374 Parse the DHCP ACK to get the address configuration and DNS information.
376 @param[in] Image The handle of the driver image.
377 @param[in] Controller The handle of the controller;
378 @param[in, out] ConfigData The attempt configuration data.
380 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
381 @retval EFI_NO_MAPPING DHCP failed to acquire address and other
383 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
384 @retval EFI_DEVICE_ERROR Some unexpected error occurred.
385 @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the
387 @retval EFI_NO_MEDIA There was a media error.
393 IN EFI_HANDLE Controller
,
394 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
397 EFI_HANDLE Dhcp6Handle
;
398 EFI_DHCP6_PROTOCOL
*Dhcp6
;
400 EFI_STATUS TimerStatus
;
401 EFI_DHCP6_PACKET_OPTION
*Oro
;
402 EFI_DHCP6_RETRANSMISSION InfoReqReXmit
;
404 EFI_STATUS MediaStatus
;
407 // Check media status before doing DHCP.
409 MediaStatus
= EFI_SUCCESS
;
410 NetLibDetectMediaWaitTimeout (Controller
, ISCSI_CHECK_MEDIA_GET_DHCP_WAITING_TIME
, &MediaStatus
);
411 if (MediaStatus
!= EFI_SUCCESS
) {
412 AsciiPrint ("\n Error: Could not detect network connection.\n");
417 // iSCSI will only request target info from DHCPv6 server.
419 if (!ConfigData
->SessionConfigData
.TargetInfoFromDhcp
) {
429 // Create a DHCP6 child instance and get the protocol.
431 Status
= NetLibCreateServiceChild (
434 &gEfiDhcp6ServiceBindingProtocolGuid
,
437 if (EFI_ERROR (Status
)) {
441 Status
= gBS
->OpenProtocol (
443 &gEfiDhcp6ProtocolGuid
,
447 EFI_OPEN_PROTOCOL_BY_DRIVER
449 if (EFI_ERROR (Status
)) {
453 Oro
= AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION
) + 5);
455 Status
= EFI_OUT_OF_RESOURCES
;
460 // Ask the server to reply with DNS and Boot File URL options by info request.
461 // All members in EFI_DHCP6_PACKET_OPTION are in network order.
463 Oro
->OpCode
= HTONS (DHCP6_OPT_ORO
);
464 Oro
->OpLen
= HTONS (2 * 3);
465 Oro
->Data
[1] = DHCP6_OPT_DNS_SERVERS
;
466 Oro
->Data
[3] = DHCP6_OPT_BOOT_FILE_URL
;
467 Oro
->Data
[5] = DHCP6_OPT_BOOT_FILE_PARAM
;
469 InfoReqReXmit
.Irt
= 4;
470 InfoReqReXmit
.Mrc
= 1;
471 InfoReqReXmit
.Mrt
= 10;
472 InfoReqReXmit
.Mrd
= 30;
474 Status
= Dhcp6
->InfoRequest (
482 IScsiDhcp6ParseReply
,
485 if (Status
== EFI_NO_MAPPING
) {
486 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
487 if (EFI_ERROR (Status
)) {
491 Status
= gBS
->SetTimer (
494 ISCSI_GET_MAPPING_TIMEOUT
497 if (EFI_ERROR (Status
)) {
502 TimerStatus
= gBS
->CheckEvent (Timer
);
504 if (!EFI_ERROR (TimerStatus
)) {
505 Status
= Dhcp6
->InfoRequest (
513 IScsiDhcp6ParseReply
,
517 } while (TimerStatus
== EFI_NOT_READY
);
527 gBS
->CloseEvent (Timer
);
533 &gEfiDhcp6ProtocolGuid
,
539 NetLibDestroyServiceChild (
542 &gEfiDhcp6ServiceBindingProtocolGuid
,