1 /** @file 2 iSCSI DHCP related configuration routines. 3 4 Copyright (c) 2004 - 2015, 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 9 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. 12 13 **/ 14 15 #include "IScsiImpl.h" 16 17 /** 18 Extract the Root Path option and get the required target information. 19 20 @param[in] RootPath The RootPath. 21 @param[in] Length Length of the RootPath option payload. 22 @param[in, out] ConfigNvData The iSCSI session configuration data read from nonvolatile device. 23 24 @retval EFI_SUCCESS All required information is extracted from the RootPath option. 25 @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath. 26 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. 27 @retval EFI_INVALID_PARAMETER The RootPath is mal-formatted. 28 **/ 29 EFI_STATUS 30 IScsiDhcpExtractRootPath ( 31 IN CHAR8 *RootPath, 32 IN UINT8 Length, 33 IN OUT ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData 34 ) 35 { 36 EFI_STATUS Status; 37 UINT8 IScsiRootPathIdLen; 38 CHAR8 *TmpStr; 39 ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX]; 40 ISCSI_ROOT_PATH_FIELD *Field; 41 UINT32 FieldIndex; 42 UINT8 Index; 43 44 // 45 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname> 46 // 47 IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID); 48 49 if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) { 50 return EFI_NOT_FOUND; 51 } 52 // 53 // Skip the iSCSI RootPath ID "iscsi:". 54 // 55 RootPath += IScsiRootPathIdLen; 56 Length = (UINT8) (Length - IScsiRootPathIdLen); 57 58 TmpStr = (CHAR8 *) AllocatePool (Length + 1); 59 if (TmpStr == NULL) { 60 return EFI_OUT_OF_RESOURCES; 61 } 62 63 CopyMem (TmpStr, RootPath, Length); 64 TmpStr[Length] = '\0'; 65 66 Index = 0; 67 FieldIndex = RP_FIELD_IDX_SERVERNAME; 68 ZeroMem (&Fields[0], sizeof (Fields)); 69 70 // 71 // Extract the fields in the Root Path option string. 72 // 73 for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) { 74 if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) { 75 Fields[FieldIndex].Str = &TmpStr[Index]; 76 } 77 78 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) { 79 Index++; 80 } 81 82 if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) { 83 if (FieldIndex != RP_FIELD_IDX_TARGETNAME) { 84 TmpStr[Index] = '\0'; 85 Index++; 86 } 87 88 if (Fields[FieldIndex].Str != NULL) { 89 Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str); 90 } 91 } 92 } 93 94 if (FieldIndex != RP_FIELD_IDX_MAX) { 95 Status = EFI_INVALID_PARAMETER; 96 goto ON_EXIT; 97 } 98 99 if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) || 100 (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) || 101 (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1) 102 ) { 103 104 Status = EFI_INVALID_PARAMETER; 105 goto ON_EXIT; 106 } 107 // 108 // Get the IP address of the target. 109 // 110 Field = &Fields[RP_FIELD_IDX_SERVERNAME]; 111 Status = IScsiAsciiStrToIp (Field->Str, &ConfigNvData->TargetIp); 112 if (EFI_ERROR (Status)) { 113 goto ON_EXIT; 114 } 115 // 116 // Check the protocol type. 117 // 118 Field = &Fields[RP_FIELD_IDX_PROTOCOL]; 119 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) { 120 Status = EFI_INVALID_PARAMETER; 121 goto ON_EXIT; 122 } 123 // 124 // Get the port of the iSCSI target. 125 // 126 Field = &Fields[RP_FIELD_IDX_PORT]; 127 if (Field->Str != NULL) { 128 ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str); 129 } else { 130 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT; 131 } 132 // 133 // Get the LUN. 134 // 135 Field = &Fields[RP_FIELD_IDX_LUN]; 136 if (Field->Str != NULL) { 137 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun); 138 if (EFI_ERROR (Status)) { 139 goto ON_EXIT; 140 } 141 } else { 142 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun)); 143 } 144 // 145 // Get the target iSCSI Name. 146 // 147 Field = &Fields[RP_FIELD_IDX_TARGETNAME]; 148 149 if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) { 150 Status = EFI_INVALID_PARAMETER; 151 goto ON_EXIT; 152 } 153 // 154 // Validate the iSCSI name. 155 // 156 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str)); 157 if (EFI_ERROR (Status)) { 158 goto ON_EXIT; 159 } 160 161 AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str); 162 163 ON_EXIT: 164 165 FreePool (TmpStr); 166 167 return Status; 168 } 169 170 /** 171 The callback function registerd to the DHCP4 instance which is used to select 172 the qualified DHCP OFFER. 173 174 @param[in] This The DHCP4 protocol. 175 @param[in] Context The context set when configuring the DHCP4 protocol. 176 @param[in] CurrentState The current state of the DHCP4 protocol. 177 @param[in] Dhcp4Event The event occurs in the current state. 178 @param[in] Packet The DHCP packet that is to be sent or already received. 179 @param[out] NewPacket The packet used to replace the above Packet. 180 181 @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted 182 in the Dhcp4Event. 183 @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements. 184 @retval Others Other errors as indicated. 185 **/ 186 EFI_STATUS 187 EFIAPI 188 IScsiDhcpSelectOffer ( 189 IN EFI_DHCP4_PROTOCOL * This, 190 IN VOID *Context, 191 IN EFI_DHCP4_STATE CurrentState, 192 IN EFI_DHCP4_EVENT Dhcp4Event, 193 IN EFI_DHCP4_PACKET * Packet, OPTIONAL 194 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL 195 ) 196 { 197 EFI_STATUS Status; 198 UINT32 OptionCount; 199 EFI_DHCP4_PACKET_OPTION **OptionList; 200 UINT32 Index; 201 202 if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) { 203 return EFI_SUCCESS; 204 } 205 206 OptionCount = 0; 207 208 Status = This->Parse (This, Packet, &OptionCount, NULL); 209 if (Status != EFI_BUFFER_TOO_SMALL) { 210 return EFI_NOT_READY; 211 } 212 213 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *)); 214 if (OptionList == NULL) { 215 return EFI_NOT_READY; 216 } 217 218 Status = This->Parse (This, Packet, &OptionCount, OptionList); 219 if (EFI_ERROR (Status)) { 220 FreePool (OptionList); 221 return EFI_NOT_READY; 222 } 223 224 for (Index = 0; Index < OptionCount; Index++) { 225 if (OptionList[Index]->OpCode != DHCP4_TAG_ROOT_PATH) { 226 continue; 227 } 228 229 Status = IScsiDhcpExtractRootPath ( 230 (CHAR8 *) &OptionList[Index]->Data[0], 231 OptionList[Index]->Length, 232 (ISCSI_SESSION_CONFIG_NVDATA *) Context 233 ); 234 235 break; 236 } 237 238 if (Index == OptionCount) { 239 Status = EFI_NOT_READY; 240 } 241 242 FreePool (OptionList); 243 244 return Status; 245 } 246 247 /** 248 Parse the DHCP ACK to get the address configuration and DNS information. 249 250 @param[in] Dhcp4 The DHCP4 protocol. 251 @param[in, out] ConfigData The session configuration data. 252 253 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. 254 @retval EFI_NO_MAPPING DHCP failed to acquire address and other information. 255 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is mal-formatted. 256 @retval EFI_DEVICE_ERROR Other errors as indicated. 257 **/ 258 EFI_STATUS 259 IScsiParseDhcpAck ( 260 IN EFI_DHCP4_PROTOCOL *Dhcp4, 261 IN OUT ISCSI_SESSION_CONFIG_DATA *ConfigData 262 ) 263 { 264 EFI_STATUS Status; 265 EFI_DHCP4_MODE_DATA Dhcp4ModeData; 266 UINT32 OptionCount; 267 EFI_DHCP4_PACKET_OPTION **OptionList; 268 UINT32 Index; 269 270 Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData); 271 if (EFI_ERROR (Status)) { 272 return Status; 273 } 274 275 if (Dhcp4ModeData.State != Dhcp4Bound) { 276 return EFI_NO_MAPPING; 277 } 278 279 CopyMem (&ConfigData->NvData.LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); 280 CopyMem (&ConfigData->NvData.SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); 281 CopyMem (&ConfigData->NvData.Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); 282 283 OptionCount = 0; 284 OptionList = NULL; 285 286 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList); 287 if (Status != EFI_BUFFER_TOO_SMALL) { 288 return EFI_DEVICE_ERROR; 289 } 290 291 OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *)); 292 if (OptionList == NULL) { 293 return EFI_OUT_OF_RESOURCES; 294 } 295 296 Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList); 297 if (EFI_ERROR (Status)) { 298 FreePool (OptionList); 299 return EFI_DEVICE_ERROR; 300 } 301 302 for (Index = 0; Index < OptionCount; Index++) { 303 // 304 // Get DNS server addresses and DHCP server address from this offer. 305 // 306 if (OptionList[Index]->OpCode == DHCP4_TAG_DNS) { 307 308 if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) { 309 Status = EFI_INVALID_PARAMETER; 310 break; 311 } 312 // 313 // Primary DNS server address. 314 // 315 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS)); 316 317 if (OptionList[Index]->Length > 4) { 318 // 319 // Secondary DNS server address 320 // 321 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS)); 322 } 323 } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) { 324 if (OptionList[Index]->Length != 4) { 325 Status = EFI_INVALID_PARAMETER; 326 break; 327 } 328 329 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS)); 330 } 331 } 332 333 FreePool (OptionList); 334 335 return Status; 336 } 337 338 /** 339 Parse the DHCP ACK to get the address configuration and DNS information. 340 341 @param[in] Image The handle of the driver image. 342 @param[in] Controller The handle of the controller; 343 @param[in, out] ConfigData The session configuration data. 344 345 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. 346 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. 347 @retval EFI_NO_MEDIA There was a media error. 348 @retval Others Other errors as indicated. 349 350 **/ 351 EFI_STATUS 352 IScsiDoDhcp ( 353 IN EFI_HANDLE Image, 354 IN EFI_HANDLE Controller, 355 IN OUT ISCSI_SESSION_CONFIG_DATA *ConfigData 356 ) 357 { 358 EFI_HANDLE Dhcp4Handle; 359 EFI_DHCP4_PROTOCOL *Dhcp4; 360 EFI_STATUS Status; 361 EFI_DHCP4_PACKET_OPTION *ParaList; 362 EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData; 363 BOOLEAN MediaPresent; 364 UINT8 *Data; 365 366 Dhcp4Handle = NULL; 367 Dhcp4 = NULL; 368 ParaList = NULL; 369 370 // 371 // Check media status before do DHCP 372 // 373 MediaPresent = TRUE; 374 NetLibDetectMedia (Controller, &MediaPresent); 375 if (!MediaPresent) { 376 return EFI_NO_MEDIA; 377 } 378 379 // 380 // Create a DHCP4 child instance and get the protocol. 381 // 382 Status = NetLibCreateServiceChild ( 383 Controller, 384 Image, 385 &gEfiDhcp4ServiceBindingProtocolGuid, 386 &Dhcp4Handle 387 ); 388 if (EFI_ERROR (Status)) { 389 return Status; 390 } 391 392 Status = gBS->OpenProtocol ( 393 Dhcp4Handle, 394 &gEfiDhcp4ProtocolGuid, 395 (VOID **)&Dhcp4, 396 Image, 397 Controller, 398 EFI_OPEN_PROTOCOL_BY_DRIVER 399 ); 400 if (EFI_ERROR (Status)) { 401 goto ON_EXIT; 402 } 403 404 ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3); 405 if (ParaList == NULL) { 406 Status = EFI_OUT_OF_RESOURCES; 407 goto ON_EXIT; 408 } 409 // 410 // Ask the server to reply with Netmask, Router, DNS and RootPath options. 411 // 412 ParaList->OpCode = DHCP4_TAG_PARA_LIST; 413 ParaList->Length = (UINT8) (ConfigData->NvData.TargetInfoFromDhcp ? 4 : 3); 414 Data = &ParaList->Data[0]; 415 Data[0] = DHCP4_TAG_NETMASK; 416 Data[1] = DHCP4_TAG_ROUTER; 417 Data[2] = DHCP4_TAG_DNS; 418 Data[3] = DHCP4_TAG_ROOT_PATH; 419 420 ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA)); 421 Dhcp4ConfigData.OptionCount = 1; 422 Dhcp4ConfigData.OptionList = &ParaList; 423 424 if (ConfigData->NvData.TargetInfoFromDhcp) { 425 // 426 // Use callback to select an offer which contains target information. 427 // 428 Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer; 429 Dhcp4ConfigData.CallbackContext = &ConfigData->NvData; 430 } 431 432 Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData); 433 if (EFI_ERROR (Status)) { 434 goto ON_EXIT; 435 } 436 437 Status = Dhcp4->Start (Dhcp4, NULL); 438 if (EFI_ERROR (Status)) { 439 goto ON_EXIT; 440 } 441 // 442 // Parse the ACK to get required information. 443 // 444 Status = IScsiParseDhcpAck (Dhcp4, ConfigData); 445 446 ON_EXIT: 447 448 if (ParaList != NULL) { 449 FreePool (ParaList); 450 } 451 452 if (Dhcp4 != NULL) { 453 Dhcp4->Stop (Dhcp4); 454 Dhcp4->Configure (Dhcp4, NULL); 455 456 gBS->CloseProtocol ( 457 Dhcp4Handle, 458 &gEfiDhcp4ProtocolGuid, 459 Image, 460 Controller 461 ); 462 } 463 464 NetLibDestroyServiceChild ( 465 Controller, 466 Image, 467 &gEfiDhcp4ServiceBindingProtocolGuid, 468 Dhcp4Handle 469 ); 470 471 return Status; 472 } 473