Home | History | Annotate | Download | only in IScsiDxe
      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