root/usr/src/cmd/iscsiadm/sun_ima.c
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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
 */
/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>

#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <libdevinfo.h>

#include <sys/scsi/adapters/iscsi_if.h>
#include <sys/scsi/adapters/iscsi_door.h>
#include <sys/iscsi_protocol.h>
#include <ima.h>
#include "iscsiadm.h"
#include "sun_ima.h"

#define LIBRARY_PROPERTY_SUPPORTED_IMA_VERSION  1
#define LIBRARY_PROPERTY_IMPLEMENTATION_VERSION L"1.0.0"
#define LIBRARY_PROPERTY_VENDOR L"Sun Microsystems, Inc."
#define DEFAULT_NODE_NAME_FORMAT    "iqn.2003-13.com.ima.%s"
#define PLUGIN_OWNER 1
#define MAX_CHAP_SECRET_LEN     16

/* LINTED E_STATIC_UNUSED */
static IMA_INT32                number_of_plugins = -1;
/* LINTED E_STATIC_UNUSED */
static IMA_NODE_NAME            sharedNodeName;
/* LINTED E_STATIC_UNUSED */
static IMA_NODE_ALIAS           sharedNodeAlias;
/* LINTED E_STATIC_UNUSED */
static IMA_PLUGIN_PROPERTIES    PluginProperties;

/* LINTED E_STATIC_UNUSED */
static IMA_OID                  pluginOid;
static IMA_OID                  lhbaObjectId;
/* LINTED E_STATIC_UNUSED */
static boolean_t                pluginInit = B_FALSE;

/* Forward declaration */
#define BOOL_PARAM              1
#define MIN_MAX_PARAM           2
#define PARAM_OP_OK             0
#define PARAM_OP_FAILED         1

static int open_driver(int *fd);
static IMA_STATUS getISCSINodeParameter(int paramType,
    IMA_OID *oid,
    void *pProps,
    uint32_t paramIndex);
static IMA_STATUS setISCSINodeParameter(int paramType,
    IMA_OID *oid,
    void *pProps,
    uint32_t paramIndex);
static IMA_STATUS getDigest(IMA_OID oid, int ioctlCmd,
    SUN_IMA_DIGEST_ALGORITHM_VALUE *algorithm);
static IMA_STATUS setAuthMethods(IMA_OID oid, IMA_UINT *pMethodCount,
    const IMA_AUTHMETHOD *pMethodList);
static IMA_STATUS getAuthMethods(IMA_OID oid, IMA_UINT *pMethodCount,
    IMA_AUTHMETHOD *pMethodList);
IMA_STATUS getNegotiatedDigest(int digestType,
        SUN_IMA_DIGEST_ALGORITHM_VALUE *algorithm,
        SUN_IMA_CONN_PROPERTIES *connProps);

/* OK */
#define DISC_ADDR_OK                0
/* Incorrect IP address */
#define DISC_ADDR_INTEGRITY_ERROR   1
/* Error converting text IP address to numeric binary form */
#define DISC_ADDR_IP_CONV_ERROR     2
static int prepare_discovery_entry(SUN_IMA_TARGET_ADDRESS discoveryAddress,
    entry_t *entry);
static int prepare_discovery_entry_IMA(IMA_TARGET_ADDRESS discoveryAddress,
    entry_t *entry);

/* LINTED E_STATIC_UNUSED */
static IMA_STATUS configure_discovery_method(IMA_BOOL enable,
    iSCSIDiscoveryMethod_t method);

static IMA_STATUS get_target_oid_list(uint32_t targetListType,
    IMA_OID_LIST **ppList);

static IMA_STATUS get_target_lun_oid_list(IMA_OID * targetOid,
                                            iscsi_lun_list_t  **ppLunList);

static int get_lun_devlink(di_devlink_t link, void *arg);

static IMA_STATUS getConnOidList(
        IMA_OID                 *oid,
        iscsi_conn_list_t       **ppConnList);

static IMA_STATUS getConnProps(
        iscsi_if_conn_t         *pConn,
        iscsi_conn_props_t      **ppConnProps);

/* LINTED E_STATIC_UNUSED */
static void libSwprintf(wchar_t *wcs, const wchar_t *lpszFormat, ...)
{
        va_list args;
        va_start(args, lpszFormat);
        (void) vswprintf(wcs, 255, lpszFormat, args);
        va_end(args);
}


char *
_strlwr(char *s)
{
        char *t = s;
        while (t != NULL && *t) {
                if (*t >= 'A' && *t <= 'Z')
                        *t += 32;
                t++;
        }
        return (s);
}

/* LINTED E_STATIC_UNUSED */
static void GetBuildTime(IMA_DATETIME* pdatetime)
{
#if defined(BUILD_DATE)
        if (strptime(BUILD_DATE, "%Y/%m/%d %T %Z", pdatetime) == NULL) {
                (void) memset(pdatetime, 0, sizeof (IMA_DATETIME));
        }
#else
        (void) memset(pdatetime, 0, sizeof (IMA_DATETIME));
#endif
}

/*
 * Non-IMA defined function.
 */
IMA_API IMA_STATUS SUN_IMA_GetDiscoveryAddressPropertiesList(
    SUN_IMA_DISC_ADDR_PROP_LIST **ppList
)
{
        char                discovery_addr_str[256];
        int                 fd;
        int                 i;
        int                 discovery_addr_list_size;
        int                 status;
        int                 out_cnt;
        iscsi_addr_list_t   *ialp;
        /* LINTED E_FUNC_SET_NOT_USED */
        IMA_IP_ADDRESS      *ipAddr;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        ialp = (iscsi_addr_list_t *)calloc(1, sizeof (iscsi_addr_list_t));
        if (ialp == NULL) {
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        ialp->al_vers = ISCSI_INTERFACE_VERSION;
        ialp->al_in_cnt = ialp->al_out_cnt = 1;

        /*
         * Issue ISCSI_DISCOVERY_ADDR_LIST_GET ioctl
         * We have allocated space for one entry, if more than one
         * address is going to be returned, we will re-issue the ioctl
         */
        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, ialp) != 0) {
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_DISCOVERY_ADDR_LIST_GET ioctl failed, errno: %d",
                    errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (ialp->al_out_cnt > 1) {
                /*
                 * we need to allocate more space, save off the out_cnt
                 * and free ialp
                 */
                out_cnt = ialp->al_out_cnt;
                free(ialp);

                discovery_addr_list_size = sizeof (iscsi_addr_list_t);
                discovery_addr_list_size += (sizeof (iscsi_addr_t) *
                    out_cnt - 1);
                ialp = (iscsi_addr_list_t *)calloc(1, discovery_addr_list_size);
                if (ialp == NULL) {
                        (void) close(fd);
                        return (IMA_ERROR_INSUFFICIENT_MEMORY);
                }
                ialp->al_vers = ISCSI_INTERFACE_VERSION;
                ialp->al_in_cnt = out_cnt;

                /*
                 * Issue ISCSI_DISCOVERY_ADDR_LIST_GET ioctl again to obtain all
                 * the discovery addresses.
                 */
                if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, ialp) != 0) {
#define ERROR_STR "ISCSI_DISCOVERY_ADDR_LIST_GET ioctl failed, errno :%d"
                        free(ialp);
                        (void) close(fd);
                        syslog(LOG_USER|LOG_DEBUG,
                            ERROR_STR, errno);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
#undef ERROR_STR

                }
        }

        *ppList = (SUN_IMA_DISC_ADDR_PROP_LIST *)calloc(1,
            sizeof (SUN_IMA_DISC_ADDR_PROP_LIST) +
            ialp->al_out_cnt * sizeof (IMA_DISCOVERY_ADDRESS_PROPERTIES));
        if (*ppList == NULL) {
                free(ialp);
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppList)->discAddrCount = ialp->al_out_cnt;

        for (i = 0; i < ialp->al_out_cnt; i++) {
                if (ialp->al_addrs[i].a_addr.i_insize ==
                    sizeof (struct in_addr)) {

                        (*ppList)->props[i].discoveryAddress.hostnameIpAddress.
                        id.ipAddress.ipv4Address = IMA_TRUE;

                } else if (ialp->al_addrs[i].a_addr.i_insize ==
                    sizeof (struct in6_addr)) {

                        (*ppList)->props[i].discoveryAddress.hostnameIpAddress.
                        id.ipAddress.ipv4Address = IMA_FALSE;

                } else {
                        (void) strlcpy(discovery_addr_str, "unknown",
                            sizeof (discovery_addr_str));
                }

                ipAddr = &(*ppList)->props[i].discoveryAddress.
                    hostnameIpAddress.id.ipAddress;

                bcopy(&ialp->al_addrs[i].a_addr.i_addr,
                    (*ppList)->props[i].discoveryAddress.hostnameIpAddress.id.
                    ipAddress.ipAddress,
                    sizeof (ipAddr->ipAddress));

                (*ppList)->props[i].discoveryAddress.portNumber =
                    ialp->al_addrs[i].a_port;
        }

        free(ialp);
        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS SUN_IMA_GetStaticTargetProperties(
                IMA_OID staticTargetOid,
                SUN_IMA_STATIC_TARGET_PROPERTIES *pProps
)
{
        int fd;
        int status;
        iscsi_static_property_t prop;
        /* LINTED */
        IMA_IP_ADDRESS      *ipAddr;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&prop, 0, sizeof (iscsi_static_property_t));
        prop.p_vers = ISCSI_INTERFACE_VERSION;
        prop.p_oid = (uint32_t)staticTargetOid.objectSequenceNumber;
        if (ioctl(fd, ISCSI_STATIC_GET, &prop) != 0) {
                status = errno;
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_STATIC_GET ioctl failed, errno: %d", status);
                if (status == ENOENT) {
                        return (IMA_ERROR_OBJECT_NOT_FOUND);
                } else {
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }
        (void) close(fd);

        (void) mbstowcs(pProps->staticTarget.targetName, (char *)prop.p_name,
            sizeof (pProps->staticTarget.targetName)/sizeof (IMA_WCHAR));

        if (prop.p_addr_list.al_addrs[0].a_addr.i_insize ==
            sizeof (struct in_addr)) {
                /* IPv4 */
                pProps->staticTarget.targetAddress.imaStruct.hostnameIpAddress.
                        id.ipAddress.ipv4Address = IMA_TRUE;
        } else if (prop.p_addr_list.al_addrs[0].a_addr.i_insize ==
            sizeof (struct in6_addr)) {
                /* IPv6 */
                pProps->staticTarget.targetAddress.imaStruct.hostnameIpAddress.
                        id.ipAddress.ipv4Address = IMA_FALSE;
        } else {
                /* Should not happen */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_STATIC_GET returned bad address");
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        ipAddr = &pProps->staticTarget.targetAddress.imaStruct.
            hostnameIpAddress.id.ipAddress;

        bcopy(&prop.p_addr_list.al_addrs[0].a_addr.i_addr,
            pProps->staticTarget.targetAddress.imaStruct.hostnameIpAddress.id.
            ipAddress.ipAddress, sizeof (ipAddr->ipAddress));

        pProps->staticTarget.targetAddress.imaStruct.portNumber =
            prop.p_addr_list.al_addrs[0].a_port;


        if (prop.p_addr_list.al_tpgt == (uint32_t)ISCSI_DEFAULT_TPGT) {
                pProps->staticTarget.targetAddress.defaultTpgt = IMA_TRUE;
                pProps->staticTarget.targetAddress.tpgt = 0;
        } else {
                pProps->staticTarget.targetAddress.defaultTpgt = IMA_FALSE;
                pProps->staticTarget.targetAddress.tpgt =
                    prop.p_addr_list.al_tpgt;
        }

        return (IMA_STATUS_SUCCESS);
}

/*ARGSUSED*/
IMA_API IMA_STATUS SUN_IMA_AddStaticTarget(
                IMA_OID lhbaOid,
                const SUN_IMA_STATIC_DISCOVERY_TARGET staticConfig,
                IMA_OID *pTargetOid
)
{
        iscsi_target_entry_t    target;
        int                     fd;
        int                     target_in_addr_size;
        int                     status;
        union {
                struct in_addr  u_in4;
                struct in6_addr u_in6;
        }                       target_in;

        /*
         * staticConfig.address may come in with port number at its trailer.
         * Parse it to separate the IP address and port number.
         * Also translate the hostname to IP address if needed.
         */

        if (staticConfig.targetAddress.imaStruct.hostnameIpAddress.id.ipAddress.
            ipv4Address == IMA_FALSE) {

                bcopy(staticConfig.targetAddress.imaStruct.hostnameIpAddress.
                    id.ipAddress.ipAddress, &target_in.u_in6,
                    sizeof (target_in.u_in6));

                target_in_addr_size = sizeof (struct in6_addr);
        } else {

                bcopy(staticConfig.targetAddress.imaStruct.hostnameIpAddress.
                    id.ipAddress.ipAddress, &target_in.u_in4,
                    sizeof (target_in.u_in4));

                target_in_addr_size = sizeof (struct in_addr);
        }

        (void) memset(&target, 0, sizeof (iscsi_target_entry_t));
        target.te_entry.e_vers = ISCSI_INTERFACE_VERSION;
        target.te_entry.e_oid = ISCSI_OID_NOTSET;

        (void) wcstombs((char *)target.te_name, staticConfig.targetName,
            ISCSI_MAX_NAME_LEN);

        target.te_entry.e_insize = target_in_addr_size;
        if (target.te_entry.e_insize == sizeof (struct in_addr)) {
                target.te_entry.e_u.u_in4.s_addr = target_in.u_in4.s_addr;
        } else if (target.te_entry.e_insize == sizeof (struct in6_addr)) {
                bcopy(target_in.u_in6.s6_addr,
                    target.te_entry.e_u.u_in6.s6_addr,
                    sizeof (struct in6_addr));
        } else {
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        target.te_entry.e_port =
            staticConfig.targetAddress.imaStruct.portNumber;

        if (staticConfig.targetAddress.defaultTpgt == IMA_TRUE) {
                target.te_entry.e_tpgt = ISCSI_DEFAULT_TPGT;
        } else {
                target.te_entry.e_tpgt = staticConfig.targetAddress.tpgt;
        }

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        if (ioctl(fd, ISCSI_STATIC_SET, &target)) {
                /*
                 * Encountered problem setting the IP address and port for
                 * the target just added.
                 */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_STATIC_SET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        pTargetOid->objectType = IMA_OBJECT_TYPE_TARGET;
        pTargetOid->ownerId = 1;
        pTargetOid->objectSequenceNumber = target.te_entry.e_oid;

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS SUN_IMA_GetTargetProperties(
                IMA_OID targetId,
                SUN_IMA_TARGET_PROPERTIES *pProps
)
{
        int                 fd;
        int                     status;
        iscsi_property_t    prop;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&prop, 0, sizeof (iscsi_property_t));
        prop.p_vers = ISCSI_INTERFACE_VERSION;
        prop.p_oid = (uint32_t)targetId.objectSequenceNumber;

        if (ioctl(fd, ISCSI_TARGET_PROPS_GET, &prop) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TARGET_PROPS_GET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) mbstowcs(pProps->imaProps.name,
            (char *)prop.p_name, IMA_NODE_NAME_LEN);
        (void) memset(pProps->imaProps.alias, 0,
            (sizeof (IMA_WCHAR) * SUN_IMA_NODE_ALIAS_LEN));
        if (prop.p_alias_len > 0) {
                (void) mbstowcs(pProps->imaProps.alias, (char *)prop.p_alias,
                    SUN_IMA_NODE_ALIAS_LEN);
        }

        /* Initialize the discovery method to unknown method. */
        pProps->imaProps.discoveryMethodFlags =
            IMA_TARGET_DISCOVERY_METHOD_UNKNOWN;
        if (!((prop.p_discovery & iSCSIDiscoveryMethodStatic) ^
            iSCSIDiscoveryMethodStatic)) {
                pProps->imaProps.discoveryMethodFlags |=
                    IMA_TARGET_DISCOVERY_METHOD_STATIC;
        }

        if (!((prop.p_discovery & iSCSIDiscoveryMethodSLP) ^
            iSCSIDiscoveryMethodSLP)) {
                pProps->imaProps.discoveryMethodFlags |=
                    IMA_TARGET_DISCOVERY_METHOD_SLP;
        }

        if (!((prop.p_discovery & iSCSIDiscoveryMethodISNS) ^
            iSCSIDiscoveryMethodISNS)) {
                pProps->imaProps.discoveryMethodFlags |=
                    iSCSIDiscoveryMethodISNS;
        }

        if (!((prop.p_discovery & iSCSIDiscoveryMethodSendTargets) ^
            iSCSIDiscoveryMethodSendTargets)) {
                pProps->imaProps.discoveryMethodFlags |=
                    iSCSIDiscoveryMethodSendTargets;
        }

        if (prop.p_tpgt_conf == ISCSI_DEFAULT_TPGT) {
                pProps->defaultTpgtConf = IMA_TRUE;
                pProps->tpgtConf = 0;
        } else {
                pProps->defaultTpgtConf = IMA_FALSE;
                pProps->tpgtConf = prop.p_tpgt_conf;
        }

        if (prop.p_tpgt_nego == ISCSI_DEFAULT_TPGT) {
                pProps->defaultTpgtNego = IMA_TRUE;
                pProps->tpgtNego = 0;
        } else {
                pProps->defaultTpgtNego = IMA_FALSE;
                pProps->tpgtNego = prop.p_tpgt_nego;
        }

        bcopy(prop.p_isid, pProps->isid, ISCSI_ISID_LEN);

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

/*
 * This function only sets CHAP params since we only support CHAP for now.
 */
IMA_STATUS SUN_IMA_SetTargetAuthParams(
    IMA_OID targetOid,
    IMA_AUTHMETHOD method,
    const IMA_INITIATOR_AUTHPARMS *pParms
)
{
        int fd;
        iscsi_chap_props_t  chap_p;

        if (method != IMA_AUTHMETHOD_CHAP)
                return (IMA_ERROR_INVALID_PARAMETER);

        if ((fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
                syslog(LOG_USER|LOG_DEBUG, "Cannot open %s (%d)",
                    ISCSI_DRIVER_DEVCTL, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) memset(&chap_p, 0, sizeof (iscsi_chap_props_t));
        chap_p.c_vers = ISCSI_INTERFACE_VERSION;
        chap_p.c_oid = (uint32_t)targetOid.objectSequenceNumber;

        chap_p.c_user_len =
            pParms->chapParms.nameLength;
        (void) memcpy(chap_p.c_user,
            pParms->chapParms.name, chap_p.c_user_len);

        chap_p.c_secret_len =
            pParms->chapParms.challengeSecretLength;
        (void) memcpy(chap_p.c_secret,
            pParms->chapParms.challengeSecret,
            chap_p.c_secret_len);

        if (ioctl(fd, ISCSI_CHAP_SET, &chap_p) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_CHAP_SET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_GetTargetAuthMethods(
    IMA_OID             lhbaOid,
    IMA_OID             targetOid,
    IMA_UINT    *pMethodCount,
    IMA_AUTHMETHOD *pMethodList
)
{
        if (getAuthMethods(targetOid, pMethodCount, pMethodList)
            != IMA_STATUS_SUCCESS) {
                return (getAuthMethods(lhbaOid, pMethodCount, pMethodList));
        }
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_SetInitiatorRadiusConfig(
                IMA_OID lhbaOid,
                SUN_IMA_RADIUS_CONFIG *config
)
{
        int                     af;
        int                     fd;
        int                     status;
        iscsi_radius_props_t    radius;
        union {
                struct in_addr  u_in4;
                struct in6_addr u_in6;
        }       radius_in;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&radius, 0, sizeof (iscsi_radius_props_t));
        radius.r_vers = ISCSI_INTERFACE_VERSION;
        radius.r_oid = (uint32_t)lhbaOid.objectSequenceNumber;
        /* Get first because other data fields may already exist */
        if (ioctl(fd, ISCSI_RADIUS_GET, &radius) != 0) {
                /* EMPTY */
                /* It's fine if other data fields are not there. */
        }

        if (config->isIpv6 == IMA_TRUE) {
                af = AF_INET6;
        } else {
                af = AF_INET;
        }

        if (inet_pton(af, config->hostnameIpAddress, &radius_in.u_in4) != 1) {
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        switch (af) {
                case AF_INET:
                        radius.r_addr.u_in4.s_addr = radius_in.u_in4.s_addr;
                        radius.r_insize = sizeof (struct in_addr);
                        break;
                case AF_INET6:
                        (void) memcpy(radius.r_addr.u_in6.s6_addr,
                            radius_in.u_in6.s6_addr, 16);
                        radius.r_insize = sizeof (struct in6_addr);
                        break;
        }
        radius.r_port = config->port;
        radius.r_radius_config_valid = B_TRUE;
        /* Allow resetting the RADIUS shared secret to NULL */
        if (config->sharedSecretValid == IMA_TRUE) {
                radius.r_shared_secret_len = config->sharedSecretLength;
                (void) memset(&radius.r_shared_secret[0], 0,
                    MAX_RAD_SHARED_SECRET_LEN);
                (void) memcpy(&radius.r_shared_secret[0], config->sharedSecret,
                    config->sharedSecretLength);
        }

        if (ioctl(fd, ISCSI_RADIUS_SET, &radius) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_RADIUS_SET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_GetInitiatorRadiusConfig(
                IMA_OID lhbaOid,
                SUN_IMA_RADIUS_CONFIG *config
)
{
        int                     af;
        int                     fd;
        int                     status;
        iscsi_radius_props_t    radius;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&radius, 0, sizeof (iscsi_radius_props_t));
        radius.r_vers = ISCSI_INTERFACE_VERSION;
        radius.r_oid = (uint32_t)lhbaOid.objectSequenceNumber;

        if (ioctl(fd, ISCSI_RADIUS_GET, &radius) != 0) {
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) memset(config, 0, sizeof (SUN_IMA_RADIUS_CONFIG));
        if (radius.r_insize == sizeof (struct in_addr)) {
                /* IPv4 */
                af = AF_INET;
        } else if (radius.r_insize == sizeof (struct in6_addr)) {
                /* IPv6 */
                af = AF_INET6;
        } else {
                /*
                 * It's legitimate that the existing RADIUS record does not
                 * have configuration data.
                 */
                config->hostnameIpAddress[0] = '\0';
                config->port = 0;
                (void) close(fd);
                return (IMA_STATUS_SUCCESS);
        }
        (void) inet_ntop(af, (void *)&radius.r_addr.u_in4,
            config->hostnameIpAddress, 256);
        config->port = radius.r_port;
        (void) memcpy(config->sharedSecret, &radius.r_shared_secret[0],
            radius.r_shared_secret_len);
        config->sharedSecretLength = radius.r_shared_secret_len;
        config->sharedSecretValid = B_TRUE;

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_SetInitiatorRadiusAccess(
    IMA_OID lhbaOid,
    IMA_BOOL radiusAccess
)
{
        int                     fd;
        int                     status;
        iscsi_radius_props_t    radius;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&radius, 0, sizeof (iscsi_radius_props_t));
        radius.r_vers = ISCSI_INTERFACE_VERSION;
        radius.r_oid = (uint32_t)lhbaOid.objectSequenceNumber;
        /* Get first because other data fields may already exist */
        if (ioctl(fd, ISCSI_RADIUS_GET, &radius) != 0) {
                if (radiusAccess == IMA_TRUE) {
                        /*
                         * Cannot enable RADIUS if no RADIUS configuration
                         * can be found.
                         */
                        syslog(LOG_USER|LOG_DEBUG,
                            "RADIUS config data not found - "
                            "cannot enable RADIUS, errno: %d", errno);
                        (void) close(fd);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                } else {
                        /* EMPTY */
                        /* Otherwise it's fine to disable RADIUS */
                }
        }

        if ((radius.r_insize != sizeof (struct in_addr)) &&
                (radius.r_insize != sizeof (struct in6_addr))) {
                /*
                 * Cannot enable RADIUS if no RADIUS configuration
                 * can be found.
                 */
                if (radiusAccess == IMA_TRUE) {
                        syslog(LOG_USER|LOG_DEBUG,
                                "RADIUS config data not found - "
                                "cannot enable RADIUS");
                        (void) close(fd);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }

        radius.r_radius_access = (radiusAccess == IMA_TRUE) ?
            B_TRUE : B_FALSE;

        if (ioctl(fd, ISCSI_RADIUS_SET, &radius) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_RADIUS_SET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_GetInitiatorRadiusAccess(
    IMA_OID lhbaOid,
    IMA_BOOL *radiusAccess
)
{
        int                     fd;
        int                     status;
        iscsi_radius_props_t    radius;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&radius, 0, sizeof (iscsi_radius_props_t));
        radius.r_vers = ISCSI_INTERFACE_VERSION;
        radius.r_oid = (uint32_t)lhbaOid.objectSequenceNumber;

        if (ioctl(fd, ISCSI_RADIUS_GET, &radius) != 0) {
                (void) close(fd);
                if (errno == ENOENT) {
                        return (IMA_ERROR_OBJECT_NOT_FOUND);
                } else {
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }

        *radiusAccess = (radius.r_radius_access == B_TRUE) ?
            IMA_TRUE : IMA_FALSE;

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_SendTargets(
    IMA_NODE_NAME nodeName,
    IMA_TARGET_ADDRESS address,
    SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES **ppList
)
{
        char    *colonPos;
        char    discAddrStr[256];
        char    nodeNameStr[ISCSI_MAX_NAME_LEN];
        int     fd;
        int     ctr;
        int     stl_sz;
        int status;
        iscsi_sendtgts_list_t   *stl_hdr = NULL;
        IMA_BOOL                retry = IMA_TRUE;
        /* LINTED */
        IMA_IP_ADDRESS      *ipAddr;

#define SENDTGTS_DEFAULT_NUM_TARGETS    10

        stl_sz = sizeof (*stl_hdr) + ((SENDTGTS_DEFAULT_NUM_TARGETS - 1) *
            sizeof (iscsi_sendtgts_entry_t));
        stl_hdr = (iscsi_sendtgts_list_t *)calloc(1, stl_sz);
        if (stl_hdr == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        stl_hdr->stl_entry.e_vers = ISCSI_INTERFACE_VERSION;
        stl_hdr->stl_in_cnt = SENDTGTS_DEFAULT_NUM_TARGETS;

        (void) wcstombs(nodeNameStr, nodeName, ISCSI_MAX_NAME_LEN);

        colonPos = strchr(discAddrStr, ':');
        if (colonPos == NULL) {
                /* IPv4 */
                stl_hdr->stl_entry.e_insize = sizeof (struct in_addr);
        } else {
                /* IPv6 */
                stl_hdr->stl_entry.e_insize = sizeof (struct in6_addr);
        }

        ipAddr = &address.hostnameIpAddress.id.ipAddress;

        bcopy(address.hostnameIpAddress.id.ipAddress.ipAddress,
            &stl_hdr->stl_entry.e_u, sizeof (ipAddr->ipAddress));

        stl_hdr->stl_entry.e_port = address.portNumber;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

retry_sendtgts:
        /*
         * Issue ioctl to obtain the SendTargets list
         */
        if (ioctl(fd, ISCSI_SENDTGTS_GET, stl_hdr) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_SENDTGTS_GET ioctl failed, errno: %d", errno);
                (void) close(fd);
                free(stl_hdr);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        /* check if all targets received */
        if (stl_hdr->stl_in_cnt < stl_hdr->stl_out_cnt) {
                if (retry == IMA_TRUE) {
                        stl_sz = sizeof (*stl_hdr) +
                            ((stl_hdr->stl_out_cnt - 1) *
                            sizeof (iscsi_sendtgts_entry_t));
                        stl_hdr = (iscsi_sendtgts_list_t *)
                            realloc(stl_hdr, stl_sz);
                        if (stl_hdr == NULL) {
                                (void) close(fd);
                                return (IMA_ERROR_INSUFFICIENT_MEMORY);
                        }
                        stl_hdr->stl_in_cnt = stl_hdr->stl_out_cnt;
                        retry = IMA_FALSE;
                        goto retry_sendtgts;
                } else {
                        /*
                         * don't retry after 2 attempts.  The target list
                         * shouldn't continue to growing. Justs continue
                         * on and display what was found.
                         */
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_SENDTGTS_GET overflow: "
                            "failed to obtain all targets");
                        stl_hdr->stl_out_cnt = stl_hdr->stl_in_cnt;
                }
        }

        (void) close(fd);

        /* allocate for caller return buffer */
        *ppList = (SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES *)calloc(1,
            sizeof (SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES) +
            stl_hdr->stl_out_cnt * sizeof (SUN_IMA_DISC_ADDRESS_KEY));
        if (*ppList == NULL) {
                free(stl_hdr);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        (*ppList)->keyCount = stl_hdr->stl_out_cnt;

        for (ctr = 0; ctr < stl_hdr->stl_out_cnt; ctr++) {
                (void) mbstowcs((*ppList)->keys[ctr].name,
                    (char *)stl_hdr->stl_list[ctr].ste_name,
                    IMA_NODE_NAME_LEN);

                (*ppList)->keys[ctr].tpgt = stl_hdr->stl_list[ctr].ste_tpgt;

                (*ppList)->keys[ctr].address.portNumber =
                    stl_hdr->stl_list[ctr].ste_ipaddr.a_port;

                if (stl_hdr->stl_list[ctr].ste_ipaddr.a_addr.i_insize ==
                    sizeof (struct in_addr)) {
                        (*ppList)->keys[ctr].address.ipAddress.ipv4Address =
                            IMA_TRUE;
                } else if (stl_hdr->stl_list[ctr].ste_ipaddr.a_addr.i_insize ==
                    sizeof (struct in6_addr)) {
                        (*ppList)->keys[ctr].address.ipAddress.ipv4Address =
                            IMA_FALSE;
                } else {
                        free(stl_hdr);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }

                (void) memcpy(&(*ppList)->keys[ctr].address.ipAddress.ipAddress,
                    &(stl_hdr->stl_list[ctr].ste_ipaddr.a_addr.i_addr),
                    stl_hdr->stl_list[ctr].ste_ipaddr.a_addr.i_insize);
        }
        free(stl_hdr);

        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_SetTargetBidirAuthFlag(
    IMA_OID targetOid,
    IMA_BOOL *bidirAuthFlag
)
{
        int fd;
        int status;
        iscsi_auth_props_t auth;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&auth, 0, sizeof (iscsi_auth_props_t));
        auth.a_vers = ISCSI_INTERFACE_VERSION;
        auth.a_oid = (uint32_t)targetOid.objectSequenceNumber;
        /* Get first because other data fields may already exist */
        if (ioctl(fd, ISCSI_AUTH_GET, &auth) != 0) {
                /* EMPTY */
                /* It is fine if there is no other data fields. */
        }
        auth.a_bi_auth = (*bidirAuthFlag == IMA_TRUE) ? B_TRUE : B_FALSE;
        if (ioctl(fd, ISCSI_AUTH_SET, &auth) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_AUTH_SET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_GetTargetBidirAuthFlag(
    IMA_OID targetOid,
    IMA_BOOL *bidirAuthFlag
)
{
        int fd;
        int status;
        iscsi_auth_props_t auth;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&auth, 0, sizeof (iscsi_auth_props_t));
        auth.a_vers = ISCSI_INTERFACE_VERSION;
        auth.a_oid = (uint32_t)targetOid.objectSequenceNumber;

        if (ioctl(fd, ISCSI_AUTH_GET, &auth) != 0) {
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        *bidirAuthFlag = (auth.a_bi_auth == B_TRUE) ?
            IMA_TRUE : IMA_FALSE;

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_CreateTargetOid(
    IMA_NODE_NAME targetName,
    IMA_OID *targetOid
)
{
        int         fd;
        int             status;
        iscsi_oid_t oid;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&oid, 0, sizeof (iscsi_oid_t));
        (void) wcstombs((char *)oid.o_name, targetName, ISCSI_MAX_NAME_LEN);
        oid.o_tpgt = ISCSI_DEFAULT_TPGT;
        oid.o_vers = ISCSI_INTERFACE_VERSION;
        if (ioctl(fd, ISCSI_CREATE_OID, &oid) == -1) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_CREATE_OID ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        targetOid->objectType = IMA_OBJECT_TYPE_TARGET;
        targetOid->ownerId = 1;
        targetOid->objectSequenceNumber = oid.o_oid;

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_RemoveTargetParam(
                IMA_OID targetOid
)
{
        entry_t entry;
        int     fd;
        int status;
        iscsi_auth_props_t auth_p;
        iscsi_chap_props_t chap_p;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&entry, 0, sizeof (entry_t));
        entry.e_vers = ISCSI_INTERFACE_VERSION;
        entry.e_oid = (uint32_t)targetOid.objectSequenceNumber;
        if (ioctl(fd, ISCSI_TARGET_PARAM_CLEAR, &entry)) {
                /*
                 * It could be that the target exists but the associated
                 * target_param does not, and that is legitimate.
                 */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TARGET_PARAM_CLEAR ioctl failed, errno: %d", errno);
        }

        /* Issue ISCSI_CHAP_CLEAR ioctl */
        (void) memset(&chap_p, 0, sizeof (iscsi_chap_props_t));
        chap_p.c_vers = ISCSI_INTERFACE_VERSION;
        chap_p.c_oid = (uint32_t)targetOid.objectSequenceNumber;
        if (ioctl(fd, ISCSI_CHAP_CLEAR, &chap_p) != 0) {
                /*
                 * It could be that the CHAP of this target has never
                 * been set.
                 */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_CHAP_CLEAR ioctl failed, errno: %d", errno);
        }

        /*
         * Issue ISCSI_AUTH_CLEAR ioctl, in which the authentication information
         * is removed and the target that is not discovered by initiator
         * is removed from the memory. So this ioctl should be called at last
         */
        (void) memset(&auth_p, 0, sizeof (iscsi_auth_props_t));
        auth_p.a_vers = ISCSI_INTERFACE_VERSION;
        auth_p.a_oid = (uint32_t)targetOid.objectSequenceNumber;
        if (ioctl(fd, ISCSI_AUTH_CLEAR, &auth_p) != 0) {
                /*
                 * It could be that the auth data of this target has
                 * never been set.
                 */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_AUTH_CLEAR ioctl failed, errno: %d", errno);
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS SUN_IMA_SetHeaderDigest(
        IMA_OID oid,
        IMA_UINT algorithmCount,
        const SUN_IMA_DIGEST_ALGORITHM *algorithmList
)
{
        IMA_MIN_MAX_VALUE mv;
        uint32_t digest_algorithm;

        /* We only support one preference of digest algorithm. */
        if (algorithmCount > 1) {
                syslog(LOG_USER|LOG_DEBUG,
                    "More than one digest algorithm specified.");
                return (IMA_ERROR_NOT_SUPPORTED);
        }
        switch (algorithmList[0]) {
        case SUN_IMA_DIGEST_NONE:
                digest_algorithm = ISCSI_DIGEST_NONE;
                break;
        case SUN_IMA_DIGEST_CRC32:
                digest_algorithm = ISCSI_DIGEST_CRC32C;
                break;
        default:
                digest_algorithm = ISCSI_DIGEST_NONE;
                break;
        }
        mv.currentValue = digest_algorithm;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &oid, &mv,
            ISCSI_LOGIN_PARAM_HEADER_DIGEST));
}

IMA_API IMA_STATUS SUN_IMA_SetDataDigest(
        IMA_OID oid,
        IMA_UINT algorithmCount,
        const SUN_IMA_DIGEST_ALGORITHM *algorithmList
)
{
        IMA_MIN_MAX_VALUE mv;
        uint32_t digest_algorithm;

        /* We only support one preference of digest algorithm. */
        if (algorithmCount > 1) {
                syslog(LOG_USER|LOG_DEBUG,
                    "More than one digest algorithm specified.");
                return (IMA_ERROR_NOT_SUPPORTED);
        }
        switch (algorithmList[0]) {
        case SUN_IMA_DIGEST_NONE:
                digest_algorithm = ISCSI_DIGEST_NONE;
                break;
        case SUN_IMA_DIGEST_CRC32:
                digest_algorithm = ISCSI_DIGEST_CRC32C;
                break;
        default:
                digest_algorithm = ISCSI_DIGEST_NONE;
                break;
        }
        mv.currentValue = digest_algorithm;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &oid, &mv,
            ISCSI_LOGIN_PARAM_DATA_DIGEST));
}

IMA_API IMA_STATUS SUN_IMA_GetHeaderDigest(
        IMA_OID oid,
        SUN_IMA_DIGEST_ALGORITHM_VALUE *algorithm
)
{
        return (getDigest(oid, ISCSI_LOGIN_PARAM_HEADER_DIGEST, algorithm));
}

IMA_API IMA_STATUS SUN_IMA_GetDataDigest(
        IMA_OID oid,
        SUN_IMA_DIGEST_ALGORITHM_VALUE *algorithm
)
{
        return (getDigest(oid, ISCSI_LOGIN_PARAM_DATA_DIGEST, algorithm));
}

typedef struct walk_devlink {
        char *path;
        size_t len;
        char **linkpp;
} walk_devlink_t;

IMA_STATUS SUN_IMA_GetLuProperties(
                IMA_OID luId,
                SUN_IMA_LU_PROPERTIES *pProps
)
{
        IMA_STATUS              status;
        iscsi_lun_list_t        *pLunList;
        int                     j;
        IMA_BOOL                lunMatch = IMA_FALSE;
        int                     fd;
        int                     openStatus;
        iscsi_lun_props_t       lun;
        di_devlink_handle_t     hdl;
        walk_devlink_t          warg;
        char                    *minor_path, *devlinkp, lunpath[MAXPATHLEN];

        if (luId.objectType != IMA_OBJECT_TYPE_LU) {
                return (IMA_ERROR_INCORRECT_OBJECT_TYPE);
        }

        /*
         * get list of lun oids for all targets
         */
        status = get_target_lun_oid_list(NULL, &pLunList);
        if (!IMA_SUCCESS(status)) {
                return (status);
        }
        for (j = 0; j < pLunList->ll_out_cnt; j++) {
                /*
                 * for each lun, check if match is found
                 */
                if (pLunList->ll_luns[j].l_oid == luId.objectSequenceNumber) {
                        /*
                         * match found, break out of lun loop
                         */
                        lunMatch = IMA_TRUE;
                        break;
                }
        }

        if (lunMatch == IMA_TRUE) {
                (void) memset(&lun, 0, sizeof (iscsi_lun_props_t));
                lun.lp_vers = ISCSI_INTERFACE_VERSION;
                lun.lp_tgt_oid = pLunList->ll_luns[j].l_tgt_oid;
                lun.lp_oid = pLunList->ll_luns[j].l_oid;
        }

        free(pLunList);

        if (lunMatch == IMA_FALSE) {
                return (IMA_ERROR_OBJECT_NOT_FOUND);
        }

        /*
         * get lun properties
         */
        if ((openStatus = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | openStatus);
        }

        if (ioctl(fd, ISCSI_LUN_PROPS_GET, &lun)) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_LUN_PROPS_GET ioctl failed, errno: %d", errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) close(fd);

        /*
         * set property values
         */
        pProps->imaProps.associatedTargetOid.objectType =
            IMA_OBJECT_TYPE_TARGET;
        pProps->imaProps.associatedTargetOid.ownerId = 1;
        pProps->imaProps.associatedTargetOid.objectSequenceNumber = lun.lp_oid;
        pProps->imaProps.targetLun = (IMA_UINT64)lun.lp_num;
        (void) strncpy(pProps->vendorId, lun.lp_vid, SUN_IMA_LU_VENDOR_ID_LEN);
        (void) strncpy(pProps->productId, lun.lp_pid,
            SUN_IMA_LU_PRODUCT_ID_LEN);
        /*
         * lun.lp_status is defined as
         *      LunValid = 0
         *      LunDoesNotExist = 1
         * IMA_LU_PROPS.exposedtoOS is defined as an IMA_BOOL
         *      IMA_TRUE = 1
         *      IMA_FALSE = 0
         */
        pProps->imaProps.exposedToOs = !lun.lp_status;
        if (gmtime_r(&lun.lp_time_online, &pProps->imaProps.timeExposedToOs)
            == NULL) {
                (void) memset(&pProps->imaProps.timeExposedToOs, 0,
                    sizeof (pProps->imaProps.timeExposedToOs));
        }

        if (lun.lp_status == LunValid) {
                if ((strlen(lun.lp_pathname) + strlen("/devices")) >
                    (MAXPATHLEN -1)) {
                        /*
                         * lun.lp_pathname length too long
                         */
                        pProps->imaProps.osDeviceNameValid = IMA_FALSE;
                        pProps->imaProps.osParallelIdsValid = IMA_FALSE;
                        return (IMA_STATUS_SUCCESS);
                }
                if ((strstr(lun.lp_pathname, "st@") != NULL) ||
                    (strstr(lun.lp_pathname, "tape@") != NULL)) {
                        (void) strlcat(lun.lp_pathname, ":n", MAXPATHLEN);
                } else if ((strstr(lun.lp_pathname, "sd@") != NULL) ||
                    (strstr(lun.lp_pathname, "ssd@") != NULL) ||
                    (strstr(lun.lp_pathname, "disk@") != NULL)) {
                        /*
                         * modify returned pathname to obtain the 2nd slice
                         * of the raw disk
                         */
                        (void) strlcat(lun.lp_pathname, ":c,raw", MAXPATHLEN);
                } else if ((strstr(lun.lp_pathname, "ses@") != NULL) ||
                    (strstr(lun.lp_pathname, "enclosure@") != NULL)) {
                        (void) strlcat(lun.lp_pathname, ":0", MAXPATHLEN);
                }

                (void) snprintf(lunpath, sizeof (lun.lp_pathname),
                    "/devices%s", lun.lp_pathname);
                if (strchr(lunpath, ':')) {
                        minor_path = lunpath;
                        if (strstr(minor_path, "/devices") != NULL) {
                                minor_path = lunpath +
                                    strlen("/devices");
                        } else {
                                minor_path = lunpath;
                        }
                        warg.path = NULL;
                } else {
                        minor_path = NULL;
                        warg.len = strlen(lunpath);
                        warg.path = lunpath;
                }
                devlinkp = NULL;
                warg.linkpp = &devlinkp;

                /*
                 * Pathname returned by driver is the physical device path.
                 * This name needs to be converted to the OS device name.
                 */
                if (hdl = di_devlink_init(lun.lp_pathname, DI_MAKE_LINK)) {
                        pProps->imaProps.osDeviceName[0] = L'\0';
                        (void) di_devlink_walk(hdl, NULL, minor_path,
                            DI_PRIMARY_LINK, (void *)&warg, get_lun_devlink);
                        if (devlinkp != NULL) {
                                (void) mbstowcs(pProps->imaProps.osDeviceName,
                                    devlinkp, MAXPATHLEN);
                                free(devlinkp);
                                pProps->imaProps.osDeviceNameValid = IMA_TRUE;
                        } else {
                                /* OS device name is asynchronously made */
                                pProps->imaProps.osDeviceNameValid = IMA_FALSE;
                        }

                        (void) di_devlink_fini(&hdl);

                } else {
                        pProps->imaProps.osDeviceNameValid = IMA_FALSE;
                }

        } else {
                pProps->imaProps.osDeviceNameValid = IMA_FALSE;
        }

        pProps->imaProps.osParallelIdsValid = IMA_FALSE;

        return (IMA_STATUS_SUCCESS);
}

static int
get_lun_devlink(di_devlink_t link, void *arg)
{
        walk_devlink_t *warg = (walk_devlink_t *)arg;
        if (warg->path) {
                char *content = (char *)di_devlink_content(link);
                char *start = strstr(content, "/devices");
                if (start == NULL ||
                    strncmp(start, warg->path, warg->len) != 0 ||
                    start[warg->len] != ':')
                        return (DI_WALK_CONTINUE);
        }

        *(warg->linkpp) = strdup(di_devlink_path(link));
        return (DI_WALK_TERMINATE);

}

/*
 * SUN_IMA_GetConnectionOidList -
 *
 * Non-IMA defined function.
 */
IMA_API IMA_STATUS SUN_IMA_GetConnOidList(
        IMA_OID                 *oid,
        IMA_OID_LIST            **ppList
)
{
        IMA_STATUS              imaStatus;
        IMA_OID_LIST            *imaOidList;
        iscsi_conn_list_t       *iscsiConnList = NULL;
        int                     i;
        size_t                  allocLen;

        if ((lhbaObjectId.objectType == oid->objectType) &&
            (lhbaObjectId.ownerId == oid->ownerId) &&
            (lhbaObjectId.objectSequenceNumber == oid->objectSequenceNumber)) {
                imaStatus = getConnOidList(NULL, &iscsiConnList);
        } else {
                if (oid->objectType == IMA_OBJECT_TYPE_TARGET) {
                        imaStatus = getConnOidList(oid, &iscsiConnList);
                } else {
                        return (IMA_ERROR_INCORRECT_OBJECT_TYPE);
                }
        }
        if (imaStatus != IMA_STATUS_SUCCESS) {
                return (imaStatus);
        }

        /*
         * Based on the results a SUN_IMA_CONN_LIST structure is allocated.
         */
        allocLen  = iscsiConnList->cl_out_cnt * sizeof (IMA_OID);
        allocLen += sizeof (IMA_OID_LIST) - sizeof (IMA_OID);
        imaOidList = (IMA_OID_LIST *)calloc(1, allocLen);

        if (imaOidList == NULL) {
                free(iscsiConnList);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        /* The data is transfered from iscsiConnList to imaConnList. */
        imaOidList->oidCount = iscsiConnList->cl_out_cnt;
        for (i = 0; i < iscsiConnList->cl_out_cnt; i++) {
                imaOidList->oids[i].objectType = SUN_IMA_OBJECT_TYPE_CONN;
                imaOidList->oids[i].ownerId = 1;
                imaOidList->oids[i].objectSequenceNumber =
                    iscsiConnList->cl_list[i].c_oid;
        }
        /* The pointer to the SUN_IMA_CONN_LIST structure is returned. */
        *ppList = imaOidList;

        free(iscsiConnList);
        return (IMA_STATUS_SUCCESS);
}

/*
 * SUN_IMA_GetConnProperties -
 *
 * Non-IMA defined function.
 */
IMA_API IMA_STATUS SUN_IMA_GetConnProperties(
        IMA_OID                 *connOid,
        SUN_IMA_CONN_PROPERTIES **pProps
)
{
        iscsi_conn_list_t       *pConnList;
        iscsi_conn_props_t      *pConnProps;
        /* LINTED */
        struct sockaddr_in6     *addrIn6;
        /* LINTED */
        struct sockaddr_in      *addrIn;
        SUN_IMA_CONN_PROPERTIES *pImaConnProps;
        IMA_STATUS              imaStatus;
        int                     i;

        /* If there is any error *pProps should be set to NULL */
        *pProps = NULL;

        pImaConnProps = (SUN_IMA_CONN_PROPERTIES *)calloc(1,
            sizeof (SUN_IMA_CONN_PROPERTIES));

        if (pImaConnProps == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        imaStatus = getConnOidList(NULL, &pConnList);

        if (imaStatus != IMA_STATUS_SUCCESS) {
                free(pImaConnProps);
                return (imaStatus);
        }

        /*
         * Walk the list returned to find our connection.
         */
        for (i = 0; i < pConnList->cl_out_cnt; i++) {

                if (pConnList->cl_list[i].c_oid ==
                    (uint32_t)connOid->objectSequenceNumber) {

                        /* This is our connection. */
                        imaStatus = getConnProps(&pConnList->cl_list[i],
                            &pConnProps);

                        if (imaStatus != IMA_STATUS_SUCCESS) {
                                free(pConnList);
                                free(pImaConnProps);
                                return (imaStatus);
                        }
                        pImaConnProps->connectionID = pConnProps->cp_cid;

                        /*
                         * Local Propeties
                         */
                        if (pConnProps->cp_local.soa4.sin_family == AF_INET) {

                                pImaConnProps->local.ipAddress.ipv4Address =
                                    IMA_TRUE;
                                pImaConnProps->local.portNumber =
                                    pConnProps->cp_local.soa4.sin_port;
                                addrIn = &(pConnProps->cp_local.soa4);
                                bcopy(&pConnProps->cp_local.soa4.sin_addr,
                                    pImaConnProps->local.ipAddress.ipAddress,
                                    sizeof (addrIn->sin_addr));

                        } else {
                                pImaConnProps->local.ipAddress.ipv4Address =
                                    IMA_FALSE;
                                pImaConnProps->local.portNumber =
                                    pConnProps->cp_local.soa6.sin6_port;
                                addrIn6 = &(pConnProps->cp_local.soa6);
                                bcopy(&pConnProps->cp_local.soa6.sin6_addr,
                                    pImaConnProps->local.ipAddress.ipAddress,
                                    sizeof (addrIn6->sin6_addr));

                        }

                        /*
                         * Peer Propeties
                         */
                        if (pConnProps->cp_peer.soa4.sin_family == AF_INET) {

                                pImaConnProps->peer.ipAddress.ipv4Address =
                                    IMA_TRUE;
                                pImaConnProps->peer.portNumber =
                                    pConnProps->cp_peer.soa4.sin_port;
                                addrIn = &(pConnProps->cp_local.soa4);
                                bcopy(&pConnProps->cp_peer.soa4.sin_addr,
                                    pImaConnProps->peer.ipAddress.ipAddress,
                                    sizeof (addrIn->sin_addr));

                        } else {
                                pImaConnProps->peer.ipAddress.ipv4Address =
                                    IMA_FALSE;
                                pImaConnProps->peer.portNumber =
                                    pConnProps->cp_peer.soa6.sin6_port;

                                addrIn6 = &pConnProps->cp_local.soa6;
                                bcopy(&pConnProps->cp_peer.soa6.sin6_addr,
                                    pImaConnProps->peer.ipAddress.ipAddress,
                                    sizeof (addrIn6->sin6_addr));
                        }


                        pImaConnProps->valuesValid =
                            pConnProps->cp_params_valid;
                        pImaConnProps->defaultTime2Retain =
                            pConnProps->cp_params.default_time_to_retain;
                        pImaConnProps->defaultTime2Wait =
                            pConnProps->cp_params.default_time_to_wait;
                        pImaConnProps->errorRecoveryLevel =
                            pConnProps->cp_params.error_recovery_level;
                        pImaConnProps->firstBurstLength =
                            pConnProps->cp_params.first_burst_length;
                        pImaConnProps->maxBurstLength =
                            pConnProps->cp_params.max_burst_length;
                        pImaConnProps->maxConnections =
                            pConnProps->cp_params.max_connections;
                        pImaConnProps->maxOutstandingR2T =
                            pConnProps->cp_params.max_outstanding_r2t;
                        pImaConnProps->maxRecvDataSegmentLength =
                            pConnProps->cp_params.max_recv_data_seg_len;

                        pImaConnProps->dataPduInOrder =
                            pConnProps->cp_params.data_pdu_in_order;
                        pImaConnProps->dataSequenceInOrder =
                            pConnProps->cp_params.data_sequence_in_order;
                        pImaConnProps->immediateData =
                            pConnProps->cp_params.immediate_data;
                        pImaConnProps->initialR2T =
                            pConnProps->cp_params.initial_r2t;

                        pImaConnProps->headerDigest =
                            pConnProps->cp_params.header_digest;
                        pImaConnProps->dataDigest =
                            pConnProps->cp_params.data_digest;

                        free(pConnProps);
                        break;
                }
        }
        free(pConnList);
        *pProps = pImaConnProps;
        return (IMA_STATUS_SUCCESS);
}


/*
 * SUN_IMA_GetConfigSessions -
 *
 * Non-IMA defined function.
 */
IMA_API IMA_STATUS SUN_IMA_GetConfigSessions(
    IMA_OID targetOid,
    SUN_IMA_CONFIG_SESSIONS **pConfigSessions
)
{
        int                     fd;
        int                     status;
        iscsi_config_sess_t     *ics;
        int                     size, idx;

        /* Allocate and setup initial buffer */
        size = sizeof (*ics);
        ics = (iscsi_config_sess_t *)calloc(1, size);
        if (ics == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        ics->ics_ver = ISCSI_INTERFACE_VERSION;
        ics->ics_oid = targetOid.objectSequenceNumber;
        ics->ics_in  = 1;

        /* Open driver devctl for ioctl */
        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        /* Issue ioctl request */
        if (ioctl(fd, ISCSI_GET_CONFIG_SESSIONS, ics) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_GET_CONFIG_SESSIONS ioctl failed, errno: %d",
                    errno);
                (void) close(fd);
                free(ics);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        /* Check if we need to collect more information */
        idx = ics->ics_out;
        if (idx > 1) {

                /* Free old buffer and reallocate re-sized buffer */
                free(ics);
                size = ISCSI_SESSION_CONFIG_SIZE(idx);
                ics = (iscsi_config_sess_t *)calloc(1, size);
                if (ics == NULL) {
                        return (IMA_ERROR_INSUFFICIENT_MEMORY);
                }
                ics->ics_ver = ISCSI_INTERFACE_VERSION;
                ics->ics_oid = targetOid.objectSequenceNumber;
                ics->ics_in = idx;

                /* Issue ioctl request */
                if (ioctl(fd, ISCSI_GET_CONFIG_SESSIONS, ics) != 0) {
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_GET_CONFIG_SESSIONS ioctl failed, errno: %d",
                            errno);
                        (void) close(fd);
                        free(ics);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }
        (void) close(fd);

        /* Allocate output buffer */
        size = sizeof (SUN_IMA_CONFIG_SESSIONS) +
            ((ics->ics_out - 1) * sizeof (IMA_ADDRESS_KEY));
        *pConfigSessions = (SUN_IMA_CONFIG_SESSIONS *)calloc(1, size);
        if ((*pConfigSessions) == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        /* Copy output information */
        (*pConfigSessions)->bound =
            (ics->ics_bound == B_TRUE ?  IMA_TRUE : IMA_FALSE);
        (*pConfigSessions)->in = ics->ics_in;
        (*pConfigSessions)->out = ics->ics_out;
        for (idx = 0; idx < ics->ics_in; idx++) {
                if (ics->ics_bindings[idx].i_insize ==
                    sizeof (struct in_addr)) {
                        (*pConfigSessions)->bindings[idx].ipAddress.
                            ipv4Address = IMA_TRUE;
                        bcopy(&ics->ics_bindings[idx].i_addr.in4,
                            (*pConfigSessions)->bindings[idx].ipAddress.
                            ipAddress, sizeof (struct in_addr));
                } else {
                        (*pConfigSessions)->bindings[idx].ipAddress.
                            ipv4Address = IMA_FALSE;
                        bcopy(&ics->ics_bindings[idx].i_addr.in6,
                            (*pConfigSessions)->bindings[idx].ipAddress.
                            ipAddress, sizeof (struct in6_addr));
                }
        }

        free(ics);
        return (IMA_STATUS_SUCCESS);
}

/*
 * SUN_IMA_SetConfigSessions -
 *
 * Non-IMA defined function.
 */
IMA_API IMA_STATUS SUN_IMA_SetConfigSessions(
    IMA_OID targetOid,
    SUN_IMA_CONFIG_SESSIONS *pConfigSessions
)
{
        int                 fd;
        int                 status;
        iscsi_config_sess_t *ics;
        int                 idx, size;

        /* verify allowed range of sessions */
        if ((pConfigSessions->in < ISCSI_MIN_CONFIG_SESSIONS) ||
            (pConfigSessions->in > ISCSI_MAX_CONFIG_SESSIONS)) {
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        /* allocate record config_sess size */
        size = ISCSI_SESSION_CONFIG_SIZE(pConfigSessions->in);
        ics = (iscsi_config_sess_t *)malloc(size);

        /* setup config_sess information */
        (void) memset(ics, 0, sizeof (iscsi_config_sess_t));
        ics->ics_ver = ISCSI_INTERFACE_VERSION;
        ics->ics_oid = targetOid.objectSequenceNumber;
        ics->ics_bound =
            (pConfigSessions->bound == IMA_TRUE ?  B_TRUE : B_FALSE);
        ics->ics_in  = pConfigSessions->in;
        for (idx = 0; idx < ics->ics_in; idx++) {
                if (pConfigSessions->bindings[idx].ipAddress.
                    ipv4Address == IMA_TRUE) {
                        ics->ics_bindings[idx].i_insize =
                            sizeof (struct in_addr);
                        bcopy(pConfigSessions->bindings[idx].
                            ipAddress.ipAddress,
                            &ics->ics_bindings[idx].i_addr.in4,
                            sizeof (struct in_addr));
                } else {
                        ics->ics_bindings[idx].i_insize =
                            sizeof (struct in6_addr);
                        bcopy(pConfigSessions->bindings[idx].
                            ipAddress.ipAddress,
                            &ics->ics_bindings[idx].i_addr.in6,
                            sizeof (struct in6_addr));
                }
        }

        /* open driver */
        if ((status = open_driver(&fd))) {
                free(ics);
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        /* issue ioctl request */
        if (ioctl(fd, ISCSI_SET_CONFIG_SESSIONS, ics) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_SET_CONFIG_SESSIONS ioctl failed, errno: %d",
                    errno);
                (void) close(fd);
                free(ics);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }
        (void) close(fd);
        free(ics);
        return (IMA_STATUS_SUCCESS);
}

/* A helper function to obtain iSCSI node parameters. */
static IMA_STATUS
getISCSINodeParameter(
    int paramType,
    IMA_OID *oid,
    void *pProps,
    uint32_t paramIndex
)
{
        int                 fd;
        int             status;
        iscsi_param_get_t   pg;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&pg, 0, sizeof (iscsi_param_get_t));
        pg.g_vers = ISCSI_INTERFACE_VERSION;
        pg.g_oid = (uint32_t)oid->objectSequenceNumber;
        pg.g_param = paramIndex;
        pg.g_param_type = ISCSI_SESS_PARAM;

        if (ioctl(fd, ISCSI_PARAM_GET, &pg) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_PARAM_GET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        switch (paramType) {
                IMA_BOOL_VALUE *bp;
                IMA_MIN_MAX_VALUE *mp;

                case MIN_MAX_PARAM:
                        mp = (IMA_MIN_MAX_VALUE *)pProps;

                        mp->currentValueValid =
                            (pg.g_value.v_valid == B_TRUE) ?
                            IMA_TRUE : IMA_FALSE;
                        mp->currentValue = pg.g_value.v_integer.i_current;
                        mp->defaultValue = pg.g_value.v_integer.i_default;
                        mp->minimumValue = pg.g_value.v_integer.i_min;
                        mp->maximumValue = pg.g_value.v_integer.i_max;
                        mp->incrementValue = pg.g_value.v_integer.i_incr;
                        break;

                case BOOL_PARAM:
                        bp = (IMA_BOOL_VALUE *)pProps;
                        bp->currentValueValid =
                            (pg.g_value.v_valid == B_TRUE) ?
                            IMA_TRUE : IMA_FALSE;
                        bp->currentValue = pg.g_value.v_bool.b_current;
                        bp->defaultValue = pg.g_value.v_bool.b_default;
                        break;

                default:
                        break;
        }

        /* Issue ISCSI_PARAM_GET ioctl again to obtain connection parameters. */
        pg.g_param_type = ISCSI_CONN_PARAM;
        if (ioctl(fd, ISCSI_PARAM_GET, &pg) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_PARAM_GET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

/* A helper function to set iSCSI node parameters. */
static IMA_STATUS
setISCSINodeParameter(
    int paramType,
    IMA_OID *oid,
    void *pProp,
    uint32_t paramIndex
)
{
        int                 fd;
        int                     status;
        iscsi_param_set_t   ps;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&ps, 0, sizeof (iscsi_param_set_t));
        ps.s_vers = ISCSI_INTERFACE_VERSION;
        ps.s_oid = (uint32_t)oid->objectSequenceNumber;
        ps.s_param = paramIndex;

        switch (paramType) {
                IMA_BOOL_VALUE *bp;
                IMA_MIN_MAX_VALUE *mp;

                case MIN_MAX_PARAM:
                        mp = (IMA_MIN_MAX_VALUE *)pProp;
                        ps.s_value.v_integer = mp->currentValue;
                        break;
                case BOOL_PARAM:
                        bp = (IMA_BOOL_VALUE *)pProp;
                        ps.s_value.v_bool =
                            (bp->currentValue == IMA_TRUE) ?
                            B_TRUE : B_FALSE;
                        break;

                default:
                        break;
        }
        if (ioctl(fd, ISCSI_PARAM_SET, &ps)) {
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_PARAM_SET ioctl failed, errno: %d", errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

static int
prepare_discovery_entry(
    SUN_IMA_TARGET_ADDRESS discoveryAddress,
    entry_t *entry
)
{
        return (prepare_discovery_entry_IMA(discoveryAddress.imaStruct, entry));
}

static int
prepare_discovery_entry_IMA(
    IMA_TARGET_ADDRESS discoveryAddress,
    entry_t *entry
)
{
        (void) memset(entry, 0, sizeof (entry_t));
        entry->e_vers = ISCSI_INTERFACE_VERSION;
        entry->e_oid = ISCSI_OID_NOTSET;

        if (discoveryAddress.hostnameIpAddress.id.ipAddress.
            ipv4Address == IMA_FALSE) {

                bcopy(discoveryAddress.hostnameIpAddress.id.ipAddress.
                    ipAddress, entry->e_u.u_in6.s6_addr,
                    sizeof (entry->e_u.u_in6.s6_addr));

                entry->e_insize = sizeof (struct in6_addr);
        } else {

                bcopy(discoveryAddress.hostnameIpAddress.id.ipAddress.
                    ipAddress, &entry->e_u.u_in4.s_addr,
                    sizeof (entry->e_u.u_in4.s_addr));

                entry->e_insize = sizeof (struct in_addr);
        }

        entry->e_port = discoveryAddress.portNumber;
        entry->e_tpgt = 0;
        return (DISC_ADDR_OK);
}

static IMA_STATUS configure_discovery_method(
    IMA_BOOL enable,
    iSCSIDiscoveryMethod_t method
)
{
        int     fd, status;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        if (enable == IMA_FALSE) {
                if (ioctl(fd, ISCSI_DISCOVERY_CLEAR, &method)) {
                        status = errno;
                        (void) close(fd);
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_DISCOVERY_CLEAR ioctl failed, errno: %d",
                            status);
                        if (status == EBUSY) {
                                return (IMA_ERROR_LU_IN_USE);
                        } else {
                                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                        }
                }

                (void) close(fd);
                return (IMA_STATUS_SUCCESS);
        } else {
                /* Set the discovery method */
                if (ioctl(fd, ISCSI_DISCOVERY_SET, &method)) {
                        status = errno;
                        (void) close(fd);
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_DISCOVERY_SET ioctl failed, errno: %d",
                            status);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }

                (void) close(fd);
                return (IMA_STATUS_SUCCESS);
        }
}

/* LINTED E_STATIC_UNUSED */
static IMA_BOOL authMethodMatch(
    IMA_AUTHMETHOD matchingMethod,
    IMA_AUTHMETHOD *methodList,
    IMA_UINT maxEntries
)
{
        IMA_UINT i;

        for (i = 0; i < maxEntries; i++) {
                if (methodList[i] == matchingMethod) {
                        return (IMA_TRUE);
                }
        }

        return (IMA_FALSE);
}

static IMA_STATUS get_target_oid_list(
    uint32_t targetListType,
    IMA_OID_LIST **ppList)
{
        int                 fd;
        int                 i;
        int                 target_list_size;
        int                 status;
        int                 out_cnt;
        iscsi_target_list_t *idlp;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        idlp = (iscsi_target_list_t *)calloc(1, sizeof (iscsi_target_list_t));
        if (idlp == NULL) {
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        idlp->tl_vers = ISCSI_INTERFACE_VERSION;
        idlp->tl_in_cnt = idlp->tl_out_cnt = 1;
        idlp->tl_tgt_list_type = targetListType;

        /*
         * Issue ioctl.  Space has been allocted for one entry.
         * If more than one entry should be returned, we will re-issue the
         * entry with the right amount of space allocted
         */
        if (ioctl(fd, ISCSI_TARGET_OID_LIST_GET, idlp) != 0) {
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TARGET_OID_LIST_GET ioctl %d failed, errno: %d",
                    targetListType, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }
        if (idlp->tl_out_cnt > 1) {
                out_cnt = idlp->tl_out_cnt;
                free(idlp);

                target_list_size = sizeof (iscsi_target_list_t);
                target_list_size += (sizeof (uint32_t) * out_cnt - 1);
                idlp = (iscsi_target_list_t *)calloc(1, target_list_size);
                if (idlp == NULL) {
                        (void) close(fd);
                        return (IMA_ERROR_INSUFFICIENT_MEMORY);
                }
                idlp->tl_vers = ISCSI_INTERFACE_VERSION;
                idlp->tl_in_cnt = out_cnt;
                idlp->tl_tgt_list_type = targetListType;

                /* Issue the same ioctl again to obtain all the OIDs. */
                if (ioctl(fd, ISCSI_TARGET_OID_LIST_GET, idlp) != 0) {
#define ERROR_STR "ISCSI_DISCOVERY_ADDR_LIST_GET ioctl failed, errno :%d"
                        free(idlp);
                        (void) close(fd);
                        syslog(LOG_USER|LOG_DEBUG,
                            ERROR_STR, targetListType, errno);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
#undef ERROR_STR

                }
        }

        *ppList = (IMA_OID_LIST *)calloc(1, sizeof (IMA_OID_LIST) +
            idlp->tl_out_cnt * sizeof (IMA_OID));
        (*ppList)->oidCount = idlp->tl_out_cnt;
        for (i = 0; i < idlp->tl_out_cnt; i++) {
                (*ppList)->oids[i].objectType = IMA_OBJECT_TYPE_TARGET;
                (*ppList)->oids[i].ownerId = 1;
                (*ppList)->oids[i].objectSequenceNumber = idlp->tl_oid_list[i];
        }

        free(idlp);
        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

static IMA_STATUS get_target_lun_oid_list(
    IMA_OID * targetOid,
    iscsi_lun_list_t  **ppLunList)
{
        int                     fd;
        iscsi_lun_list_t        *illp, *illp_saved;
        int                     lun_list_size;
        int                     status;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        illp = (iscsi_lun_list_t *)calloc(1, sizeof (iscsi_lun_list_t));
        if (illp == NULL) {
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        illp->ll_vers = ISCSI_INTERFACE_VERSION;
        if (targetOid == NULL) {
                /* get lun oid list for all targets */
                illp->ll_all_tgts = B_TRUE;
        } else {
                /* get lun oid list for single target */
                illp->ll_all_tgts = B_FALSE;
                illp->ll_tgt_oid = (uint32_t)targetOid->objectSequenceNumber;
        }
        illp->ll_in_cnt = illp->ll_out_cnt = 1;

        /*
         * Issue ioctl to retrieve the target luns.  Space has been allocted
         * for one entry.  If more than one entry should be returned, we
         * will re-issue the entry with the right amount of space allocted
         */
        if (ioctl(fd, ISCSI_LUN_OID_LIST_GET, illp) != 0) {
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_LUN_LIST_GET ioctl failed, errno: %d", errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (illp->ll_out_cnt > 1) {
                illp_saved = illp;
                lun_list_size = sizeof (iscsi_lun_list_t);
                lun_list_size += (sizeof (iscsi_if_lun_t) *
                    (illp->ll_out_cnt - 1));
                illp = (iscsi_lun_list_t *)calloc(1, lun_list_size);
                if (illp == NULL) {
                        (void) close(fd);
                        return (IMA_ERROR_INSUFFICIENT_MEMORY);
                }
                illp->ll_vers = ISCSI_INTERFACE_VERSION;
                illp->ll_all_tgts = illp_saved->ll_all_tgts;
                illp->ll_tgt_oid = illp_saved->ll_tgt_oid;
                illp->ll_in_cnt = illp_saved->ll_out_cnt;

                free(illp_saved);

                /* Issue the same ioctl again to get all the target LUN list */
                if (ioctl(fd, ISCSI_LUN_OID_LIST_GET, illp) != 0) {
                        free(illp);
                        (void) close(fd);
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_LUN_LIST_GET ioctl failed, errno: %d",
                            errno);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);

                }
        }
        *ppLunList = illp;


        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

/* A helper function to obtain digest algorithms. */
static IMA_STATUS
getDigest(
    IMA_OID oid,
    int ioctlCmd,
    SUN_IMA_DIGEST_ALGORITHM_VALUE *algorithm
)
{
        IMA_MIN_MAX_VALUE pProps;
        IMA_STATUS status;

        if ((status = getISCSINodeParameter(MIN_MAX_PARAM, &oid, &pProps,
            ioctlCmd)) != IMA_STATUS_SUCCESS) {
                return (status);
        }

        switch (pProps.defaultValue) {
                case ISCSI_DIGEST_NONE:
                        algorithm->defaultAlgorithms[0] = ISCSI_DIGEST_NONE;
                        algorithm->defaultAlgorithmCount = 1;
                        break;
                case ISCSI_DIGEST_CRC32C:
                        algorithm->defaultAlgorithms[0] = ISCSI_DIGEST_CRC32C;
                        algorithm->defaultAlgorithmCount = 1;
                        break;

                case ISCSI_DIGEST_CRC32C_NONE:
                        algorithm->defaultAlgorithms[0] = ISCSI_DIGEST_CRC32C;
                        algorithm->defaultAlgorithms[1] = ISCSI_DIGEST_NONE;
                        algorithm->defaultAlgorithmCount = 2;
                        break;
                case ISCSI_DIGEST_NONE_CRC32C:
                        algorithm->defaultAlgorithms[0] = ISCSI_DIGEST_NONE;
                        algorithm->defaultAlgorithms[1] = ISCSI_DIGEST_CRC32C;
                        algorithm->defaultAlgorithmCount = 2;
                        break;
                default:
                        /* Error */
                        syslog(LOG_USER|LOG_DEBUG,
                            "Invalid default digest: %d", pProps.defaultValue);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        /* The configured value */
        if (pProps.currentValueValid == IMA_TRUE) {
                algorithm->currentValid = IMA_TRUE;

                switch (pProps.currentValue) {
                        case ISCSI_DIGEST_NONE:
                                algorithm->currentAlgorithms[0] =
                                    ISCSI_DIGEST_NONE;
                                algorithm->currentAlgorithmCount = 1;
                                break;
                        case ISCSI_DIGEST_CRC32C:
                                algorithm->currentAlgorithms[0] =
                                    ISCSI_DIGEST_CRC32C;
                                algorithm->currentAlgorithmCount = 1;
                                break;

                        case ISCSI_DIGEST_CRC32C_NONE:
                                algorithm->currentAlgorithms[0] =
                                    ISCSI_DIGEST_CRC32C;
                                algorithm->currentAlgorithms[1] =
                                    ISCSI_DIGEST_NONE;
                                algorithm->currentAlgorithmCount = 2;
                                break;
                        case ISCSI_DIGEST_NONE_CRC32C:
                                algorithm->currentAlgorithms[0] =
                                    ISCSI_DIGEST_NONE;
                                algorithm->currentAlgorithms[1] =
                                    ISCSI_DIGEST_CRC32C;
                                algorithm->currentAlgorithmCount = 2;
                                break;
                        default:
                                /* Error */
                                syslog(LOG_USER|LOG_DEBUG,
                                    "Invalid configured digest: %d",
                                    pProps.defaultValue);
                                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }

        } else {
                algorithm->currentValid = IMA_FALSE;
        }

        return (IMA_STATUS_SUCCESS);
}

/*
 * getConnOidList -
 */
static IMA_STATUS getConnOidList(
        IMA_OID                 *sessOid,
        iscsi_conn_list_t       **ppConnList
)
{
        iscsi_conn_list_t       *iscsiConnList = NULL;
        size_t                  allocLen;
        int                     fd;
        int                     status;
        int                     out_cnt;

        /* Preset it to NULL to prepare for the case of failure */
        *ppConnList = NULL;

        /* We try to open the driver now. */
        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        iscsiConnList = (iscsi_conn_list_t *)calloc(1,
            sizeof (iscsi_conn_list_t));
        if (iscsiConnList == NULL) {
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        iscsiConnList->cl_vers = ISCSI_INTERFACE_VERSION;
        iscsiConnList->cl_in_cnt = iscsiConnList->cl_out_cnt = 1;
        if (sessOid == NULL) {
                iscsiConnList->cl_all_sess = B_TRUE;
        } else {
                iscsiConnList->cl_all_sess = B_FALSE;
                iscsiConnList->cl_sess_oid =
                    (uint32_t)sessOid->objectSequenceNumber;
        }
        /*
         * Issue ioctl to retrieve the connection OIDs.  Space has been
         * allocated for one entry.  If more than one entry should be
         * returned, we will re-issue the entry with the right amount of
         * space allocted
         */
        if (ioctl(fd, ISCSI_CONN_OID_LIST_GET, iscsiConnList) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_CONN_OID_LIST_GET ioctl failed, errno: %d", errno);
                *ppConnList = NULL;
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }
        if (iscsiConnList->cl_out_cnt > 1) {
                out_cnt = iscsiConnList->cl_out_cnt;
                free(iscsiConnList);

                allocLen = sizeof (iscsi_conn_list_t);
                allocLen += (sizeof (iscsi_if_conn_t) * out_cnt - 1);
                iscsiConnList = (iscsi_conn_list_t *)calloc(1, allocLen);
                if (iscsiConnList == NULL) {
                        *ppConnList = NULL;
                        (void) close(fd);
                        return (IMA_ERROR_INSUFFICIENT_MEMORY);
                }
                iscsiConnList->cl_vers = ISCSI_INTERFACE_VERSION;
                iscsiConnList->cl_in_cnt = out_cnt;
                if (sessOid == NULL) {
                        iscsiConnList->cl_all_sess = B_TRUE;
                } else {
                        iscsiConnList->cl_all_sess = B_FALSE;
                        iscsiConnList->cl_sess_oid =
                            (uint32_t)sessOid->objectSequenceNumber;
                }
                /* Issue the same ioctl again to obtain all the OIDs */
                if (ioctl(fd, ISCSI_CONN_OID_LIST_GET, iscsiConnList) != 0) {

                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_CONN_OID_LIST_GET ioctl failed, errno: %d",
                            errno);
                        *ppConnList = NULL;
                        free(iscsiConnList);
                        (void) close(fd);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);

                }

                if (out_cnt < iscsiConnList->cl_out_cnt) {
                        /*
                         * The connection list grew between the first and second
                         * ioctls.
                         */
                        syslog(LOG_USER|LOG_DEBUG,
                            "The connection list has grown. There could be "
                            "more connections than listed.");
                }
        }


        (void) close(fd);
        *ppConnList = iscsiConnList;
        return (IMA_STATUS_SUCCESS);
}

/*
 * getConnProps -
 */
static IMA_STATUS getConnProps(
        iscsi_if_conn_t         *pConn,
        iscsi_conn_props_t      **ppConnProps
)
{
        iscsi_conn_props_t      *iscsiConnProps;
        int                     fd;
        int                     status;

        /* We try to open the driver. */
        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        iscsiConnProps = (iscsi_conn_props_t *)calloc(1,
            sizeof (*iscsiConnProps));

        if (iscsiConnProps == NULL) {
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        iscsiConnProps->cp_vers = ISCSI_INTERFACE_VERSION;
        iscsiConnProps->cp_oid = pConn->c_oid;
        iscsiConnProps->cp_cid = pConn->c_cid;
        iscsiConnProps->cp_sess_oid = pConn->c_sess_oid;

        /* The IOCTL is submitted. */
        if (ioctl(fd, ISCSI_CONN_PROPS_GET, iscsiConnProps) != 0) {
                /* IOCTL failed */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_AUTH_CLEAR ioctl failed, errno: %d", errno);
                free(iscsiConnProps);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }
        (void) close(fd);
        *ppConnProps = iscsiConnProps;
        return (IMA_STATUS_SUCCESS);
}

/* A helper function to set authentication method. */
static IMA_STATUS
setAuthMethods(
    IMA_OID oid,
    IMA_UINT *pMethodCount,
    const IMA_AUTHMETHOD *pMethodList
)
{
        int fd;
        int i;
        int status;
        iscsi_auth_props_t auth;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }
        (void) memset(&auth, 0, sizeof (iscsi_auth_props_t));
        auth.a_vers = ISCSI_INTERFACE_VERSION;
        auth.a_oid = (uint32_t)oid.objectSequenceNumber;

        /*
         * Get the current auth fields so they don't need to be reset
         * here.
         */
        if (ioctl(fd, ISCSI_AUTH_GET, &auth) != 0) {
            /* EMPTY */
            /* Initializing auth structure with current settings */
        }
        auth.a_auth_method = authMethodNone;

        for (i = 0; i < *pMethodCount; i++) {
                switch (pMethodList[i]) {
                        case IMA_AUTHMETHOD_CHAP:
                                auth.a_auth_method |= authMethodCHAP;
                                break;
                        default:
                                break;
                }
        }

        if (ioctl(fd, ISCSI_AUTH_SET, &auth) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_AUTH_SET failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

/* A helper function to set authentication method. */
static IMA_STATUS getAuthMethods(
    IMA_OID oid,
    IMA_UINT    *pMethodCount,
    IMA_AUTHMETHOD *pMethodList
)
{
        int fd;
        int status;
        iscsi_auth_props_t auth;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) memset(&auth, 0, sizeof (iscsi_auth_props_t));
        auth.a_vers = ISCSI_INTERFACE_VERSION;
        auth.a_oid = (uint32_t)oid.objectSequenceNumber;

        if (ioctl(fd, ISCSI_AUTH_GET, &auth) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_AUTH_GET failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (auth.a_auth_method == authMethodNone) {
                pMethodList[0] = IMA_AUTHMETHOD_NONE;
                *pMethodCount = 1;
        } else {
                int i = 0;
                if (!((auth.a_auth_method & authMethodCHAP)^authMethodCHAP)) {
                        pMethodList[i++] = IMA_AUTHMETHOD_CHAP;
                }
                *pMethodCount = i;
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

/* Helper function to open driver */
int open_driver(
        int *fd
)
{
        int ret = 0;
        if ((*fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
                ret = errno;
                syslog(LOG_USER|LOG_DEBUG, "Cannot open %s (%d)",
                    ISCSI_DRIVER_DEVCTL, ret);
        }
        return (ret);
}

/*
 * Iscsi driver does not support OID for discovery address. Create
 * a modified version of IMA_RemoveDiscoveryAddress that takes
 * discoveryAddress (instead of an OID) as input argument.
 */
IMA_API IMA_STATUS SUN_IMA_RemoveDiscoveryAddress(
    SUN_IMA_TARGET_ADDRESS discoveryAddress
)
{
        entry_t entry;
        int     fd;
        int status, i, addr_list_size, insize;
        iscsi_addr_list_t *idlp, al_info;
        iscsi_addr_t *matched_addr = NULL;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        if (prepare_discovery_entry(discoveryAddress, &entry) !=
            DISC_ADDR_OK) {
                (void) close(fd);
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        (void) memset(&al_info, 0, sizeof (al_info));
        al_info.al_vers = ISCSI_INTERFACE_VERSION;
        al_info.al_in_cnt = 0;
        /*
         * Issue ioctl to get the number of discovery address.
         */
        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, &al_info) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_DISCOVERY_ADDR_LIST_GET ioctl %d failed, errno: %d",
                    ISCSI_DISCOVERY_ADDR_LIST_GET, errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (al_info.al_out_cnt == 0) {
                (void) close(fd);
                return (IMA_ERROR_OBJECT_NOT_FOUND);
        }

        addr_list_size = sizeof (iscsi_addr_list_t);
        if (al_info.al_out_cnt > 1) {
                addr_list_size += (sizeof (iscsi_addr_t) *
                    (al_info.al_out_cnt - 1));
        }

        idlp = (iscsi_addr_list_t *)calloc(1, addr_list_size);
        if (idlp == NULL) {
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        idlp->al_vers = ISCSI_INTERFACE_VERSION;
        idlp->al_in_cnt = al_info.al_out_cnt;

        /* issue the same ioctl to get all the discovery addresses */
        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, idlp) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_DISCOVERY_ADDR_LIST_GET ioctl %d failed, errno: %d",
                    ISCSI_DISCOVERY_ADDR_LIST_GET, errno);
                free(idlp);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        /*
         * find the matched discovery address
         */
        for (i = 0; i < idlp->al_out_cnt; i++) {
                insize = idlp->al_addrs[i].a_addr.i_insize;
                if (insize != entry.e_insize) {
                        continue;
                }
                if (insize == sizeof (struct in_addr)) {
                        if (idlp->al_addrs[i].a_addr.i_addr.in4.s_addr ==
                            entry.e_u.u_in4.s_addr) {
                                matched_addr = &(idlp->al_addrs[i]);
                                break;
                        }
                }
                if (insize == sizeof (struct in6_addr)) {
                        if (bcmp(entry.e_u.u_in6.s6_addr,
                            idlp->al_addrs[i].a_addr.i_addr.in6.s6_addr,
                            insize) == 0) {
                                matched_addr = &(idlp->al_addrs[i]);
                                break;
                        }
                }
        }

        free(idlp);

        if (matched_addr == NULL) {
                (void) close(fd);
                return (IMA_ERROR_OBJECT_NOT_FOUND);
        }

        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_CLEAR, &entry)) {
                status = errno;
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_DISCOVERY_ADDR_CLEAR ioctl failed, errno: %d",
                    errno);
                if (status == EBUSY) {
                        return (IMA_ERROR_LU_IN_USE);
                } else {
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_SetTargetAuthMethods(
                IMA_OID targetOid,
                IMA_UINT *methodCount,
                const IMA_AUTHMETHOD *pMethodList
)
{
        return (setAuthMethods(targetOid, methodCount, pMethodList));
}

IMA_STATUS getNegotiatedDigest(
        int digestType,
        SUN_IMA_DIGEST_ALGORITHM_VALUE *algorithm,
        SUN_IMA_CONN_PROPERTIES *connProps) {

        IMA_UINT digest;

        if (connProps->valuesValid == IMA_TRUE) {
                algorithm->negotiatedValid = IMA_TRUE;

                if (digestType == ISCSI_LOGIN_PARAM_HEADER_DIGEST) {
                        digest = connProps->headerDigest;
                } else {
                        digest = connProps->dataDigest;
                }

                switch (digest) {
                        case ISCSI_DIGEST_NONE:
                                algorithm->negotiatedAlgorithms[0] =
                                    ISCSI_DIGEST_NONE;
                                algorithm->negotiatedAlgorithmCount = 1;
                                break;
                        case ISCSI_DIGEST_CRC32C:
                                algorithm->negotiatedAlgorithms[0] =
                                    ISCSI_DIGEST_CRC32C;
                                algorithm->negotiatedAlgorithmCount = 1;
                                break;

                        case ISCSI_DIGEST_CRC32C_NONE:
                                algorithm->negotiatedAlgorithms[0] =
                                    ISCSI_DIGEST_CRC32C;
                                algorithm->negotiatedAlgorithms[1] =
                                    ISCSI_DIGEST_NONE;
                                algorithm->negotiatedAlgorithmCount = 2;
                                break;
                        case ISCSI_DIGEST_NONE_CRC32C:
                                algorithm->negotiatedAlgorithms[0] =
                                    ISCSI_DIGEST_NONE;
                                algorithm->negotiatedAlgorithms[1] =
                                    ISCSI_DIGEST_CRC32C;
                                algorithm->negotiatedAlgorithmCount = 2;
                                break;
                        default:
                                syslog(LOG_USER|LOG_DEBUG,
                                    "Invalid negotiated digest: %d",
                                    digest);
                                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        } else {
                algorithm->negotiatedValid = IMA_FALSE;
        }
        return (IMA_STATUS_SUCCESS);
}

/*
 * Non-IMA defined function.
 */
IMA_API IMA_STATUS SUN_IMA_GetISNSServerAddressPropertiesList(
    SUN_IMA_DISC_ADDR_PROP_LIST **ppList
)
{
        char                isns_server_addr_str[256];
        int                 fd;
        int                 i;
        int                 isns_server_addr_list_size;
        int                 status;
        int                 out_cnt;
        iscsi_addr_list_t   *ialp;
        /* LINTED */
        IMA_IP_ADDRESS      *ipAddr;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        ialp = (iscsi_addr_list_t *)calloc(1, sizeof (iscsi_addr_list_t));
        if (ialp == NULL) {
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        ialp->al_vers = ISCSI_INTERFACE_VERSION;
        ialp->al_in_cnt = ialp->al_out_cnt = 1;

        /*
         * Issue ioctl to retrieve the isns server addresses.  Space has been
         * allocted for one entry.  If more than one entry should be returned,
         * we will re-issue the entry with the right amount of space allocted
         */
        if (ioctl(fd, ISCSI_ISNS_SERVER_ADDR_LIST_GET, ialp) != 0) {
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_ISNS_SERVER_ADDR_LIST_GET ioctl failed, errno: %d",
                    errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        isns_server_addr_list_size = sizeof (iscsi_addr_list_t);
        if (ialp->al_out_cnt > 1) {
                out_cnt = ialp->al_out_cnt;
                free(ialp);

                isns_server_addr_list_size += (sizeof (iscsi_addr_t) *
                    out_cnt - 1);
                ialp = (iscsi_addr_list_t *)calloc(1,
                    isns_server_addr_list_size);
                if (ialp == NULL) {
                        (void) close(fd);
                        return (IMA_ERROR_INSUFFICIENT_MEMORY);
                }
                ialp->al_vers = ISCSI_INTERFACE_VERSION;
                ialp->al_in_cnt = out_cnt;

                /*
                 * Issue ISCSI_ISNS_SERVER_ADDR_LIST_GET ioctl again to obtain
                 * the list of all the iSNS server addresses
                 */
                if (ioctl(fd, ISCSI_ISNS_SERVER_ADDR_LIST_GET, ialp) != 0) {
                        free(ialp);
                        (void) close(fd);
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_ISNS_SERVER_ADDR_LIST_GET ioctl failed, "
                            "errno: %d", errno);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);

                }
        }

        *ppList = (SUN_IMA_DISC_ADDR_PROP_LIST *)calloc(1,
            sizeof (SUN_IMA_DISC_ADDR_PROP_LIST) +
            ialp->al_out_cnt * sizeof (IMA_DISCOVERY_ADDRESS_PROPERTIES));
        if (*ppList == NULL) {
                free(ialp);
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppList)->discAddrCount = ialp->al_out_cnt;

        for (i = 0; i < ialp->al_out_cnt; i++) {
                if (ialp->al_addrs[i].a_addr.i_insize ==
                    sizeof (struct in_addr)) {
                        (*ppList)->props[i].discoveryAddress.hostnameIpAddress.
                        id.ipAddress.ipv4Address = IMA_TRUE;
                } else if (ialp->al_addrs[i].a_addr.i_insize ==
                    sizeof (struct in6_addr)) {
                        (*ppList)->props[i].discoveryAddress.hostnameIpAddress.
                            id.ipAddress.ipv4Address = IMA_FALSE;
                } else {
                        (void) strlcpy(isns_server_addr_str, "unknown",
                            sizeof (isns_server_addr_str));
                }

                ipAddr = &(*ppList)->props[i].discoveryAddress.
                    hostnameIpAddress.id.ipAddress;
                bcopy(&ialp->al_addrs[i].a_addr.i_addr,
                    (*ppList)->props[i].discoveryAddress.hostnameIpAddress.id.
                    ipAddress.ipAddress,
                    sizeof (ipAddr->ipAddress));
                (*ppList)->props[i].discoveryAddress.portNumber =
                    ialp->al_addrs[i].a_port;
        }

        free(ialp);
        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

/*ARGSUSED*/
/*
 * Remove iSNS Server Address
 */
IMA_API IMA_STATUS SUN_IMA_RemoveISNSServerAddress(
    SUN_IMA_TARGET_ADDRESS isnsServerAddress
)
{
        entry_t entry;
        int     fd, status;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        if (prepare_discovery_entry(isnsServerAddress, &entry) !=
            DISC_ADDR_OK) {
                (void) close(fd);
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        if (ioctl(fd, ISCSI_ISNS_SERVER_ADDR_CLEAR, &entry)) {
                status = errno;
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_ISNS_SERVER_ADDR_CLEAR ioctl failed, errno: %d",
                    status);
                if (status == EBUSY) {
                        return (IMA_ERROR_LU_IN_USE);
                } else {
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

/*ARGSUSED*/
IMA_API IMA_STATUS SUN_IMA_AddISNSServerAddress(
                const SUN_IMA_TARGET_ADDRESS isnsServerAddress
)
{
        entry_t                     entry;
        int                         fd;
        int                         status;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        if (prepare_discovery_entry(isnsServerAddress, &entry) !=
            DISC_ADDR_OK) {
                (void) close(fd);
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        if (ioctl(fd, ISCSI_ISNS_SERVER_ADDR_SET, &entry)) {
                /*
                 * Encountered problem setting the discovery address.
                 */
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_ISNS_SERVER_ADDR_SET ioctl failed, errno: %d",
                    errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_RetrieveISNSServerTargets(
    IMA_TARGET_ADDRESS serverAddress,
    SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES **ppList
)
{
        int                                 fd;
        int                                 ctr;
        int                                 server_pg_list_sz;
        int                                 status;
        isns_server_portal_group_list_t     *server_pg_list = NULL;
        isns_portal_group_list_t            *pg_list = NULL;
        IMA_BOOL                            retry = IMA_TRUE;
        entry_t                             entry;

#define ISNS_SERVER_DEFAULT_NUM_TARGETS 50

        server_pg_list_sz = sizeof (*server_pg_list) +
            ((ISNS_SERVER_DEFAULT_NUM_TARGETS - 1) *
            sizeof (isns_portal_group_t));

        server_pg_list = (isns_server_portal_group_list_t *)calloc(1,
            server_pg_list_sz);
        if (server_pg_list == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        server_pg_list->addr_port_list.pg_in_cnt =
            ISNS_SERVER_DEFAULT_NUM_TARGETS;

        if ((prepare_discovery_entry_IMA(serverAddress, &entry)
            != DISC_ADDR_OK)) {
                free(server_pg_list);
                return (IMA_ERROR_INVALID_PARAMETER);
        }
        server_pg_list->addr.a_port = entry.e_port;
        server_pg_list->addr.a_addr.i_insize = entry.e_insize;
        if (entry.e_insize == sizeof (struct in_addr)) {
                server_pg_list->addr.a_addr.i_addr.in4.s_addr =
                    (entry.e_u.u_in4.s_addr);
        } else if (entry.e_insize == sizeof (struct in6_addr)) {
                bcopy(&entry.e_u.u_in6.s6_addr,
                    server_pg_list->addr.a_addr.i_addr.in6.s6_addr, 16);
        }

        if ((status = open_driver(&fd))) {
                free(server_pg_list);
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

retry_isns:
        /*
         * Issue ioctl to obtain the ISNS Portal Group List list
         */
        if (ioctl(fd, ISCSI_ISNS_SERVER_GET, server_pg_list) != 0) {
                int tmp_errno = errno;
                IMA_STATUS return_status;

                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_ISNS_SERVER_GET ioctl failed, errno: %d", tmp_errno);
                if (tmp_errno == EACCES) {
                        return_status = IMA_ERROR_OBJECT_NOT_FOUND;
                } else {
                        return_status = IMA_ERROR_UNEXPECTED_OS_ERROR;
                }
                (void) close(fd);
                free(server_pg_list);
                return (return_status);
        }
        pg_list = &server_pg_list->addr_port_list;

        /* check if all targets received */
        if (pg_list->pg_in_cnt < pg_list->pg_out_cnt) {
                if (retry == IMA_TRUE) {
                        server_pg_list_sz = sizeof (*server_pg_list) +
                            ((pg_list->pg_out_cnt - 1) *
                            sizeof (isns_server_portal_group_list_t));
                        server_pg_list = (isns_server_portal_group_list_t *)
                            realloc(server_pg_list, server_pg_list_sz);
                        if (server_pg_list == NULL) {
                                (void) close(fd);
                                return (IMA_ERROR_INSUFFICIENT_MEMORY);
                        }
                        pg_list = &server_pg_list->addr_port_list;
                        pg_list->pg_in_cnt = pg_list->pg_out_cnt;
                        retry = IMA_FALSE;
                        goto retry_isns;
                } else {
                        /*
                         * don't retry after 2 attempts.  The target list
                         * shouldn't continue growing. Just continue
                         * on and display what was found.
                         */
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_SENDTGTS_GET overflow: "
                            "failed to obtain all targets");
                        pg_list->pg_out_cnt = pg_list->pg_in_cnt;
                }
        }

        (void) close(fd);

        /* allocate for caller return buffer */
        *ppList = (SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES *)calloc(1,
            sizeof (SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES) +
            pg_list->pg_out_cnt * sizeof (SUN_IMA_DISC_ADDRESS_KEY));
        if (*ppList == NULL) {
                free(server_pg_list);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        (*ppList)->keyCount = pg_list->pg_out_cnt;

        for (ctr = 0; ctr < pg_list->pg_out_cnt; ctr++) {
                (void) mbstowcs((*ppList)->keys[ctr].name,
                    (char *)pg_list->pg_list[ctr].pg_iscsi_name,
                    IMA_NODE_NAME_LEN);

                (*ppList)->keys[ctr].tpgt = pg_list->pg_list[ctr].pg_tag;

                (*ppList)->keys[ctr].address.portNumber =
                    pg_list->pg_list[ctr].pg_port;

                if (pg_list->pg_list[ctr].insize == sizeof (struct in_addr)) {
                        (*ppList)->keys[ctr].address.ipAddress.ipv4Address =
                            IMA_TRUE;
                } else if (pg_list->pg_list[ctr].insize ==
                    sizeof (struct in6_addr)) {
                        (*ppList)->keys[ctr].address.ipAddress.ipv4Address =
                            IMA_FALSE;
                } else {
                        free(pg_list);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }

                (void) memcpy(&(*ppList)->keys[ctr].address.ipAddress.ipAddress,
                    &(pg_list->pg_list[ctr].pg_ip_addr),
                    pg_list->pg_list[ctr].insize);
        }
        free(server_pg_list);

        return (IMA_STATUS_SUCCESS);
}

/* ARGSUSED */
IMA_STATUS SUN_IMA_GetSessionOidList(
    IMA_OID initiatorOid,
    IMA_OID_LIST **ppList
)
{
        return (get_target_oid_list(ISCSI_TGT_OID_LIST, ppList));
}

/*ARGSUSED*/
IMA_API IMA_STATUS SUN_IMA_GetTargetAuthParms(
        IMA_OID oid,
        IMA_AUTHMETHOD method,
        IMA_INITIATOR_AUTHPARMS *pParms
)
{
        int fd;
        iscsi_chap_props_t  chap_p;

        if (pParms == NULL) {
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        if (oid.objectType != IMA_OBJECT_TYPE_TARGET) {
                return (IMA_ERROR_INCORRECT_OBJECT_TYPE);
        }

        if (method != IMA_AUTHMETHOD_CHAP) {
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        if ((fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
                syslog(LOG_USER|LOG_DEBUG, "Cannot open %s (%d)",
                    ISCSI_DRIVER_DEVCTL, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) memset(&chap_p, 0, sizeof (iscsi_chap_props_t));
        chap_p.c_vers = ISCSI_INTERFACE_VERSION;
        chap_p.c_oid = (uint32_t)oid.objectSequenceNumber;

        if (ioctl(fd, ISCSI_CHAP_GET, &chap_p) != 0) {
                syslog(LOG_USER|LOG_DEBUG,

                    "ISCSI_CHAP_GET ioctl failed, errno: %d",
                    errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        (void) memcpy(pParms->chapParms.name, chap_p.c_user,
            chap_p.c_user_len);

        pParms->chapParms.nameLength = chap_p.c_user_len;
        (void) memcpy(pParms->chapParms.challengeSecret, chap_p.c_secret,
            chap_p.c_secret_len);

        pParms->chapParms.challengeSecretLength = chap_p.c_secret_len;

        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS SUN_IMA_GetBootTargetName(
    IMA_NODE_NAME tgtName
)
{
        int fd;
        IMA_STATUS rtn;
        iscsi_boot_property_t bootProp;

        bootProp.tgt_name.n_name[0] = '\0';
        bootProp.tgt_chap.c_user[0] = '\0';
        tgtName[0] = L'\0';
        rtn = IMA_ERROR_UNEXPECTED_OS_ERROR;
        if ((fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
                syslog(LOG_USER|LOG_DEBUG, "Unable to open %s (%d)",
                    ISCSI_DRIVER_DEVCTL, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (ioctl(fd, ISCSI_BOOTPROP_GET, &bootProp) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_BOOTPROP_GET ioctl failed, errno: %d",
                    errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if ((bootProp.tgt_name.n_name[0] != '\0') && (tgtName != NULL)) {
                if (mbstowcs(tgtName, (const char *)bootProp.tgt_name.n_name,
                    IMA_NODE_NAME_LEN) == (size_t)-1) {
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI Target name covert to WCHAR fail");
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                } else {
                        rtn = IMA_STATUS_SUCCESS;
                }
        }

        return (rtn);
}

IMA_API IMA_STATUS SUN_IMA_GetBootTargetAuthParams(
    IMA_INITIATOR_AUTHPARMS *pTgtCHAP
)
{
        int fd;
        IMA_STATUS rtn;
        iscsi_boot_property_t bootProp;

        bootProp.tgt_name.n_name[0] = '\0';
        bootProp.tgt_chap.c_user[0] = '\0';
        bootProp.tgt_chap.c_secret[0] = '\0';
        rtn = IMA_ERROR_UNEXPECTED_OS_ERROR;
        if ((fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
                syslog(LOG_USER|LOG_DEBUG, "Unable to open %s (%d)",
                    ISCSI_DRIVER_DEVCTL, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (ioctl(fd, ISCSI_BOOTPROP_GET, &bootProp) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_BOOTPROP_GET ioctl failed, errno: %d",
                    errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (pTgtCHAP != NULL) {
                if (bootProp.tgt_chap.c_user[0] != '\0') {
                        (void) memcpy(pTgtCHAP->chapParms.name,
                            bootProp.tgt_chap.c_user, ISCSI_MAX_NAME_LEN);
                } else {
                        pTgtCHAP->chapParms.name[0] = '\0';
                }
                if (bootProp.tgt_chap.c_secret[0] != '\0') {
                        (void) memcpy(pTgtCHAP->chapParms.challengeSecret,
                            bootProp.tgt_chap.c_secret, MAX_CHAP_SECRET_LEN);
                } else {
                        pTgtCHAP->chapParms.challengeSecret[0] = '\0';
                }
                rtn = IMA_STATUS_SUCCESS;
        }
        return (rtn);
}

IMA_STATUS SUN_IMA_GetBootMpxio(
    IMA_BOOL *pMpxioEnabled
)
{
        int fd;
        iscsi_boot_property_t bootProp;

        bootProp.hba_mpxio_enabled = B_FALSE;
        *pMpxioEnabled = IMA_UNKNOWN;

        if ((fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
                syslog(LOG_USER|LOG_DEBUG, "Unable to open %s (%d)",
                    ISCSI_DRIVER_DEVCTL, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (ioctl(fd, ISCSI_BOOTPROP_GET, &bootProp) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_BOOTPROP_GET ioctl failed, errno: %d",
                    errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (bootProp.hba_mpxio_enabled) {
                *pMpxioEnabled = IMA_TRUE;
        } else {
                *pMpxioEnabled = IMA_FALSE;
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_GetBootIscsi(
    IMA_BOOL *pIscsiBoot
)
{
        int fd;
        iscsi_boot_property_t bootProp;

        bootProp.iscsiboot = 0;
        *pIscsiBoot = 0;

        if ((fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
                syslog(LOG_USER|LOG_DEBUG, "Unable to open %s (%d)",
                    ISCSI_DRIVER_DEVCTL, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (ioctl(fd, ISCSI_BOOTPROP_GET, &bootProp) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_BOOTPROP_GET ioctl failed, errno: %d",
                    errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        *pIscsiBoot = bootProp.iscsiboot;

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_GetSvcStatus(
    IMA_BOOL *pSvcEnabled)
{
        int             fd;
        uint32_t        status = ISCSI_SERVICE_DISABLED;

        if (pSvcEnabled == NULL)
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        *pSvcEnabled = 0;

        if ((fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
                syslog(LOG_USER|LOG_DEBUG, "Unable to open %s (%d)",
                    ISCSI_DRIVER_DEVCTL, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (ioctl(fd, ISCSI_SMF_GET, &status) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_SVC_GET ioctl failed, errno: %d",
                    errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (status == ISCSI_SERVICE_ENABLED) {
                *pSvcEnabled = 1;
        }

        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS SUN_IMA_ReEnumeration(
        IMA_OID targetId
)
{
        int             fd;
        int             status;
        iscsi_reen_t    reet;

        reet.re_ver = ISCSI_INTERFACE_VERSION;
        reet.re_oid = (uint32_t)targetId.objectSequenceNumber;

        if ((status = open_driver(&fd))) {
                return (SUN_IMA_ERROR_SYSTEM_ERROR | status);
        }

        (void) ioctl(fd, ISCSI_TARGET_REENUM, &reet);

        (void) close(fd);

        return (IMA_STATUS_SUCCESS);
}