Home | History | Annotate | Download | only in IScsiDxe
      1 /** @file
      2   iSCSI DHCP6 related configuration routines.
      3 
      4 Copyright (c) 2009 - 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 /**
     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_PARA) {
    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_REQUEST_OPTION);
    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_PARA;
    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