/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <libscf.h>
#include <wchar.h>
#include <widec.h>
#include <libintl.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
/* Non-public header files needed to work with the iSCSI driver. */
#include "suri_impl.h"
#include "suri_strings.h"
"iSCSI discovery addresses because logical unit is in use")
typedef struct suri_iscsi_disc_addr_props
{
static suri_err_t
{
SURIGTEXT("Could not open iSCSI control device"),
return (ESURI_ERR);
}
return (ESURI_OK);
}
/*
* We have only one iSCSI logical HBA in Solaris. Get it.
*/
static suri_err_t
{
if (!IMA_SUCCESS(status)) {
SURIGTEXT("Could not get list of iSCSI host adaptors"));
return (ESURI_ERR);
}
(void) IMA_FreeMemory(lhbaList);
SURIGTEXT("No iSCSI host adaptor present on the system"));
return (ESURI_NOENT);
}
(void) IMA_FreeMemory(lhbaList);
return (ESURI_OK);
}
/*
* Enable the send-targets discovery method if not already enabled.
*/
static suri_err_t
{
if (!IMA_SUCCESS(status)) {
SURIGTEXT("Could not get discovery method properties"));
return (ESURI_ERR);
}
/*
* IMA_SetSendTargetsDiscovery() can take some time to finish, do not
* enable the method unless we know it is disabled.
*/
if (!IMA_SUCCESS(status)) {
SURIGTEXT("Could not enable SendTargets discovery "
"method"));
return (ESURI_ERR);
}
}
return (ESURI_OK);
}
/*
* Based on resolved hostname:port provided in 'tgt', add discovery addresses.
* You should check whether all the discovery addresses already exist before
* using this function. See suri_tgt_disc_addrs_exist().
*/
{
return (ret);
return (ret);
/* Make lines more manageable. */
/*
* We add discovery addresses for all IP addresses our hostname
* resolved to.
*/
/*
* In an unlikely case we fail in inet_ntop() and still need a
* printable IP it means we failed in IMA_AddDiscoveryAddress()
* which is what is interesting to the caller. So, let's have
* something in the string if we fail to convert it.
*/
/* iSCSI works over IP only. */
continue;
}
/* LINTED alignment */
/* See a comment for str_ip above on why we do (void) */
sizeof (str_ip));
} else {
/* LINTED alignment */
/* See a comment for str_ip above on why we do (void) */
sizeof (str_ip));
}
if (suri_iscsi->si_prop_port == 0)
else
/*
* Adding an existing discovery address is not an issue and
* IMA_STATUS_SUCCESS is returned.
*/
&addressOid);
if (!IMA_SUCCESS(status)) {
SURIGTEXT("Could not add IP discovery address"),
str_ip);
return (ESURI_ERR);
}
}
/*
* We might have non-AF_INET families in tgt so if there is no
*/
if (disc_addr_added == B_FALSE) {
return (ESURI_ERR);
}
return (ESURI_OK);
}
/*
* Remove a discovery address.
*/
static suri_err_t
{
return (ret);
if (!IMA_SUCCESS(status)) {
if (status == IMA_ERROR_LU_IN_USE)
return (ESURI_LU_INUSE);
SURIGTEXT("Cannot remove iSCSI "
"discovery address"));
return (ESURI_ERR);
}
}
return (ESURI_OK);
}
/*
* Remove all discovery addresses based on a resolved hostname and port in the
* addrinfo structure.
*/
{
int i, ret;
return (ret);
/*
* We are quering discovery targets so it is expected that the method
* will be used. Enable it now if disabled.
*/
return (ret);
if (!IMA_SUCCESS(status)) {
"iSCSI discovery addresses"));
return (ESURI_ERR);
}
if (suri_iscsi->si_prop_port != 0)
/*
* Go through a list of existing discovery addresses and try to match
* each of those with one of the addresses that our hostname resolved
* into.
*/
&props);
if (!IMA_SUCCESS(status)) {
SURIGTEXT("Could not get iSCSI discovery address "
"properties"));
(void) IMA_FreeMemory(daddrs);
return (ESURI_ERR);
}
/*
* libima(3LIB) does not support hostnames in discovery
* addresses.
*/
continue;
}
continue;
/*
* Continue if the discovery address being
* processed is IPv4 but our resolved address is
* IPv6.
*/
continue;
/* LINTED alignment */
continue;
}
} else {
/*
* Continue if the discovery address being
* processed is IPv6 but our resolved address is
* IPv4.
*/
continue;
/* LINTED alignment */
continue;
}
}
/* Found a discovery address to be removed. */
/* Warn just once. */
/* Not considered an error */
}
break;
}
}
(void) IMA_FreeMemory(daddrs);
return (ret);
}
/*
* Return a string IP address converted from a binary representation stored in
* discovery address properties. A helper function needed for rich error
* reporting.
*/
static void
{
const char *p;
/*
* inet_ntop(3SOCKET) can only fail on an incorrect address family or a
* buffer of an inadequate size. It is an internal error if we hit any
* of those here.
*/
}
/*
* Allocate a buffer for SendTargets inquiry. 'ntgts' is the number of target
* elements memory should be allocated for. If the function fails '*hdr' is
* left intact.
*/
static suri_err_t
{
int stl_size;
stl_size = sizeof (iscsi_sendtgts_list_t) +
/* Set the header to the newly allocated memory and initialize. */
else
return (ESURI_OK);
}
/*
* Function issues a SendTargets request and returns a list of targets available
* on the iSCSI server. 'props' are properties for one specific discovery
* address which contains the IP address of the iSCSI server we are going to
* send the request to.
*/
static suri_err_t
{
return (ret);
/*
* Iterate until we can accomodate all the targets available. Each
* additional iteration means that the room allocated was not enough so
* we have to reallocate the memory for the target list and send another
* inquiry.
*/
for (;;) {
int err;
return (ret);
}
/*
* If the discovery address is not accessible or has other
* problems, we get EFAULT. It is not considered an error. We
* cannot control what discovery addresses are configured on the
* system. We return an empty output for this situation.
*/
stl_hdr->stl_out_cnt = 0;
break;
}
/* We return an error for other issues the driver run into. */
if (err != 0) {
/*
* We do not want get_str_ip_for_disc_addr_props() to
* change the errno value we got from ioctl(2) above.
*/
/* Let's have a printable IP address for the message */
sizeof (saddr));
SURIGTEXT("SendTargets request failed"),
return (ESURI_ERR);
}
/* If we got all the targets, we are done. */
break;
/* We need to increase our output buffer and try again */
}
/*
* Allocate return buffer. We allocate on more entry but we need it so
* that we still allocate a structure for an empty output.
*/
sizeof (suri_iscsi_disc_addr_props) +
}
for (i = 0; i < stl_hdr->stl_out_cnt; i++) {
}
return (IMA_STATUS_SUCCESS);
}
/*
* Return a list of discovery address OIDs.
*/
{
int ret;
return (ret);
if (!IMA_SUCCESS(status)) {
"discovery address list"));
return (ESURI_ERR);
}
return (ESURI_OK);
}
/*
* For a given target iqn, fill out a hostname and port properties of the handle
* if the target is present in the output of the SendTargets query for the
* discovery address 'daddr_oid'. If the target is not found, leave the hostname
* and port unset and return ESURI_OK. If the target is found but the IP address
* cannot be resolved, use a string representation of the IP address.
*/
{
int err, i;
const char *p;
int addr_family;
/* Fits both IPv4 and IPv6 address */
if (!IMA_SUCCESS(status)) {
/*
* Note we cannot be more specific and print which discovery
* address it failed for since the IP address is in the
* properties.
*/
SURIGTEXT("Could not get iSCSI discovery address "
"properties"));
return (ESURI_ERR);
}
return (ret);
/* Find our iSCSI target port name (or not) */
for (i = 0; i < pList->name_count; ++i) {
break;
}
/* Will have an empty authority section if the target was not found. */
if (i >= pList->name_count) {
return (ESURI_OK);
}
/*
* We found our target port name, let's try to find the hostname for the
* discovery address.
*/
/*
* inet_ntop can only fail on incorrect address family or a buffer of an
* inadequate size. It is an internal error if we hit any of those here.
*/
/*
* Discovery address is an IP address, not a hostname. To resolve it,
* getnameinfo(3SOCKET) is used. However, that function needs sockaddr_t
* structure while discovery addresses are byte arrays. So, we use
* getaddrinfo(3SOCKET) to get a sockaddr_t structure out of a string IP
* address prepared above and then we use the structure to look up the
* hostname (or get back to the string IP address if there is no reverse
* available).
*/
SURIGTEXT("Cannot convert IP address into its "
saddr);
return (ESURI_ERR);
}
/*
* We were "resolving" an IP address and can only have one (the first)
* sockaddr_t structure in the addrinfo list.
*/
if (err == EAI_MEMORY)
SURIGTEXT("Cannot not resolve IP address"),
return (ESURI_ERR);
}
/*
* We can have an IPv6 discovery address for which there was no reverse.
* As convention requires, we must enclose it in [] in the URI so that
* it does not clash with the ":port" suffix. inet_pton(3SOCKET) will
* tell us if we have an IPv6 address.
*/
/* Could happen on unknown address family. Not possible here. */
if (err == 1) {
/* We have an IPv6 address, enclose it in brackets */
} else {
/* Hostname or an IPv4 address */
}
return (ESURI_OK);
}
/*
* Return ESURI_OK if the devname is found in the LUN properties. Return
* ESURI_NOENT if not found and ESURI_ERR if any error is hit.
*
* Devname is a device name stripped of the slice ("c0t0d0", for example).
*/
static int
{
int j;
if (!IMA_SUCCESS(status)) {
/* lint's directive PRINTFLIKE2 does not accept %S */
SURIGTEXT("Cannot get LUN properties for target"),
mbtgt);
return (ESURI_ERR);
}
continue;
return (ESURI_OK);
}
return (ESURI_NOENT);
}
/*
* For a given device path 'devp', find an iSCSI target for which the device is
* accessible through. Caller is responsible for freeing the output parameter
* '*tgt'. The device path is supposed to be an existing absolute path for an
* slice.
*
* Return ESURI_OK if target is found, ESURI_NOENT if not found, and a specific
* error otherwise.
*/
char **tgt)
{
int i;
char *devcp;
const char *sdev;
return (ret);
/* Get initiator node OID. */
if (!IMA_SUCCESS(status)) {
SURIGTEXT("Cannot get iSCSI initiator"));
goto out;
}
if (!IMA_SUCCESS(status)) {
SURIGTEXT("Cannot get iSCSI initiator properties"));
goto out;
}
if (!IMA_SUCCESS(status)) {
SURIGTEXT("Cannot get iSCSI target list"));
goto out;
}
/* Each target can have several logical units accessible through it. */
for (i = 0; i < target_list->oidCount; i++) {
if (!IMA_SUCCESS(status)) {
/*
* We cannot be more specific and print the target name
* since it is in the properties we failed to get.
*/
"get properties for iSCSI target"));
goto out;
}
if (!IMA_SUCCESS(status)) {
/*
* lint's directive PRINTFLIKE2 used for
* suri_err_set_desc() does not accept %S, we must
* convert the name to a multibyte string.
*/
"get logical unit list for target"), mbtgt);
goto out;
}
(void) IMA_FreeMemory(lun_list);
continue;
}
if (ret == ESURI_NOENT) {
(void) IMA_FreeMemory(lun_list);
continue;
}
goto out;
goto out;
}
break;
}
/*
* If target is not found, returned value is from the last
* devname_in_lunlist() call.
*/
out:
if (target_list != NULL)
(void) IMA_FreeMemory(target_list);
(void) IMA_FreeMemory(lun_list);
return (ret);
}