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