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