1 /** @file 2 iSCSI DHCP6 related configuration routines. 3 4 Copyright (c) 2009 - 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 from 20 Boot File Uniform Resource Locator (URL) Option. 21 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 25 nonvolatile device. 26 27 @retval EFI_SUCCESS All required information is extracted from the 28 RootPath option. 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. 32 33 **/ 34 EFI_STATUS 35 IScsiDhcp6ExtractRootPath ( 36 IN CHAR8 *RootPath, 37 IN UINT16 Length, 38 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData 39 ) 40 { 41 EFI_STATUS Status; 42 UINT16 IScsiRootPathIdLen; 43 CHAR8 *TmpStr; 44 ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX]; 45 ISCSI_ROOT_PATH_FIELD *Field; 46 UINT32 FieldIndex; 47 UINT8 Index; 48 ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData; 49 EFI_IP_ADDRESS Ip; 50 UINT8 IpMode; 51 52 ConfigNvData = &ConfigData->SessionConfigData; 53 54 // 55 // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname> 56 // 57 IScsiRootPathIdLen = (UINT16) AsciiStrLen (ISCSI_ROOT_PATH_ID); 58 59 if ((Length <= IScsiRootPathIdLen) || 60 (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) { 61 return EFI_NOT_FOUND; 62 } 63 // 64 // Skip the iSCSI RootPath ID "iscsi:". 65 // 66 RootPath = RootPath + IScsiRootPathIdLen; 67 Length = (UINT16) (Length - IScsiRootPathIdLen); 68 69 TmpStr = (CHAR8 *) AllocatePool (Length + 1); 70 if (TmpStr == NULL) { 71 return EFI_OUT_OF_RESOURCES; 72 } 73 74 CopyMem (TmpStr, RootPath, Length); 75 TmpStr[Length] = '\0'; 76 77 Index = 0; 78 FieldIndex = 0; 79 ZeroMem (&Fields[0], sizeof (Fields)); 80 81 // 82 // Extract SERVERNAME field in the Root Path option. 83 // 84 if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) { 85 Status = EFI_INVALID_PARAMETER; 86 goto ON_EXIT; 87 } else { 88 Index++; 89 } 90 91 Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index]; 92 93 while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER) && (Index < Length)) { 94 Index++; 95 } 96 97 // 98 // Skip ']' and ':'. 99 // 100 TmpStr[Index] = '\0'; 101 Index += 2; 102 103 Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8) AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str); 104 105 // 106 // Extract others fields in the Root Path option string. 107 // 108 for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) { 109 110 if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) { 111 Fields[FieldIndex].Str = &TmpStr[Index]; 112 } 113 114 while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) { 115 Index++; 116 } 117 118 if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) { 119 if (FieldIndex != RP_FIELD_IDX_TARGETNAME) { 120 TmpStr[Index] = '\0'; 121 Index++; 122 } 123 124 if (Fields[FieldIndex].Str != NULL) { 125 Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str); 126 } 127 } 128 } 129 130 if (FieldIndex != RP_FIELD_IDX_MAX) { 131 Status = EFI_INVALID_PARAMETER; 132 goto ON_EXIT; 133 } 134 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) 138 ) { 139 140 Status = EFI_INVALID_PARAMETER; 141 goto ON_EXIT; 142 } 143 // 144 // Get the IP address of the target. 145 // 146 Field = &Fields[RP_FIELD_IDX_SERVERNAME]; 147 if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) { 148 IpMode = ConfigNvData->IpMode; 149 } else { 150 IpMode = ConfigData->AutoConfigureMode; 151 } 152 153 Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip); 154 CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS)); 155 156 157 if (EFI_ERROR (Status)) { 158 goto ON_EXIT; 159 } 160 // 161 // Check the protocol type. 162 // 163 Field = &Fields[RP_FIELD_IDX_PROTOCOL]; 164 if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) { 165 Status = EFI_INVALID_PARAMETER; 166 goto ON_EXIT; 167 } 168 // 169 // Get the port of the iSCSI target. 170 // 171 Field = &Fields[RP_FIELD_IDX_PORT]; 172 if (Field->Str != NULL) { 173 ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str); 174 } else { 175 ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT; 176 } 177 // 178 // Get the LUN. 179 // 180 Field = &Fields[RP_FIELD_IDX_LUN]; 181 if (Field->Str != NULL) { 182 Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun); 183 if (EFI_ERROR (Status)) { 184 goto ON_EXIT; 185 } 186 } else { 187 ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun)); 188 } 189 // 190 // Get the target iSCSI Name. 191 // 192 Field = &Fields[RP_FIELD_IDX_TARGETNAME]; 193 194 if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) { 195 Status = EFI_INVALID_PARAMETER; 196 goto ON_EXIT; 197 } 198 // 199 // Validate the iSCSI name. 200 // 201 Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str)); 202 if (EFI_ERROR (Status)) { 203 goto ON_EXIT; 204 } 205 206 AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str); 207 208 ON_EXIT: 209 210 FreePool (TmpStr); 211 212 return Status; 213 } 214 215 /** 216 EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol 217 instance to intercept events that occurs in the DHCPv6 Information Request 218 exchange process. 219 220 @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that 221 is used to configure this callback function. 222 @param[in] Context Pointer to the context that is initialized in 223 the EFI_DHCP6_PROTOCOL.InfoRequest(). 224 @param[in] Packet Pointer to Reply packet that has been received. 225 The EFI DHCPv6 Protocol instance is responsible 226 for freeing the buffer. 227 228 @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish 229 Information Request exchange process. 230 @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue 231 Information Request exchange process. 232 @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort 233 the Information Request exchange process. 234 @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish 235 the Information Request exchange process because some 236 request information are not received. 237 238 **/ 239 EFI_STATUS 240 EFIAPI 241 IScsiDhcp6ParseReply ( 242 IN EFI_DHCP6_PROTOCOL *This, 243 IN VOID *Context, 244 IN EFI_DHCP6_PACKET *Packet 245 ) 246 { 247 EFI_STATUS Status; 248 UINT32 Index; 249 UINT32 OptionCount; 250 EFI_DHCP6_PACKET_OPTION *BootFileOpt; 251 EFI_DHCP6_PACKET_OPTION **OptionList; 252 ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData; 253 UINT16 ParaLen; 254 255 OptionCount = 0; 256 BootFileOpt = NULL; 257 258 Status = This->Parse (This, Packet, &OptionCount, NULL); 259 if (Status != EFI_BUFFER_TOO_SMALL) { 260 return EFI_NOT_READY; 261 } 262 263 OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *)); 264 if (OptionList == NULL) { 265 return EFI_NOT_READY; 266 } 267 268 Status = This->Parse (This, Packet, &OptionCount, OptionList); 269 if (EFI_ERROR (Status)) { 270 Status = EFI_NOT_READY; 271 goto Exit; 272 } 273 274 ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context; 275 276 for (Index = 0; Index < OptionCount; Index++) { 277 OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode); 278 OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen); 279 280 // 281 // Get DNS server addresses from this reply packet. 282 // 283 if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) { 284 285 if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) { 286 Status = EFI_UNSUPPORTED; 287 goto Exit; 288 } 289 // 290 // Primary DNS server address. 291 // 292 CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS)); 293 294 if (OptionList[Index]->OpLen > 16) { 295 // 296 // Secondary DNS server address 297 // 298 CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS)); 299 } 300 301 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) { 302 // 303 // The server sends this option to inform the client about an URL to a boot file. 304 // 305 BootFileOpt = OptionList[Index]; 306 } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_PARAM) { 307 // 308 // The server sends this option to inform the client about DHCP6 server address. 309 // 310 if (OptionList[Index]->OpLen < 18) { 311 Status = EFI_UNSUPPORTED; 312 goto Exit; 313 } 314 // 315 // Check param-len 1, should be 16 bytes. 316 // 317 CopyMem (&ParaLen, &OptionList[Index]->Data[0], sizeof (UINT16)); 318 if (NTOHS (ParaLen) != 16) { 319 Status = EFI_UNSUPPORTED; 320 goto Exit; 321 } 322 323 CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[2], sizeof (EFI_IPv6_ADDRESS)); 324 } 325 } 326 327 if (BootFileOpt == NULL) { 328 Status = EFI_UNSUPPORTED; 329 goto Exit; 330 } 331 332 // 333 // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option 334 // 335 Status = IScsiDhcp6ExtractRootPath ( 336 (CHAR8 *) BootFileOpt->Data, 337 BootFileOpt->OpLen, 338 ConfigData 339 ); 340 341 Exit: 342 343 FreePool (OptionList); 344 return Status; 345 } 346 347 348 /** 349 Parse the DHCP ACK to get the address configuration and DNS information. 350 351 @param[in] Image The handle of the driver image. 352 @param[in] Controller The handle of the controller; 353 @param[in, out] ConfigData The attempt configuration data. 354 355 @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. 356 @retval EFI_NO_MAPPING DHCP failed to acquire address and other 357 information. 358 @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted. 359 @retval EFI_DEVICE_ERROR Some unexpected error occurred. 360 @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the 361 operation. 362 @retval EFI_NO_MEDIA There was a media error. 363 364 **/ 365 EFI_STATUS 366 IScsiDoDhcp6 ( 367 IN EFI_HANDLE Image, 368 IN EFI_HANDLE Controller, 369 IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData 370 ) 371 { 372 EFI_HANDLE Dhcp6Handle; 373 EFI_DHCP6_PROTOCOL *Dhcp6; 374 EFI_STATUS Status; 375 EFI_STATUS TimerStatus; 376 EFI_DHCP6_PACKET_OPTION *Oro; 377 EFI_DHCP6_RETRANSMISSION InfoReqReXmit; 378 EFI_EVENT Timer; 379 BOOLEAN MediaPresent; 380 381 // 382 // Check media status before doing DHCP. 383 // 384 MediaPresent = TRUE; 385 NetLibDetectMedia (Controller, &MediaPresent); 386 if (!MediaPresent) { 387 return EFI_NO_MEDIA; 388 } 389 390 // 391 // iSCSI will only request target info from DHCPv6 server. 392 // 393 if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) { 394 return EFI_SUCCESS; 395 } 396 397 Dhcp6Handle = NULL; 398 Dhcp6 = NULL; 399 Oro = NULL; 400 Timer = NULL; 401 402 // 403 // Create a DHCP6 child instance and get the protocol. 404 // 405 Status = NetLibCreateServiceChild ( 406 Controller, 407 Image, 408 &gEfiDhcp6ServiceBindingProtocolGuid, 409 &Dhcp6Handle 410 ); 411 if (EFI_ERROR (Status)) { 412 return Status; 413 } 414 415 Status = gBS->OpenProtocol ( 416 Dhcp6Handle, 417 &gEfiDhcp6ProtocolGuid, 418 (VOID **) &Dhcp6, 419 Image, 420 Controller, 421 EFI_OPEN_PROTOCOL_BY_DRIVER 422 ); 423 if (EFI_ERROR (Status)) { 424 goto ON_EXIT; 425 } 426 427 Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 5); 428 if (Oro == NULL) { 429 Status = EFI_OUT_OF_RESOURCES; 430 goto ON_EXIT; 431 } 432 433 // 434 // Ask the server to reply with DNS and Boot File URL options by info request. 435 // All members in EFI_DHCP6_PACKET_OPTION are in network order. 436 // 437 Oro->OpCode = HTONS (DHCP6_OPT_ORO); 438 Oro->OpLen = HTONS (2 * 3); 439 Oro->Data[1] = DHCP6_OPT_DNS_SERVERS; 440 Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL; 441 Oro->Data[5] = DHCP6_OPT_BOOT_FILE_PARAM; 442 443 InfoReqReXmit.Irt = 4; 444 InfoReqReXmit.Mrc = 1; 445 InfoReqReXmit.Mrt = 10; 446 InfoReqReXmit.Mrd = 30; 447 448 Status = Dhcp6->InfoRequest ( 449 Dhcp6, 450 TRUE, 451 Oro, 452 0, 453 NULL, 454 &InfoReqReXmit, 455 NULL, 456 IScsiDhcp6ParseReply, 457 ConfigData 458 ); 459 if (Status == EFI_NO_MAPPING) { 460 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); 461 if (EFI_ERROR (Status)) { 462 goto ON_EXIT; 463 } 464 465 Status = gBS->SetTimer ( 466 Timer, 467 TimerRelative, 468 ISCSI_GET_MAPPING_TIMEOUT 469 ); 470 471 if (EFI_ERROR (Status)) { 472 goto ON_EXIT; 473 } 474 475 do { 476 477 TimerStatus = gBS->CheckEvent (Timer); 478 479 if (!EFI_ERROR (TimerStatus)) { 480 Status = Dhcp6->InfoRequest ( 481 Dhcp6, 482 TRUE, 483 Oro, 484 0, 485 NULL, 486 &InfoReqReXmit, 487 NULL, 488 IScsiDhcp6ParseReply, 489 ConfigData 490 ); 491 } 492 493 } while (TimerStatus == EFI_NOT_READY); 494 495 } 496 497 ON_EXIT: 498 499 if (Oro != NULL) { 500 FreePool (Oro); 501 } 502 503 if (Timer != NULL) { 504 gBS->CloseEvent (Timer); 505 } 506 507 if (Dhcp6 != NULL) { 508 gBS->CloseProtocol ( 509 Dhcp6Handle, 510 &gEfiDhcp6ProtocolGuid, 511 Image, 512 Controller 513 ); 514 } 515 516 NetLibDestroyServiceChild ( 517 Controller, 518 Image, 519 &gEfiDhcp6ServiceBindingProtocolGuid, 520 Dhcp6Handle 521 ); 522 523 return Status; 524 } 525 526