2 iSCSI DHCP6 related configuration routines.
4 Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include "IScsiImpl.h"
19 Extract the Root Path option and get the required target information from
20 Boot File Uniform Resource Locator (URL) Option.
22 @param[in] RootPath The RootPath string.
23 @param[in] Length Length of the RootPath option payload.
24 @param[in, out] ConfigData The iSCSI session configuration data read from
27 @retval EFI_SUCCESS All required information is extracted from the
29 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
30 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
31 @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
35 IScsiDhcp6ExtractRootPath (
38 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
42 UINT16 IScsiRootPathIdLen
;
44 ISCSI_ROOT_PATH_FIELD Fields
[RP_FIELD_IDX_MAX
];
45 ISCSI_ROOT_PATH_FIELD
*Field
;
48 ISCSI_SESSION_CONFIG_NVDATA
*ConfigNvData
;
52 ConfigNvData
= &ConfigData
->SessionConfigData
;
55 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
57 IScsiRootPathIdLen
= (UINT16
) AsciiStrLen (ISCSI_ROOT_PATH_ID
);
59 if ((Length
<= IScsiRootPathIdLen
) ||
60 (CompareMem (RootPath
, ISCSI_ROOT_PATH_ID
, IScsiRootPathIdLen
) != 0)) {
64 // Skip the iSCSI RootPath ID "iscsi:".
66 RootPath
= RootPath
+ IScsiRootPathIdLen
;
67 Length
= (UINT16
) (Length
- IScsiRootPathIdLen
);
69 TmpStr
= (CHAR8
*) AllocatePool (Length
+ 1);
71 return EFI_OUT_OF_RESOURCES
;
74 CopyMem (TmpStr
, RootPath
, Length
);
75 TmpStr
[Length
] = '\0';
79 ZeroMem (&Fields
[0], sizeof (Fields
));
82 // Extract SERVERNAME field in the Root Path option.
84 if (TmpStr
[Index
] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER
) {
85 Status
= EFI_INVALID_PARAMETER
;
91 Fields
[RP_FIELD_IDX_SERVERNAME
].Str
= &TmpStr
[Index
];
93 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER
) && (Index
< Length
)) {
100 TmpStr
[Index
] = '\0';
103 Fields
[RP_FIELD_IDX_SERVERNAME
].Len
= (UINT8
) AsciiStrLen (Fields
[RP_FIELD_IDX_SERVERNAME
].Str
);
106 // Extract others fields in the Root Path option string.
108 for (FieldIndex
= 1; (FieldIndex
< RP_FIELD_IDX_MAX
) && (Index
< Length
); FieldIndex
++) {
110 if (TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) {
111 Fields
[FieldIndex
].Str
= &TmpStr
[Index
];
114 while ((TmpStr
[Index
] != ISCSI_ROOT_PATH_FIELD_DELIMITER
) && (Index
< Length
)) {
118 if (TmpStr
[Index
] == ISCSI_ROOT_PATH_FIELD_DELIMITER
) {
119 if (FieldIndex
!= RP_FIELD_IDX_TARGETNAME
) {
120 TmpStr
[Index
] = '\0';
124 if (Fields
[FieldIndex
].Str
!= NULL
) {
125 Fields
[FieldIndex
].Len
= (UINT8
) AsciiStrLen (Fields
[FieldIndex
].Str
);
130 if (FieldIndex
!= RP_FIELD_IDX_MAX
) {
131 Status
= EFI_INVALID_PARAMETER
;
135 if ((Fields
[RP_FIELD_IDX_SERVERNAME
].Str
== NULL
) ||
136 (Fields
[RP_FIELD_IDX_TARGETNAME
].Str
== NULL
) ||
137 (Fields
[RP_FIELD_IDX_PROTOCOL
].Len
> 1)
140 Status
= EFI_INVALID_PARAMETER
;
144 // Get the IP address of the target.
146 Field
= &Fields
[RP_FIELD_IDX_SERVERNAME
];
147 if (ConfigNvData
->IpMode
< IP_MODE_AUTOCONFIG
) {
148 IpMode
= ConfigNvData
->IpMode
;
150 IpMode
= ConfigData
->AutoConfigureMode
;
154 // Server name is expressed as domain name, just save it.
156 if ((!NET_IS_DIGIT (*(Field
->Str
))) && (*(Field
->Str
) != '[')) {
157 ConfigNvData
->DnsMode
= TRUE
;
158 if (Field
->Len
> sizeof (ConfigNvData
->TargetUrl
)) {
159 return EFI_INVALID_PARAMETER
;
161 CopyMem (&ConfigNvData
->TargetUrl
, Field
->Str
, Field
->Len
);
162 ConfigNvData
->TargetUrl
[Field
->Len
+ 1] = '\0';
164 ZeroMem(&ConfigNvData
->TargetUrl
, sizeof (ConfigNvData
->TargetUrl
));
165 Status
= IScsiAsciiStrToIp (Field
->Str
, IpMode
, &Ip
);
166 CopyMem (&ConfigNvData
->TargetIp
, &Ip
, sizeof (EFI_IP_ADDRESS
));
168 if (EFI_ERROR (Status
)) {
174 // Check the protocol type.
176 Field
= &Fields
[RP_FIELD_IDX_PROTOCOL
];
177 if ((Field
->Str
!= NULL
) && ((*(Field
->Str
) - '0') != EFI_IP_PROTO_TCP
)) {
178 Status
= EFI_INVALID_PARAMETER
;
182 // Get the port of the iSCSI target.
184 Field
= &Fields
[RP_FIELD_IDX_PORT
];
185 if (Field
->Str
!= NULL
) {
186 ConfigNvData
->TargetPort
= (UINT16
) AsciiStrDecimalToUintn (Field
->Str
);
188 ConfigNvData
->TargetPort
= ISCSI_WELL_KNOWN_PORT
;
193 Field
= &Fields
[RP_FIELD_IDX_LUN
];
194 if (Field
->Str
!= NULL
) {
195 Status
= IScsiAsciiStrToLun (Field
->Str
, ConfigNvData
->BootLun
);
196 if (EFI_ERROR (Status
)) {
200 ZeroMem (ConfigNvData
->BootLun
, sizeof (ConfigNvData
->BootLun
));
203 // Get the target iSCSI Name.
205 Field
= &Fields
[RP_FIELD_IDX_TARGETNAME
];
207 if (AsciiStrLen (Field
->Str
) > ISCSI_NAME_MAX_SIZE
- 1) {
208 Status
= EFI_INVALID_PARAMETER
;
212 // Validate the iSCSI name.
214 Status
= IScsiNormalizeName (Field
->Str
, AsciiStrLen (Field
->Str
));
215 if (EFI_ERROR (Status
)) {
219 AsciiStrCpyS (ConfigNvData
->TargetName
, ISCSI_NAME_MAX_SIZE
, Field
->Str
);
229 EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol
230 instance to intercept events that occurs in the DHCPv6 Information Request
233 @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that
234 is used to configure this callback function.
235 @param[in] Context Pointer to the context that is initialized in
236 the EFI_DHCP6_PROTOCOL.InfoRequest().
237 @param[in] Packet Pointer to Reply packet that has been received.
238 The EFI DHCPv6 Protocol instance is responsible
239 for freeing the buffer.
241 @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish
242 Information Request exchange process.
243 @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue
244 Information Request exchange process.
245 @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort
246 the Information Request exchange process.
247 @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish
248 the Information Request exchange process because some
249 request information are not received.
254 IScsiDhcp6ParseReply (
255 IN EFI_DHCP6_PROTOCOL
*This
,
257 IN EFI_DHCP6_PACKET
*Packet
263 EFI_DHCP6_PACKET_OPTION
*BootFileOpt
;
264 EFI_DHCP6_PACKET_OPTION
**OptionList
;
265 ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
;
271 Status
= This
->Parse (This
, Packet
, &OptionCount
, NULL
);
272 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
273 return EFI_NOT_READY
;
276 OptionList
= AllocateZeroPool (OptionCount
* sizeof (EFI_DHCP6_PACKET_OPTION
*));
277 if (OptionList
== NULL
) {
278 return EFI_NOT_READY
;
281 Status
= This
->Parse (This
, Packet
, &OptionCount
, OptionList
);
282 if (EFI_ERROR (Status
)) {
283 Status
= EFI_NOT_READY
;
287 ConfigData
= (ISCSI_ATTEMPT_CONFIG_NVDATA
*) Context
;
289 for (Index
= 0; Index
< OptionCount
; Index
++) {
290 OptionList
[Index
]->OpCode
= NTOHS (OptionList
[Index
]->OpCode
);
291 OptionList
[Index
]->OpLen
= NTOHS (OptionList
[Index
]->OpLen
);
294 // Get DNS server addresses from this reply packet.
296 if (OptionList
[Index
]->OpCode
== DHCP6_OPT_DNS_SERVERS
) {
298 if (((OptionList
[Index
]->OpLen
& 0xf) != 0) || (OptionList
[Index
]->OpLen
== 0)) {
299 Status
= EFI_UNSUPPORTED
;
303 // Primary DNS server address.
305 CopyMem (&ConfigData
->PrimaryDns
, &OptionList
[Index
]->Data
[0], sizeof (EFI_IPv6_ADDRESS
));
307 if (OptionList
[Index
]->OpLen
> 16) {
309 // Secondary DNS server address
311 CopyMem (&ConfigData
->SecondaryDns
, &OptionList
[Index
]->Data
[16], sizeof (EFI_IPv6_ADDRESS
));
314 } else if (OptionList
[Index
]->OpCode
== DHCP6_OPT_BOOT_FILE_URL
) {
316 // The server sends this option to inform the client about an URL to a boot file.
318 BootFileOpt
= OptionList
[Index
];
319 } else if (OptionList
[Index
]->OpCode
== DHCP6_OPT_BOOT_FILE_PARAM
) {
321 // The server sends this option to inform the client about DHCP6 server address.
323 if (OptionList
[Index
]->OpLen
< 18) {
324 Status
= EFI_UNSUPPORTED
;
328 // Check param-len 1, should be 16 bytes.
330 CopyMem (&ParaLen
, &OptionList
[Index
]->Data
[0], sizeof (UINT16
));
331 if (NTOHS (ParaLen
) != 16) {
332 Status
= EFI_UNSUPPORTED
;
336 CopyMem (&ConfigData
->DhcpServer
, &OptionList
[Index
]->Data
[2], sizeof (EFI_IPv6_ADDRESS
));
340 if (BootFileOpt
== NULL
) {
341 Status
= EFI_UNSUPPORTED
;
346 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option
348 Status
= IScsiDhcp6ExtractRootPath (
349 (CHAR8
*) BootFileOpt
->Data
,
356 FreePool (OptionList
);
362 Parse the DHCP ACK to get the address configuration and DNS information.
364 @param[in] Image The handle of the driver image.
365 @param[in] Controller The handle of the controller;
366 @param[in, out] ConfigData The attempt configuration data.
368 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
369 @retval EFI_NO_MAPPING DHCP failed to acquire address and other
371 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
372 @retval EFI_DEVICE_ERROR Some unexpected error occurred.
373 @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the
375 @retval EFI_NO_MEDIA There was a media error.
381 IN EFI_HANDLE Controller
,
382 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA
*ConfigData
385 EFI_HANDLE Dhcp6Handle
;
386 EFI_DHCP6_PROTOCOL
*Dhcp6
;
388 EFI_STATUS TimerStatus
;
389 EFI_DHCP6_PACKET_OPTION
*Oro
;
390 EFI_DHCP6_RETRANSMISSION InfoReqReXmit
;
392 BOOLEAN MediaPresent
;
395 // Check media status before doing DHCP.
398 NetLibDetectMedia (Controller
, &MediaPresent
);
404 // iSCSI will only request target info from DHCPv6 server.
406 if (!ConfigData
->SessionConfigData
.TargetInfoFromDhcp
) {
416 // Create a DHCP6 child instance and get the protocol.
418 Status
= NetLibCreateServiceChild (
421 &gEfiDhcp6ServiceBindingProtocolGuid
,
424 if (EFI_ERROR (Status
)) {
428 Status
= gBS
->OpenProtocol (
430 &gEfiDhcp6ProtocolGuid
,
434 EFI_OPEN_PROTOCOL_BY_DRIVER
436 if (EFI_ERROR (Status
)) {
440 Oro
= AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION
) + 5);
442 Status
= EFI_OUT_OF_RESOURCES
;
447 // Ask the server to reply with DNS and Boot File URL options by info request.
448 // All members in EFI_DHCP6_PACKET_OPTION are in network order.
450 Oro
->OpCode
= HTONS (DHCP6_OPT_ORO
);
451 Oro
->OpLen
= HTONS (2 * 3);
452 Oro
->Data
[1] = DHCP6_OPT_DNS_SERVERS
;
453 Oro
->Data
[3] = DHCP6_OPT_BOOT_FILE_URL
;
454 Oro
->Data
[5] = DHCP6_OPT_BOOT_FILE_PARAM
;
456 InfoReqReXmit
.Irt
= 4;
457 InfoReqReXmit
.Mrc
= 1;
458 InfoReqReXmit
.Mrt
= 10;
459 InfoReqReXmit
.Mrd
= 30;
461 Status
= Dhcp6
->InfoRequest (
469 IScsiDhcp6ParseReply
,
472 if (Status
== EFI_NO_MAPPING
) {
473 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
474 if (EFI_ERROR (Status
)) {
478 Status
= gBS
->SetTimer (
481 ISCSI_GET_MAPPING_TIMEOUT
484 if (EFI_ERROR (Status
)) {
490 TimerStatus
= gBS
->CheckEvent (Timer
);
492 if (!EFI_ERROR (TimerStatus
)) {
493 Status
= Dhcp6
->InfoRequest (
501 IScsiDhcp6ParseReply
,
506 } while (TimerStatus
== EFI_NOT_READY
);
517 gBS
->CloseEvent (Timer
);
523 &gEfiDhcp6ProtocolGuid
,
529 NetLibDestroyServiceChild (
532 &gEfiDhcp6ServiceBindingProtocolGuid
,