root/usr/src/lib/libsun_ima/common/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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#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 <wchar.h>
#include <widec.h>
#include <libsysevent.h>
#include <sys/nvpair.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <libdevinfo.h>
#include <sys/scsi/generic/commands.h>
#include <sys/scsi/generic/status.h>
#include <sys/scsi/adapters/iscsi_if.h>
#include <sys/iscsi_protocol.h>
#include <ima.h>
#include <libsun_ima.h>

#define LIBRARY_PROPERTY_IMPLEMENTATION_VERSION L"1.0.0"
#define LIBRARY_PROPERTY_VENDOR                 L"Sun Microsystems, Inc."
#define OS_DEVICE_NAME                          "/devices/iscsi"
#define LIBRARY_FILE_NAME                       L"libsun_ima.so"

#define OS_DEVICE_NAME_LEN              256
#define USCSI_TIMEOUT_IN_SEC            10
#define MAX_AUTHMETHODS                 10
#define NUM_SUPPORTED_AUTH_METHODS      2
#define SUN_IMA_MAX_DIGEST_ALGORITHMS   2       /* NONE and CRC 32 */
#define SUN_IMA_IP_ADDRESS_LEN          256
#define SUN_IMA_IP_PORT_LEN             64
#define SUN_IMA_MAX_RADIUS_SECRET_LEN   128
#define MAX_LONG_LONG_STRING_LEN        10
#define MAX_INQUIRY_BUFFER_LEN          0xffff
#define MAX_REPORT_LUNS_BUFFER_LEN      0xffffffff
#define MAX_READ_CAPACITY16_BUFFER_LEN  0xffffffff

/* Forward declaration */
#define BOOL_PARAM                      1
#define MIN_MAX_PARAM                   2

/* 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

/* Currently not defined in  IMA_TARGET_DISCOVERY_METHOD enum */
#define IMA_TARGET_DISCOVERY_METHOD_UNKNOWN  0

static IMA_OID          lhbaObjectId;
static IMA_UINT32       pluginOwnerId;
static sysevent_handle_t *shp;



/*
 * Custom struct to allow tgpt to be specified.
 */
typedef struct _SUN_IMA_DISC_ADDRESS_KEY
{
        IMA_NODE_NAME name;
        IMA_ADDRESS_KEY address;
        IMA_UINT16 tpgt;
} SUN_IMA_DISC_ADDRESS_KEY;

/*
 * Custom struct to allow tgpt to be specified.
 */
typedef struct _SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES
{
        IMA_UINT keyCount;
        SUN_IMA_DISC_ADDRESS_KEY keys[1];
} SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES;

/*
 * Custom struct to allow tgpt to be specified.
 */
typedef struct _SUN_IMA_DISC_ADDR_PROP_LIST
{
        IMA_UINT discAddrCount;
        IMA_DISCOVERY_ADDRESS_PROPERTIES props[1];
} SUN_IMA_DISC_ADDR_PROP_LIST;


static IMA_OBJECT_VISIBILITY_FN pObjectVisibilityCallback = NULL;
static IMA_OBJECT_PROPERTY_FN pObjectPropertyCallback = NULL;

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 setAuthMethods(IMA_OID oid, IMA_UINT *pMethodCount,
    const IMA_AUTHMETHOD *pMethodList);
static IMA_STATUS getAuthMethods(IMA_OID oid, IMA_UINT *pMethodCount,
    IMA_AUTHMETHOD *pMethodList);

static int prepare_discovery_entry(IMA_TARGET_ADDRESS discoveryAddress,
    entry_t *entry);
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 *osDeviceName);
static IMA_STATUS getDiscoveryAddressPropertiesList(
        SUN_IMA_DISC_ADDR_PROP_LIST **ppList
);
static IMA_STATUS sendTargets(IMA_TARGET_ADDRESS address,
    SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES **ppList
);

static IMA_STATUS getSupportedAuthMethods(IMA_OID lhbaOid,
    IMA_BOOL getSettableMethods, IMA_UINT *pMethodCount,
    IMA_AUTHMETHOD *pMethodList);
static IMA_STATUS getLuProperties(IMA_OID luId, IMA_LU_PROPERTIES *pProps);
static IMA_STATUS getTargetProperties(IMA_OID targetId,
    IMA_TARGET_PROPERTIES *pProps);

void InitLibrary();

static void libSwprintf(wchar_t *wcs, const wchar_t *lpszFormat, ...)
{
        va_list args;
        va_start(args, lpszFormat);
        (void) vswprintf(wcs, OS_DEVICE_NAME_LEN - 1, lpszFormat, args);
        va_end(args);
}

static void
sysevent_handler(sysevent_t *ev)
{
        IMA_OID tmpOid;
        IMA_BOOL becomingVisible = IMA_FALSE;
        IMA_UINT i;

        const char *visibility_subclasses[] = {
                ESC_ISCSI_STATIC_START,
                ESC_ISCSI_STATIC_END,
                ESC_ISCSI_SEND_TARGETS_START,
                ESC_ISCSI_SEND_TARGETS_END,
                ESC_ISCSI_SLP_START,
                ESC_ISCSI_SLP_END,
                ESC_ISCSI_ISNS_START,
                ESC_ISCSI_ISNS_END,
                NULL
        };

        tmpOid.ownerId = pluginOwnerId;
        tmpOid.objectType = IMA_OBJECT_TYPE_TARGET;
        tmpOid.objectSequenceNumber = 0;

        /* Make sure our event class matches what we are looking for */
        if (strncmp(EC_ISCSI, sysevent_get_class_name(ev),
            strlen(EC_ISCSI)) != 0) {
                return;
        }


        /* Check for object property changes */
        if ((strncmp(ESC_ISCSI_PROP_CHANGE,
            sysevent_get_subclass_name(ev),
            strlen(ESC_ISCSI_PROP_CHANGE)) == 0)) {
                if (pObjectPropertyCallback != NULL)
                        pObjectPropertyCallback(tmpOid);
        } else {
                i = 0;
                while (visibility_subclasses[i] != NULL) {
                        if ((strncmp(visibility_subclasses[i],
                            sysevent_get_subclass_name(ev),
                            strlen(visibility_subclasses[i])) == 0) &&
                            pObjectVisibilityCallback != NULL) {
                                becomingVisible = IMA_TRUE;
                                pObjectVisibilityCallback(becomingVisible,
                                    tmpOid);
                        }
                        i++;
                }
        }
}

IMA_STATUS init_sysevents() {
        const char *subclass_list[] = {
                ESC_ISCSI_STATIC_START,
                ESC_ISCSI_STATIC_END,
                ESC_ISCSI_SEND_TARGETS_START,
                ESC_ISCSI_SEND_TARGETS_END,
                ESC_ISCSI_SLP_START,
                ESC_ISCSI_SLP_END,
                ESC_ISCSI_ISNS_START,
                ESC_ISCSI_ISNS_END,
                ESC_ISCSI_PROP_CHANGE,
        };

        /* Bind event handler and create subscriber handle */
        shp = sysevent_bind_handle(sysevent_handler);
        if (shp == NULL) {
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (sysevent_subscribe_event(shp, EC_ISCSI, subclass_list, 9) != 0) {
                sysevent_unbind_handle(shp);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS Initialize(IMA_UINT32 pluginOid) {
        pluginOwnerId = pluginOid;
        return (init_sysevents());
}

void Terminate() {
        if (shp != NULL) {
                sysevent_unsubscribe_event(shp, EC_ISCSI);
        }

}

void InitLibrary() {
}

static void GetBuildTime(IMA_DATETIME* pdatetime)
{
        (void) memset(pdatetime, 0, sizeof (IMA_DATETIME));
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_GetNodeProperties(
        IMA_OID nodeOid,
        IMA_NODE_PROPERTIES *pProps
)
{
        int fd;
        iscsi_param_get_t pg;

        pProps->runningInInitiatorMode = IMA_TRUE;
        pProps->runningInTargetMode = IMA_FALSE;
        pProps->nameAndAliasSettable = IMA_FALSE;

        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(&pg, 0, sizeof (iscsi_param_get_t));
        pg.g_vers = ISCSI_INTERFACE_VERSION;
        pg.g_param = ISCSI_LOGIN_PARAM_INITIATOR_NAME;

        if (ioctl(fd, ISCSI_PARAM_GET, &pg) == -1) {
                pProps->nameValid = IMA_FALSE;
        } else {
                if (strlen((char *)pg.g_value.v_name) > 0) {
                        (void) mbstowcs(pProps->name,
                            (char *)pg.g_value.v_name,
                            IMA_NODE_NAME_LEN);
                        pProps->nameValid = IMA_TRUE;
                } else {
                        pProps->nameValid = IMA_FALSE;
                }
        }

        (void) memset(&pg, 0, sizeof (iscsi_param_get_t));
        pg.g_vers = ISCSI_INTERFACE_VERSION;
        pg.g_param = ISCSI_LOGIN_PARAM_INITIATOR_ALIAS;
        (void) memset(pProps->alias, 0,
            sizeof (IMA_WCHAR) * IMA_NODE_ALIAS_LEN);
        if (ioctl(fd, ISCSI_PARAM_GET, &pg) == -1) {
                pProps->aliasValid = IMA_FALSE;
        } else {
                if (strlen((char *)pg.g_value.v_name) > 0) {
                        (void) mbstowcs(pProps->alias,
                            (char *)pg.g_value.v_name,
                            IMA_NODE_ALIAS_LEN);
                        pProps->aliasValid = IMA_TRUE;
                }
        }

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

IMA_API IMA_STATUS IMA_SetNodeName(
        IMA_OID nodeOid,
        const IMA_NODE_NAME newName
)
{
        int fd;
        iscsi_param_set_t ps;

        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(&ps, 0, sizeof (iscsi_param_set_t));
        ps.s_oid = nodeOid.objectSequenceNumber;
        ps.s_vers = ISCSI_INTERFACE_VERSION;
        ps.s_param = ISCSI_LOGIN_PARAM_INITIATOR_NAME;
        (void) wcstombs((char *)ps.s_value.v_name, newName, ISCSI_MAX_NAME_LEN);
        if (ioctl(fd, ISCSI_INIT_NODE_NAME_SET, &ps)) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_PARAM_SET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

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

IMA_API IMA_STATUS IMA_SetNodeAlias(
        IMA_OID nodeOid,
        const IMA_NODE_ALIAS newAlias
)
{
        int fd;
        iscsi_param_set_t ps;

        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(&ps, 0, sizeof (iscsi_param_set_t));
        ps.s_oid = nodeOid.objectSequenceNumber;
        ps.s_vers = ISCSI_INTERFACE_VERSION;
        ps.s_param = ISCSI_LOGIN_PARAM_INITIATOR_ALIAS;

        /* newAlias = NULL specifies that the alias should be deleted. */
        if (newAlias != NULL)
                (void) wcstombs((char *)ps.s_value.v_name, newAlias,
                    ISCSI_MAX_NAME_LEN);
        else
                (void) wcstombs((char *)ps.s_value.v_name,
                    L"", ISCSI_MAX_NAME_LEN);

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

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


IMA_API IMA_STATUS IMA_GetLhbaOidList(
        IMA_OID_LIST **ppList
)
{
        /* Always return the same object ID for the lhba */
        lhbaObjectId.objectType = IMA_OBJECT_TYPE_LHBA;
        lhbaObjectId.ownerId = pluginOwnerId;
        lhbaObjectId.objectSequenceNumber = ISCSI_INITIATOR_OID;

        *ppList = (IMA_OID_LIST*)calloc(1, sizeof (IMA_OID_LIST));
        if (*ppList == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        (*ppList)->oidCount = 1;
        (void) memcpy(&(*ppList)->oids[0],
            &lhbaObjectId, sizeof (lhbaObjectId));
        return (IMA_STATUS_SUCCESS);
}


/*
 * Get the discovery properties of the LHBA
 */
/*ARGSUSED*/
IMA_API IMA_STATUS IMA_GetDiscoveryProperties(
        IMA_OID oid,
        IMA_DISCOVERY_PROPERTIES *pProps
)
{
        int fd;
        iSCSIDiscoveryProperties_t discoveryProps;

        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(&discoveryProps, 0, sizeof (discoveryProps));
        discoveryProps.vers = ISCSI_INTERFACE_VERSION;

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

        pProps->iSnsDiscoverySettable = discoveryProps.iSNSDiscoverySettable;
        pProps->iSnsDiscoveryEnabled = discoveryProps.iSNSDiscoveryEnabled;
        /*
         * Set the iSNS discovery method - The IMA specification indicates
         * this field is valid only if iSNS discovery is enabled.
         */
        if (pProps->iSnsDiscoveryEnabled == IMA_TRUE) {
                switch (discoveryProps.iSNSDiscoveryMethod) {
                        case iSNSDiscoveryMethodStatic:
                                pProps->iSnsDiscoveryMethod =
                                    IMA_ISNS_DISCOVERY_METHOD_STATIC;
                                break;
                        case iSNSDiscoveryMethodDHCP:
                                pProps->iSnsDiscoveryMethod =
                                    IMA_ISNS_DISCOVERY_METHOD_DHCP;
                                break;
                        case iSNSDiscoveryMethodSLP:
                                pProps->iSnsDiscoveryMethod =
                                    IMA_ISNS_DISCOVERY_METHOD_SLP;
                                break;
                        default:
                                (void) close(fd);
                                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }
        (void) memcpy(pProps->iSnsHost.id.hostname,
            discoveryProps.iSNSDomainName,
            sizeof (pProps->iSnsHost.id.hostname));
        pProps->slpDiscoverySettable = discoveryProps.SLPDiscoverySettable;
        pProps->slpDiscoveryEnabled = discoveryProps.SLPDiscoveryEnabled;
        pProps->staticDiscoverySettable =
            discoveryProps.StaticDiscoverySettable;
        pProps->staticDiscoveryEnabled = discoveryProps.StaticDiscoveryEnabled;
        pProps->sendTargetsDiscoverySettable =
            discoveryProps.SendTargetsDiscoverySettable;
        pProps->sendTargetsDiscoveryEnabled =
            discoveryProps.SendTargetsDiscoveryEnabled;

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

IMA_API IMA_STATUS IMA_FreeMemory(
        void *pMemory
)
{
        if (pMemory != NULL)
                free(pMemory);
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_GetNonSharedNodeOidList(
                IMA_OID_LIST **ppList
)
{
        if (ppList == NULL)
                return (IMA_ERROR_INVALID_PARAMETER);

        *ppList = (IMA_OID_LIST*) calloc(1, sizeof (IMA_OID_LIST));
        if (*ppList == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppList)->oidCount = 0;

        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_GetFirstBurstLengthProperties(
                IMA_OID Oid,
                IMA_MIN_MAX_VALUE *pProps
)
{
        return (getISCSINodeParameter(MIN_MAX_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH));
}

IMA_API IMA_STATUS IMA_GetMaxBurstLengthProperties(
                IMA_OID Oid,
                IMA_MIN_MAX_VALUE *pProps
)
{
        return (getISCSINodeParameter(MIN_MAX_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH));
}

IMA_API IMA_STATUS IMA_GetMaxRecvDataSegmentLengthProperties(
                IMA_OID Oid,
                IMA_MIN_MAX_VALUE *pProps
)
{
        return (getISCSINodeParameter(MIN_MAX_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH));
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_PluginIOCtl(
                IMA_OID pluginOid,
                IMA_UINT command,
                const void *pInputBuffer,
                IMA_UINT inputBufferLength,
                void *pOutputBuffer,
                IMA_UINT *pOutputBufferLength
)
{
        return (IMA_ERROR_NOT_SUPPORTED);
}

IMA_API IMA_STATUS IMA_SetFirstBurstLength(
                IMA_OID lhbaId,
                IMA_UINT firstBurstLength
)
{
        IMA_MIN_MAX_VALUE mv;

        mv.currentValue = firstBurstLength;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, &mv,
            ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH));
}

IMA_API IMA_STATUS IMA_SetMaxBurstLength(
                IMA_OID lhbaId,
                IMA_UINT maxBurstLength
)
{
        IMA_MIN_MAX_VALUE mv;

        mv.currentValue = maxBurstLength;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, &mv,
            ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH));
}

IMA_API IMA_STATUS IMA_SetMaxRecvDataSegmentLength(
                IMA_OID lhbaId,
                IMA_UINT maxRecvDataSegmentLength
)
{
        IMA_MIN_MAX_VALUE mv;

        mv.currentValue = maxRecvDataSegmentLength;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, &mv,
            ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH));
}

IMA_API IMA_STATUS IMA_GetMaxConnectionsProperties(
                IMA_OID Oid,
                IMA_MIN_MAX_VALUE *pProps
)
{
        return (getISCSINodeParameter(MIN_MAX_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_MAX_CONNECTIONS));
}

IMA_API IMA_STATUS IMA_SetMaxConnections(
                IMA_OID lhbaId,
                IMA_UINT maxConnections
)
{
        IMA_MIN_MAX_VALUE mv;

        mv.currentValue = maxConnections;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, &mv,
            ISCSI_LOGIN_PARAM_MAX_CONNECTIONS));
}

IMA_API IMA_STATUS IMA_GetDefaultTime2RetainProperties(
                IMA_OID lhbaId,
                IMA_MIN_MAX_VALUE *pProps
)
{
        return (getISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, pProps,
            ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN));
}

IMA_API IMA_STATUS IMA_SetDefaultTime2Retain(
                IMA_OID lhbaId,
                IMA_UINT defaultTime2Retain
)
{
        IMA_MIN_MAX_VALUE mv;

        mv.currentValue = defaultTime2Retain;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, &mv,
            ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN));
}

IMA_API IMA_STATUS IMA_GetDefaultTime2WaitProperties(
                IMA_OID lhbaId,
                IMA_MIN_MAX_VALUE *pProps
)
{
        return (getISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, pProps,
            ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT));
}

IMA_API IMA_STATUS IMA_SetDefaultTime2Wait(
                IMA_OID lhbaId,
                IMA_UINT defaultTime2Wait
)
{
        IMA_MIN_MAX_VALUE mv;

        mv.currentValue = defaultTime2Wait;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, &mv,
            ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT));
}

IMA_API IMA_STATUS IMA_GetMaxOutstandingR2TProperties(
                IMA_OID Oid,
                IMA_MIN_MAX_VALUE *pProps
)
{
        return (getISCSINodeParameter(MIN_MAX_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_OUTSTANDING_R2T));
}

IMA_API IMA_STATUS IMA_SetMaxOutstandingR2T(
                IMA_OID lhbaId,
                IMA_UINT maxOutstandingR2T
)
{
        IMA_MIN_MAX_VALUE mv;

        mv.currentValue = maxOutstandingR2T;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &lhbaId, &mv,
            ISCSI_LOGIN_PARAM_OUTSTANDING_R2T));
}


IMA_API IMA_STATUS IMA_GetErrorRecoveryLevelProperties(
                IMA_OID Oid,
                IMA_MIN_MAX_VALUE *pProps
)
{
        return (getISCSINodeParameter(MIN_MAX_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL));
}

IMA_API IMA_STATUS IMA_SetErrorRecoveryLevel(
                IMA_OID Oid,
                IMA_UINT errorRecoveryLevel
)
{
        IMA_MIN_MAX_VALUE mv;

        mv.currentValue = errorRecoveryLevel;
        return (setISCSINodeParameter(MIN_MAX_PARAM, &Oid, &mv,
            ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL));
}

IMA_API IMA_STATUS IMA_GetInitialR2TProperties(
                IMA_OID Oid,
                IMA_BOOL_VALUE *pProps
)
{
        return (getISCSINodeParameter(BOOL_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_INITIAL_R2T));
}

IMA_API IMA_STATUS IMA_SetInitialR2T(
                IMA_OID Oid,
                IMA_BOOL initialR2T
)
{
        IMA_BOOL_VALUE bv;

        bv.currentValue = initialR2T;
        return (setISCSINodeParameter(BOOL_PARAM, &Oid, &bv,
            ISCSI_LOGIN_PARAM_INITIAL_R2T));
}


IMA_API IMA_STATUS IMA_GetImmediateDataProperties(
                IMA_OID Oid,
                IMA_BOOL_VALUE *pProps
)
{
        return (getISCSINodeParameter(BOOL_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_IMMEDIATE_DATA));
}

IMA_API IMA_STATUS IMA_SetImmediateData(
                IMA_OID Oid,
                IMA_BOOL immediateData
)
{
        IMA_BOOL_VALUE bv;

        bv.currentValue = immediateData;
        return (setISCSINodeParameter(BOOL_PARAM, &Oid, &bv,
            ISCSI_LOGIN_PARAM_IMMEDIATE_DATA));
}

IMA_API IMA_STATUS IMA_GetDataPduInOrderProperties(
                IMA_OID Oid,
                IMA_BOOL_VALUE *pProps
)
{
        return (getISCSINodeParameter(BOOL_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER));
}

IMA_API IMA_STATUS IMA_SetDataPduInOrder(
                IMA_OID Oid,
                IMA_BOOL dataPduInOrder
)
{
        IMA_BOOL_VALUE bv;

        bv.currentValue = dataPduInOrder;
        return (setISCSINodeParameter(BOOL_PARAM, &Oid, &bv,
            ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER));
}

IMA_API IMA_STATUS IMA_GetDataSequenceInOrderProperties(
                IMA_OID Oid,
                IMA_BOOL_VALUE *pProps
)
{
        return (getISCSINodeParameter(BOOL_PARAM, &Oid, pProps,
            ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER));
}

IMA_API IMA_STATUS IMA_SetDataSequenceInOrder(
                IMA_OID Oid,
                IMA_BOOL dataSequenceInOrder
)
{
        IMA_BOOL_VALUE bv;

        bv.currentValue = dataSequenceInOrder;
        return (setISCSINodeParameter(BOOL_PARAM, &Oid, &bv,
            ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER));
}


/*ARGSUSED*/
IMA_API IMA_STATUS IMA_SetStatisticsCollection(
                IMA_OID Oid,
                IMA_BOOL enableStatisticsCollection
)
{
        return (IMA_ERROR_NOT_SUPPORTED);
}


/*ARGSUSED*/
IMA_API IMA_STATUS IMA_GetDiscoveryAddressOidList(
                IMA_OID Oid,
                IMA_OID_LIST **ppList
)
{
        int fd, i, addr_list_size;
        iscsi_addr_list_t *idlp, al_info;

        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(&al_info, 0, sizeof (al_info));
        al_info.al_vers = ISCSI_INTERFACE_VERSION;
        al_info.al_in_cnt = 0;

        /*
         * Issue ioctl to obtain the number of targets.
         */
        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);
        }

        addr_list_size = sizeof (iscsi_addr_list_t);
        if (al_info.al_out_cnt > 1) {
                addr_list_size += (sizeof (iscsi_addr_list_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 again to obtain the OIDs. */
        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, idlp) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TARGET_OID_LIST_GET ioctl %d failed, errno: %d",
                    ISCSI_DISCOVERY_ADDR_LIST_GET, errno);
                free(idlp);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        *ppList = (IMA_OID_LIST *)calloc(1, sizeof (IMA_OID_LIST) +
            idlp->al_out_cnt * sizeof (IMA_OID));
        if (*ppList == NULL) {
                free(idlp);
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppList)->oidCount = idlp->al_out_cnt;

        for (i = 0; i < idlp->al_out_cnt; i++) {
                (*ppList)->oids[i].objectType =
                    IMA_OBJECT_TYPE_DISCOVERY_ADDRESS;
                (*ppList)->oids[i].ownerId = pluginOwnerId;
                (*ppList)->oids[i].objectSequenceNumber =
                    idlp->al_addrs[i].a_oid;
        }

        free(idlp);
        (void) close(fd);

        return (IMA_STATUS_SUCCESS);
}


/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetStaticDiscoveryTargetOidList(
                IMA_OID Oid,
                IMA_OID_LIST **ppList
)
{
        if (Oid.objectType == IMA_OBJECT_TYPE_PNP) {
                return (IMA_ERROR_OBJECT_NOT_FOUND);
        }

        return (get_target_oid_list(ISCSI_STATIC_TGT_OID_LIST, ppList));
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetTargetOidList(
                IMA_OID Oid,
                IMA_OID_LIST **ppList
)
{
        return (get_target_oid_list(ISCSI_TGT_PARAM_OID_LIST, ppList));
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_SetIsnsDiscovery(
                IMA_OID phbaId,
                IMA_BOOL enableIsnsDiscovery,
                IMA_ISNS_DISCOVERY_METHOD discoveryMethod,
                const IMA_HOST_ID *iSnsHost
)
{
        /* XXX need to set discovery Method and domaineName */
        return (configure_discovery_method(enableIsnsDiscovery,
            iSCSIDiscoveryMethodISNS));
}


/* ARGSUSED */
IMA_API IMA_STATUS IMA_SetSlpDiscovery(
                IMA_OID phbaId,
                IMA_BOOL enableSlpDiscovery
)
{
        return (configure_discovery_method(enableSlpDiscovery,
            iSCSIDiscoveryMethodSLP));
}


/* ARGSUSED */
IMA_API IMA_STATUS IMA_SetStaticDiscovery(
                IMA_OID phbaId,
                IMA_BOOL enableStaticDiscovery
)
{
        return (configure_discovery_method(enableStaticDiscovery,
            iSCSIDiscoveryMethodStatic));
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_SetSendTargetsDiscovery(
                IMA_OID phbaId,
                IMA_BOOL enableSendTargetsDiscovery
)
{
        return (configure_discovery_method(enableSendTargetsDiscovery,
            iSCSIDiscoveryMethodSendTargets));
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_RemoveDiscoveryAddress(
                IMA_OID discoveryAddressOid
)
{
        int status, fd, i, addr_list_size;
        iscsi_addr_list_t *idlp, al_info;
        iscsi_addr_t *matched_addr = NULL;
        entry_t entry;

        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(&al_info, 0, sizeof (al_info));
        al_info.al_vers = ISCSI_INTERFACE_VERSION;
        al_info.al_in_cnt = 0;

        /*
         * Issue ioctl to obtain 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) {
                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_list_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 again to obtain the OIDs. */
        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, idlp) != 0) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TARGET_OID_LIST_GET ioctl %d failed, errno: %d",
                    ISCSI_DISCOVERY_ADDR_LIST_GET, errno);
                free(idlp);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        for (i = 0; i < idlp->al_out_cnt; i++) {
                if (discoveryAddressOid.objectSequenceNumber !=
                    idlp->al_addrs[i].a_oid)
                        continue;
                matched_addr = &(idlp->al_addrs[i]);
        }

        if (matched_addr == NULL) {
                return (IMA_ERROR_OBJECT_NOT_FOUND);
        }


        (void) memset(&entry, 0, sizeof (entry_t));
        entry.e_vers = ISCSI_INTERFACE_VERSION;
        entry.e_oid  = discoveryAddressOid.objectSequenceNumber;
        if (matched_addr->a_addr.i_insize == sizeof (struct in_addr)) {
                bcopy(&matched_addr->a_addr.i_addr.in4,
                    &entry.e_u.u_in4, sizeof (entry.e_u.u_in4));
                entry.e_insize = sizeof (struct in_addr);
        } else if (matched_addr->a_addr.i_insize == sizeof (struct in6_addr)) {
                bcopy(&matched_addr->a_addr.i_addr.in6,
                    &entry.e_u.u_in6, sizeof (entry.e_u.u_in6));
                entry.e_insize = sizeof (struct in6_addr);
        } else {
                /* Should not happen */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_STATIC_GET returned bad address");
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        entry.e_port = matched_addr->a_port;
        entry.e_tpgt = 0;
        entry.e_oid = discoveryAddressOid.objectSequenceNumber;

        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);
                }
        }

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


/*ARGSUSED*/
IMA_API IMA_STATUS IMA_AddDiscoveryAddress(
                IMA_OID oid,
                const IMA_TARGET_ADDRESS discoveryAddress,
                IMA_OID *pDiscoveryAddressOid
)
{
        entry_t     entry;
        int         fd;

        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);
        }

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

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

        pDiscoveryAddressOid->ownerId = pluginOwnerId;
        pDiscoveryAddressOid->objectType = IMA_OBJECT_TYPE_DISCOVERY_ADDRESS;
        pDiscoveryAddressOid->objectSequenceNumber = entry.e_oid;

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

IMA_API IMA_STATUS IMA_GetStaticDiscoveryTargetProperties(
                IMA_OID staticTargetOid,
                IMA_STATIC_DISCOVERY_TARGET_PROPERTIES *pProps
)
{
        char static_target_addr_str[SUN_IMA_IP_ADDRESS_LEN];
        char static_target_addr_port_str[SUN_IMA_IP_ADDRESS_LEN];
        int af, fd, status;
        iscsi_static_property_t prop;
        /* LINTED */
        IMA_HOST_ID *host;

        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(&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 */
                af = AF_INET;
        } else if (prop.p_addr_list.al_addrs[0].a_addr.i_insize ==
            sizeof (struct in6_addr)) {
                /* IPv6 */
                af = AF_INET6;
        } else {
                /* Should not happen */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_STATIC_GET returned bad address");
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        if (inet_ntop(af, &prop.p_addr_list.al_addrs[0].a_addr.i_addr,
            static_target_addr_str, sizeof (static_target_addr_str)) == NULL) {
                /* Should not happen */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_STATIC_GET returned address that cannot "
                    "be inet_ntop");
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        } else {
                if (af == AF_INET) {
                        (void) snprintf(static_target_addr_port_str,
                            SUN_IMA_IP_ADDRESS_LEN,
                            "%s:%ld",
                            static_target_addr_str,
                            prop.p_addr_list.al_addrs[0].a_port);
                } else {
                        (void) snprintf(static_target_addr_port_str,
                            SUN_IMA_IP_ADDRESS_LEN,
                            "[%s]:%ld",
                            static_target_addr_str,
                            prop.p_addr_list.al_addrs[0].a_port);
                }
                host = &pProps->staticTarget.targetAddress.hostnameIpAddress;
                (void) mbstowcs(pProps->staticTarget.
                    targetAddress.hostnameIpAddress.
                    id.hostname, static_target_addr_port_str,
                    sizeof (host->id.hostname) / sizeof (IMA_WCHAR));
        }

        return (IMA_STATUS_SUCCESS);
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_GetDiscoveryAddressProperties(
                IMA_OID discoveryAddressOid,
                IMA_DISCOVERY_ADDRESS_PROPERTIES *pProps
)
{
        int fd;
        int i;
        int addr_list_size;
        iscsi_addr_list_t *idlp, al_info;
        iscsi_addr_t *matched_addr = NULL;
        /* LINTED */
        IMA_TARGET_ADDRESS *addr;

        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(&al_info, 0, sizeof (al_info));
        al_info.al_vers = ISCSI_INTERFACE_VERSION;
        al_info.al_in_cnt = 0;

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

        if (al_info.al_out_cnt == 0) {
                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_list_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 again to obtain the OIDs. */
        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, idlp) != 0) {
                free(idlp);
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TARGET_OID_LIST_GET ioctl %d failed, errno: %d",
                    ISCSI_DISCOVERY_ADDR_LIST_GET, errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        for (i = 0; i < idlp->al_out_cnt; i++) {
                if (discoveryAddressOid.objectSequenceNumber !=
                    idlp->al_addrs[i].a_oid)
                        continue;
                matched_addr = &(idlp->al_addrs[i]);
        }

        if (matched_addr == NULL) {
                return (IMA_ERROR_OBJECT_NOT_FOUND);
        }

        if (matched_addr->a_addr.i_insize == sizeof (struct in_addr)) {
                pProps->discoveryAddress.hostnameIpAddress.id.
                    ipAddress.ipv4Address = IMA_TRUE;
        } else if (matched_addr->a_addr.i_insize == sizeof (struct in6_addr)) {
                pProps->discoveryAddress.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);
        }

        addr = &pProps->discoveryAddress;
        bcopy(&(matched_addr->a_addr.i_addr), pProps->discoveryAddress.
            hostnameIpAddress.id.ipAddress.ipAddress,
            sizeof (addr->hostnameIpAddress.id.ipAddress.ipAddress));

        pProps->discoveryAddress.portNumber = matched_addr->a_port;

        pProps->associatedLhbaOid.objectType = IMA_OBJECT_TYPE_LHBA;
        pProps->associatedLhbaOid.ownerId = pluginOwnerId;
        pProps->associatedLhbaOid.objectSequenceNumber = ISCSI_INITIATOR_OID;

        free(idlp);
        (void) close(fd);

        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_RemoveStaticDiscoveryTarget(
                IMA_OID staticTargetOid
)
{
        entry_t entry;
        int     status, fd;

        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(&entry, 0, sizeof (entry_t));
        entry.e_vers = ISCSI_INTERFACE_VERSION;
        entry.e_oid = (uint32_t)staticTargetOid.objectSequenceNumber;

        if (ioctl(fd, ISCSI_STATIC_CLEAR, &entry)) {
                status = errno;
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_STATIC_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);
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_AddStaticDiscoveryTarget(
                IMA_OID lhbaOid,
                const IMA_STATIC_DISCOVERY_TARGET staticConfig,
                IMA_OID *pTargetOid
)
{
        char                    tmp_target_str[SUN_IMA_IP_ADDRESS_LEN];
        char                    target_addr_str[SUN_IMA_IP_ADDRESS_LEN];
        char                    target_port_str[SUN_IMA_IP_PORT_LEN];
        iscsi_target_entry_t    target;
        int                     fd;
        int                     target_in_addr_size;
        int                     target_port;
        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.
         */
        (void) wcstombs(tmp_target_str,
            staticConfig.targetAddress.hostnameIpAddress.
            id.hostname, sizeof (tmp_target_str));

        if (tmp_target_str[0] == '[') {
                /* IPv6 address */
                char *closeBracketPos;
                closeBracketPos = strchr(tmp_target_str, ']');
                if (!closeBracketPos) {
                        return (IMA_ERROR_INVALID_PARAMETER);
                }

                *closeBracketPos = '\0';
                (void) strlcpy(target_addr_str, &tmp_target_str[1],
                    sizeof (target_addr_str));

                if (inet_pton(AF_INET6, target_addr_str,
                    &target_in.u_in6) != 1) {
                        return (IMA_ERROR_INVALID_PARAMETER);
                }
                target_in_addr_size = sizeof (struct in6_addr);

                /* Extract the port number */
                closeBracketPos++;
                if (*closeBracketPos == ':') {
                        closeBracketPos++;

                        if (*closeBracketPos != '\0') {
                                (void) strlcpy(target_port_str, closeBracketPos,
                                    sizeof (target_port_str));
                                target_port = atoi(target_port_str);
                        } else {
                                target_port = ISCSI_LISTEN_PORT;
                        }
                } else {
                        /* No port number specified; use default port */
                        target_port = ISCSI_LISTEN_PORT;
                }
        } else {
                /* IPv4 address */
                char *colonPos;
                colonPos = strchr(tmp_target_str, ':');
                if (!colonPos) {
                        /* No port number specified; use default port */
                        target_port = ISCSI_LISTEN_PORT;
                        (void) strlcpy(target_addr_str, tmp_target_str,
                            sizeof (target_addr_str));
                } else {
                        *colonPos = '\0';
                        (void) strlcpy(target_addr_str, tmp_target_str,
                            sizeof (target_addr_str));
                        /* Extract the port number */
                        colonPos++;
                        if (*colonPos != '\0') {
                                (void) strlcpy(target_port_str, colonPos,
                                    sizeof (target_port_str));
                                target_port = atoi(target_port_str);
                        } else {
                                target_port = ISCSI_LISTEN_PORT;
                        }
                }

                if (inet_pton(AF_INET, target_addr_str,
                    &target_in.u_in4) != 1) {
                        return (IMA_ERROR_INVALID_PARAMETER);
                }

                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;
        target.te_entry.e_tpgt = ISCSI_DEFAULT_TPGT;

        (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 {
                /* Should not happen */
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_STATIC_GET returned bad address");
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        target.te_entry.e_port = target_port;

        /* No target portal group specified. Default to -1. */
        target.te_entry.e_tpgt = ISCSI_DEFAULT_TPGT;

        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);
        }

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

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

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

IMA_API IMA_STATUS IMA_GetTargetProperties(
                IMA_OID targetId,
                IMA_TARGET_PROPERTIES *pProps
)
{
        return (getTargetProperties(targetId, pProps));
}

static IMA_STATUS getTargetProperties(
                IMA_OID targetId,
                IMA_TARGET_PROPERTIES *pProps
)
{
        int                 fd;
        iscsi_property_t    prop;

        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(&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) {
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TARGET_PROPS_GET ioctl failed, errno: %d", errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

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

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

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

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

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

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

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_GetTargetErrorStatistics(
                IMA_OID targetId,
                IMA_TARGET_ERROR_STATISTICS *pStats
)
{
        return (IMA_ERROR_NOT_SUPPORTED);
}

IMA_API IMA_STATUS IMA_GetLuOidList(
                IMA_OID oid,
                IMA_OID_LIST **ppList
)
{
        IMA_STATUS              status;
        int                     i;
        iscsi_lun_list_t        *pLunList;

        if (oid.objectType == IMA_OBJECT_TYPE_LHBA) {
                status = get_target_lun_oid_list(NULL, &pLunList);
        } else {
                status = get_target_lun_oid_list(&oid, &pLunList);
        }

        if (!IMA_SUCCESS(status)) {
                return (status);
        }

        *ppList = (IMA_OID_LIST *) calloc(1, (sizeof (IMA_OID_LIST) +
            (pLunList->ll_out_cnt * sizeof (IMA_OID))));
        if (*ppList == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppList)->oidCount = pLunList->ll_out_cnt;
        for (i = 0; i < pLunList->ll_out_cnt; i++) {
                (*ppList)->oids[i].objectType = IMA_OBJECT_TYPE_LU;
                (*ppList)->oids[i].ownerId = pluginOwnerId;
                (*ppList)->oids[i].objectSequenceNumber =
                    pLunList->ll_luns[i].l_oid;
        }

        free(pLunList);
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_GetLuOid(
                IMA_OID targetId,
                IMA_UINT64 lun,
                IMA_OID *pluId
)
{
        IMA_STATUS              status;
        int                     i;
        iscsi_lun_list_t        *pLunList;

        status = get_target_lun_oid_list(&targetId, &pLunList);
        if (!IMA_SUCCESS(status)) {
                return (status);
        }

        for (i = 0; i < pLunList->ll_out_cnt; i++) {
                if (pLunList->ll_luns[i].l_num == lun) {
                        pluId->objectType = IMA_OBJECT_TYPE_LU;
                        pluId->ownerId = pluginOwnerId;
                        pluId->objectSequenceNumber =
                            pLunList->ll_luns[i].l_oid;
                        free(pLunList);
                        return (IMA_STATUS_SUCCESS);
                }
        }

        free(pLunList);
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

IMA_API IMA_STATUS IMA_GetLuProperties(
                IMA_OID luId,
                IMA_LU_PROPERTIES *pProps
)
{
        return (getLuProperties(luId, pProps));
}

static IMA_STATUS getLuProperties(
                IMA_OID luId,
                IMA_LU_PROPERTIES *pProps
)
{
        IMA_STATUS              status;
        iscsi_lun_list_t        *pLunList;
        int                     j;
        IMA_BOOL                lunMatch = IMA_FALSE;
        int                     fd;
        iscsi_lun_props_t       lun;
        di_devlink_handle_t     hdl;

        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 ((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);
        }

        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->associatedTargetOid.objectType = IMA_OBJECT_TYPE_TARGET;
        pProps->associatedTargetOid.ownerId = pluginOwnerId;
        pProps->associatedTargetOid.objectSequenceNumber = lun.lp_tgt_oid;
        pProps->targetLun = (IMA_UINT64)lun.lp_num;
        pProps->exposedToOs = IMA_TRUE;
        (void) memset(&pProps->timeExposedToOs, 0,
            sizeof (pProps->timeExposedToOs));

        if (lun.lp_status == LunValid) {

                /* add minor device delimiter */
                (void) strcat(lun.lp_pathname, ":");

                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) strcat(lun.lp_pathname, "c,raw");
                }

                /*
                 * 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->osDeviceName[0] = L'\0';
                        (void) di_devlink_walk(hdl, NULL, lun.lp_pathname,
                            DI_PRIMARY_LINK, (void *)pProps->osDeviceName,
                            get_lun_devlink);
                        if (pProps->osDeviceName[0] != L'\0') {
                                /* OS device name synchronously made */
                                pProps->osDeviceNameValid = IMA_TRUE;
                        } else {
                                pProps->osDeviceNameValid = IMA_FALSE;
                        }

                        (void) di_devlink_fini(&hdl);
                } else {
                        pProps->osDeviceNameValid = IMA_FALSE;
                }

        } else {
                pProps->osDeviceNameValid = IMA_FALSE;
        }

        pProps->osParallelIdsValid = IMA_FALSE;

        return (IMA_STATUS_SUCCESS);
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_GetStatisticsProperties(
                IMA_OID oid,
                IMA_STATISTICS_PROPERTIES *pProps
)
{
        return (IMA_ERROR_NOT_SUPPORTED);
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_GetDeviceStatistics(
                IMA_OID luId,
                IMA_DEVICE_STATISTICS *pStats
)
{
        return (IMA_ERROR_NOT_SUPPORTED);
}

IMA_API IMA_STATUS IMA_LuInquiry(
        IMA_OID deviceId,
        IMA_BOOL evpd,
        IMA_BOOL cmddt,
        IMA_BYTE pageCode,
        IMA_BYTE *pOutputBuffer,
        IMA_UINT *pOutputBufferLength,
        IMA_BYTE *pSenseBuffer,
        IMA_UINT *pSenseBufferLength
)
{
        IMA_LU_PROPERTIES luProps;
        IMA_STATUS status;
        unsigned char cmdblk[CDB_GROUP0];
        IMA_UINT buflen;
        int fd;
        iscsi_uscsi_t uscsi;

        (void) memset(&cmdblk[0], 0, CDB_GROUP0);
        cmdblk[0] = SCMD_INQUIRY;

        if (evpd == IMA_TRUE)
                cmdblk[1] |= 0x01;
        if (cmddt == IMA_TRUE)
                cmdblk[1] |= 0x02;

        cmdblk[2] = pageCode;

        if (*pOutputBufferLength > MAX_INQUIRY_BUFFER_LEN) {
                buflen = MAX_INQUIRY_BUFFER_LEN;
        } else {
                buflen = *pOutputBufferLength;
        }
        cmdblk[3] = (buflen & 0xff00) >> 8;
        cmdblk[4] = (buflen & 0x00ff);

        (void) memset(&uscsi, 0, sizeof (iscsi_uscsi_t));
        uscsi.iu_vers = ISCSI_INTERFACE_VERSION;

        /* iu_oid is a session oid in the driver */
        if (deviceId.objectType == IMA_OBJECT_TYPE_TARGET) {
                uscsi.iu_oid    = deviceId.objectSequenceNumber;
                uscsi.iu_lun    = 0;
        } else {
                /*
                 * Get LU properties and associated session oid
                 * for this lun(deviceId) and put in uscsi.iu_oid
                 */
                status = getLuProperties(deviceId, &luProps);
                if (status != IMA_STATUS_SUCCESS) {
                        return (status);
                }
                uscsi.iu_oid = (uint32_t)luProps.associatedTargetOid.
                    objectSequenceNumber;
                uscsi.iu_lun = luProps.targetLun;
        }

        uscsi.iu_ucmd.uscsi_flags = USCSI_READ;
        uscsi.iu_ucmd.uscsi_timeout = USCSI_TIMEOUT_IN_SEC;
        uscsi.iu_ucmd.uscsi_bufaddr = (char *)pOutputBuffer;
        uscsi.iu_ucmd.uscsi_buflen = buflen;
        uscsi.iu_ucmd.uscsi_rqbuf = (char *)pSenseBuffer;
        uscsi.iu_ucmd.uscsi_rqlen = (pSenseBufferLength != NULL) ?
            *pSenseBufferLength : 0;
        uscsi.iu_ucmd.uscsi_cdb = (char *)&cmdblk[0];
        uscsi.iu_ucmd.uscsi_cdblen = CDB_GROUP0;

        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);
        }

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

        if (uscsi.iu_ucmd.uscsi_status == STATUS_CHECK) {
                if (pSenseBufferLength != NULL) {
                        *pSenseBufferLength -= uscsi.iu_ucmd.uscsi_rqresid;
                }
                return (IMA_ERROR_SCSI_STATUS_CHECK_CONDITION);
        }

        *pOutputBufferLength = buflen - uscsi.iu_ucmd.uscsi_resid;
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_LuReadCapacity(
                IMA_OID deviceId,
                IMA_UINT cdbLength,
                IMA_BYTE *pOutputBuffer,
                IMA_UINT *pOutputBufferLength,

                IMA_BYTE *pSenseBuffer,
                IMA_UINT *pSenseBufferLength
)
{
        IMA_LU_PROPERTIES luProps;
        IMA_STATUS status;
        /* CDB_GROUP4 size is safe for both 10 and 16 byte CDBs */
        unsigned char cmdblk[CDB_GROUP4];
        IMA_UINT buflen;
        int fd;
        iscsi_uscsi_t uscsi;

        (void) memset(&cmdblk[0], 0, CDB_GROUP4);

        if (cdbLength == CDB_GROUP1) {
                /* Read Capacity (10) command. */
                cmdblk[0] = SCMD_READ_CAPACITY;
                buflen = *pOutputBufferLength;
        } else if (cdbLength == CDB_GROUP4) {
                /*
                 * Read Capacity (16) is a Service Action In command. One
                 * command byte (0x9E) is overloaded for multiple operations,
                 * with the second CDB byte specifying the desired operation.
                 */
                cmdblk[0] = SCMD_SVC_ACTION_IN_G4;
                cmdblk[1] = SSVC_ACTION_READ_CAPACITY_G4;

                if (*pOutputBufferLength > MAX_READ_CAPACITY16_BUFFER_LEN) {
                        buflen = MAX_READ_CAPACITY16_BUFFER_LEN;
                } else {
                        buflen = *pOutputBufferLength;
                }
                cmdblk[10] = (buflen & 0xff000000) >> 24;
                cmdblk[11] = (buflen & 0x00ff0000) >> 16;
                cmdblk[12] = (buflen & 0x0000ff00) >> 8;
                cmdblk[13] = (buflen & 0x000000ff);
        } else {
                /* only 10 and 16 byte CDB are supported */
                return (IMA_ERROR_NOT_SUPPORTED);
        }

        (void) memset(&uscsi, 0, sizeof (iscsi_uscsi_t));
        uscsi.iu_vers = ISCSI_INTERFACE_VERSION;

        /* iu_oid is a session oid in the driver */
        if (deviceId.objectType == IMA_OBJECT_TYPE_TARGET) {
                uscsi.iu_oid    = deviceId.objectSequenceNumber;
                uscsi.iu_lun    = 0;
        } else {
                /*
                 * Get LU properties and associated session oid
                 * for this lun(deviceId) and put in uscsi.iu_oid
                 */
                status = getLuProperties(deviceId, &luProps);
                if (status != IMA_STATUS_SUCCESS) {
                        return (status);
                }
                uscsi.iu_oid = (uint32_t)luProps.associatedTargetOid.
                    objectSequenceNumber;
                uscsi.iu_lun = luProps.targetLun;
        }

        uscsi.iu_ucmd.uscsi_flags = USCSI_READ;
        uscsi.iu_ucmd.uscsi_timeout = USCSI_TIMEOUT_IN_SEC;
        uscsi.iu_ucmd.uscsi_bufaddr = (char *)pOutputBuffer;
        uscsi.iu_ucmd.uscsi_buflen = buflen;
        uscsi.iu_ucmd.uscsi_rqbuf = (char *)pSenseBuffer;
        uscsi.iu_ucmd.uscsi_rqlen = (pSenseBufferLength != NULL) ?
            *pSenseBufferLength : 0;
        uscsi.iu_ucmd.uscsi_cdb = (char *)&cmdblk[0];
        uscsi.iu_ucmd.uscsi_cdblen = cdbLength;

        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);
        }

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

        if (uscsi.iu_ucmd.uscsi_status == STATUS_CHECK) {
                if (pSenseBufferLength != NULL) {
                        *pSenseBufferLength -= uscsi.iu_ucmd.uscsi_rqresid;
                }
                return (IMA_ERROR_SCSI_STATUS_CHECK_CONDITION);
        }

        *pOutputBufferLength = buflen - uscsi.iu_ucmd.uscsi_resid;
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_LuReportLuns(
                IMA_OID deviceId,
                IMA_BOOL sendToWellKnownLun,
                IMA_BYTE selectReport,

                IMA_BYTE *pOutputBuffer,
                IMA_UINT *pOutputBufferLength,

                IMA_BYTE *pSenseBuffer,
                IMA_UINT *pSenseBufferLength
)
{
        IMA_LU_PROPERTIES luProps;
        IMA_STATUS status;
        unsigned char cmdblk[CDB_GROUP5];
        IMA_UINT buflen;
        int fd;
        iscsi_uscsi_t uscsi;

        (void) memset(&cmdblk[0], 0, CDB_GROUP5);
        cmdblk[0] = SCMD_REPORT_LUNS;
        cmdblk[2] = selectReport;

        if (*pOutputBufferLength > MAX_REPORT_LUNS_BUFFER_LEN) {
                buflen = MAX_REPORT_LUNS_BUFFER_LEN;
        } else {
                buflen = *pOutputBufferLength;
        }
        cmdblk[6] = (buflen & 0xff000000) >> 24;
        cmdblk[7] = (buflen & 0x00ff0000) >> 16;
        cmdblk[8] = (buflen & 0x0000ff00) >> 8;
        cmdblk[9] = (buflen & 0x000000ff);

        (void) memset(&uscsi, 0, sizeof (iscsi_uscsi_t));
        uscsi.iu_vers = ISCSI_INTERFACE_VERSION;

        /* iu_oid is a session oid in the driver */
        if (deviceId.objectType == IMA_OBJECT_TYPE_TARGET) {
                if (sendToWellKnownLun == IMA_TRUE) {
                        /* this optional feature is not supported now */
                        return (IMA_ERROR_NOT_SUPPORTED);
                }
                uscsi.iu_oid    = deviceId.objectSequenceNumber;
                uscsi.iu_lun    = 0;
        } else {
                /*
                 * Get LU properties and associated session oid
                 * for this lun(deviceId) and put in uscsi.iu_oid
                 */
                status = getLuProperties(deviceId, &luProps);
                if (status != IMA_STATUS_SUCCESS) {
                        return (status);
                }
                uscsi.iu_oid = (uint32_t)luProps.associatedTargetOid.
                    objectSequenceNumber;
                uscsi.iu_lun = luProps.targetLun;
        }

        uscsi.iu_ucmd.uscsi_flags = USCSI_READ;
        uscsi.iu_ucmd.uscsi_timeout = USCSI_TIMEOUT_IN_SEC;
        uscsi.iu_ucmd.uscsi_bufaddr = (char *)pOutputBuffer;
        uscsi.iu_ucmd.uscsi_buflen = buflen;
        uscsi.iu_ucmd.uscsi_rqbuf = (char *)pSenseBuffer;
        uscsi.iu_ucmd.uscsi_rqlen = (pSenseBufferLength != NULL) ?
            *pSenseBufferLength : 0;
        uscsi.iu_ucmd.uscsi_cdb = (char *)&cmdblk[0];
        uscsi.iu_ucmd.uscsi_cdblen = CDB_GROUP5;

        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);
        }

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

        if (uscsi.iu_ucmd.uscsi_status == STATUS_CHECK) {
                if (pSenseBufferLength != NULL) {
                        *pSenseBufferLength -= uscsi.iu_ucmd.uscsi_rqresid;
                }
                return (IMA_ERROR_SCSI_STATUS_CHECK_CONDITION);
        }

        *pOutputBufferLength = buflen - uscsi.iu_ucmd.uscsi_resid;
        return (IMA_STATUS_SUCCESS);
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_ExposeLu(
                IMA_OID luId
)
{
        return (IMA_ERROR_NOT_SUPPORTED);
}

/*ARGSUSED*/
IMA_API IMA_STATUS IMA_UnexposeLu(
                IMA_OID luId
)
{
        return (IMA_ERROR_NOT_SUPPORTED);
}

IMA_API IMA_STATUS IMA_GetAddressKeys(
                IMA_OID targetOid,
                IMA_ADDRESS_KEYS **ppKeys
)
{
        IMA_STATUS status;
        IMA_TARGET_PROPERTIES targetProps;
        SUN_IMA_DISC_ADDR_PROP_LIST *discAddressList;
        SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES *pList;
        int i, j, addressKeyCount = 0;
        int addressKeyIdx = 0;

        status = getTargetProperties(targetOid, &targetProps);
        if (status != IMA_STATUS_SUCCESS) {
                return (status);
        }

        status = getDiscoveryAddressPropertiesList(&discAddressList);
        if (status != IMA_STATUS_SUCCESS) {
                return (status);
        }

        /* Get the number of addresses to allocate */
        for (i = 0; i < discAddressList->discAddrCount; i++) {
                (void) sendTargets(discAddressList->props[i].discoveryAddress,
                    &pList);
                for (j = 0; j < pList->keyCount; j++) {
                        if (wcsncmp(pList->keys[j].name, targetProps.name,
                            wslen(pList->keys[j].name)) == 0) {
                                addressKeyCount++;
                        }
                }
                (void) IMA_FreeMemory(pList);
        }

        *ppKeys = (IMA_ADDRESS_KEYS *)calloc(1, sizeof (IMA_ADDRESS_KEYS) +
            addressKeyCount * sizeof (IMA_ADDRESS_KEY));
        if (*ppKeys == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppKeys)->addressKeyCount = addressKeyCount;
        addressKeyIdx = 0;

        for (i = 0; i < discAddressList->discAddrCount; i++) {
                (void) sendTargets(discAddressList->props[i].discoveryAddress,
                    &pList);
                for (j = 0; j < pList->keyCount; j++) {
                        if (wcsncmp(pList->keys[j].name, targetProps.name,
                            wslen(pList->keys[j].name)) != 0) {
                                continue;
                        }

                        bcopy(&(pList->keys[j].address.ipAddress),
                            &((*ppKeys)->addressKeys[addressKeyIdx].
                            ipAddress), sizeof (IMA_IP_ADDRESS));

                        (*ppKeys)->addressKeys[addressKeyIdx++].portNumber =
                            pList->keys[j].address.portNumber;

                }
                (void) IMA_FreeMemory(pList);
        }
        return (IMA_STATUS_SUCCESS);
}

IMA_BOOL isAuthMethodValid(IMA_OID oid, IMA_AUTHMETHOD method) {
        IMA_STATUS status;
        IMA_AUTHMETHOD supportedList[MAX_AUTHMETHODS];
        IMA_UINT i, supportedCount;
        IMA_BOOL supported;
        status = getSupportedAuthMethods(oid, IMA_FALSE, &supportedCount,
                        supportedList);
        if (status != IMA_STATUS_SUCCESS)
                return (IMA_FALSE);

        supported = IMA_FALSE;
        for (i = 0; i < supportedCount; i++) {
                if (method == supportedList[i]) {
                        supported = IMA_TRUE;
                }
        }

        return (supported);
}

IMA_BOOL isAuthMethodListValid(IMA_OID oid, const IMA_AUTHMETHOD *pMethodList,
                                IMA_UINT methodCount) {
        IMA_UINT i, j;

        if (pMethodList == NULL) {
                return (IMA_FALSE);
        }
        /* Check list for duplicates */
        for (i = 0; i < methodCount; i++) {
                for (j = i + 1; j < methodCount; j++) {
                        if (pMethodList[i] == pMethodList[j]) {
                                return (IMA_FALSE);
                        }
                }

                if (isAuthMethodValid(oid, pMethodList[i]) == IMA_FALSE) {
                        return (IMA_FALSE);
                }
        }
        return (IMA_TRUE);
}

IMA_API IMA_STATUS IMA_GetSupportedAuthMethods(
                IMA_OID lhbaOid,
                IMA_BOOL getSettableMethods,
                IMA_UINT *pMethodCount,
                IMA_AUTHMETHOD *pMethodList
)
{
        return (getSupportedAuthMethods(lhbaOid, getSettableMethods,
            pMethodCount, pMethodList));
}


/*ARGSUSED*/
static IMA_STATUS getSupportedAuthMethods(
                IMA_OID lhbaOid,
                IMA_BOOL getSettableMethods,
                IMA_UINT *pMethodCount,
                IMA_AUTHMETHOD *pMethodList
)
{
        if (pMethodList == NULL) {
                *pMethodCount = 0;
                return (IMA_STATUS_SUCCESS);
        }

        *pMethodCount = NUM_SUPPORTED_AUTH_METHODS;
        if (*pMethodCount > 1) {
                pMethodList[0] = IMA_AUTHMETHOD_NONE;
                pMethodList[1] = IMA_AUTHMETHOD_CHAP;
        }

        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_GetInUseInitiatorAuthMethods(
                IMA_OID         lhbaOid,
                IMA_UINT        *pMethodCount,
                IMA_AUTHMETHOD *pMethodList
)
{
        return (getAuthMethods(lhbaOid, pMethodCount, pMethodList));
}

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

        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)lhbaOid.objectSequenceNumber;

        if (method == IMA_AUTHMETHOD_CHAP) {
                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);
                }
        } else {
                return (IMA_ERROR_INVALID_PARAMETER);
        }

        (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 IMA_SetInitiatorAuthMethods(
                IMA_OID lhbaOid,
                IMA_UINT methodCount,
                const IMA_AUTHMETHOD *pMethodList
)
{
        if (isAuthMethodListValid(lhbaOid, pMethodList,
            methodCount) == IMA_FALSE)
                return (IMA_ERROR_INVALID_PARAMETER);
        return (setAuthMethods(lhbaOid, &methodCount, pMethodList));
}

/*
 * This function only sets CHAP params since we only support CHAP for now.
 */
IMA_API IMA_STATUS IMA_SetInitiatorAuthParms(
                IMA_OID lhbaOid,
                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 (isAuthMethodValid(lhbaOid, method) == IMA_FALSE) {
                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)lhbaOid.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 (method == IMA_AUTHMETHOD_CHAP) {
                if (ioctl(fd, ISCSI_CHAP_SET, &chap_p) != 0) {
                        (void) close(fd);
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_CHAP_SET ioctl failed, errno: %d", errno);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }

        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;
        iscsi_param_get_t   pg;

        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(&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;
        }

        (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;
        iscsi_param_set_t   ps;

        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(&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)) {
                int tmpErrno = errno;
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_PARAM_SET ioctl failed, errno: %d", errno);
                (void) close(fd);
                switch (tmpErrno) {
                        case ENOTSUP :
                                return (IMA_ERROR_NOT_SUPPORTED);
                        default :
                                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }

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

static int
prepare_discovery_entry(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 ((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);
        }

        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)) {
                        (void) close(fd);
                        syslog(LOG_USER|LOG_DEBUG,
                            "ISCSI_DISCOVERY_SET ioctl failed, errno: %d",
                            errno);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }

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

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

        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(&tl_info, 0, sizeof (tl_info));
        tl_info.tl_vers = ISCSI_INTERFACE_VERSION;
        tl_info.tl_in_cnt = 0;
        tl_info.tl_tgt_list_type = targetListType;

        /*
         * Issue ioctl to obtain the number of targets.
         */
        if (ioctl(fd, ISCSI_TARGET_OID_LIST_GET, &tl_info) != 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);
        }

        target_list_size = sizeof (iscsi_target_list_t);
        if (tl_info.tl_out_cnt > 1) {
                target_list_size += (sizeof (uint32_t) *
                    tl_info.tl_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 = tl_info.tl_out_cnt;
        idlp->tl_tgt_list_type = targetListType;

        /* Issue the same ioctl again to obtain the OIDs. */
        if (ioctl(fd, ISCSI_TARGET_OID_LIST_GET, idlp) != 0) {
                free(idlp);
                (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);
        }

        *ppList = (IMA_OID_LIST *)calloc(1, sizeof (IMA_OID_LIST) +
            idlp->tl_out_cnt * sizeof (IMA_OID));
        if (*ppList == NULL) {
                free(idlp);
                (void) close(fd);
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppList)->oidCount = idlp->tl_out_cnt;

        for (i = 0; i < idlp->tl_out_cnt; i++) {

                if (targetListType == ISCSI_STATIC_TGT_OID_LIST)
                        (*ppList)->oids[i].objectType =
                            IMA_OBJECT_TYPE_STATIC_DISCOVERY_TARGET;
                else
                        (*ppList)->oids[i].objectType = IMA_OBJECT_TYPE_TARGET;

                (*ppList)->oids[i].ownerId = pluginOwnerId;
                (*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, ll_info;
        int                     lun_list_size;

        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(&ll_info, 0, sizeof (ll_info));
        ll_info.ll_vers = ISCSI_INTERFACE_VERSION;
        if (targetOid == NULL) {
                /* get lun oid list for all targets */
                ll_info.ll_all_tgts = B_TRUE;
        } else {
                /* get lun oid list for single target */
                ll_info.ll_all_tgts = B_FALSE;
                ll_info.ll_tgt_oid = (uint32_t)targetOid->objectSequenceNumber;
        }
        ll_info.ll_in_cnt = 0;

        /*
         * Issue ioctl to obtain the number of target LUNs.
         */
        if (ioctl(fd, ISCSI_LUN_OID_LIST_GET, &ll_info) != 0) {
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_LUN_LIST_GET ioctl failed, errno: %d", errno);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        }

        lun_list_size = sizeof (iscsi_lun_list_t);
        if (ll_info.ll_out_cnt > 1) {
                lun_list_size += (sizeof (iscsi_if_lun_t) *
                    (ll_info.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 = ll_info.ll_all_tgts;
        illp->ll_tgt_oid = ll_info.ll_tgt_oid;
        illp->ll_in_cnt = ll_info.ll_out_cnt;

        /* Issue the same ioctl again to get 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 set authentication method. */
static IMA_STATUS
setAuthMethods(IMA_OID oid, IMA_UINT *pMethodCount,
    const IMA_AUTHMETHOD *pMethodList)
{
        int fd;
        int i;
        iscsi_auth_props_t auth;

        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(&auth, 0, sizeof (iscsi_auth_props_t));
        auth.a_vers = ISCSI_INTERFACE_VERSION;
        auth.a_oid = (uint32_t)oid.objectSequenceNumber;
        /* First do a get because other data fields may exist */
        if (ioctl(fd, ISCSI_AUTH_GET, &auth) != 0) {
                /* EMPTY */
                /* It is fine if there is no other data fields. */
        }
        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 get authentication method. */
static IMA_STATUS
getAuthMethods(IMA_OID oid, IMA_UINT *pMethodCount, IMA_AUTHMETHOD *pMethodList)
{
        int fd, i;
        iscsi_auth_props_t auth;

        if (pMethodList == NULL) {
                *pMethodCount = 0;
                return (IMA_STATUS_SUCCESS);
        }

        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(&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);
        }

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

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

IMA_API IMA_STATUS IMA_GetPhbaOidList(
                IMA_OID_LIST **ppList
)
{
        *ppList = (IMA_OID_LIST*)calloc(1, sizeof (IMA_OID_LIST));
        if (*ppList == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppList)->oidCount = 0;
        return (IMA_STATUS_SUCCESS);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetPhbaProperties(
                IMA_OID phbaOid,
                IMA_PHBA_PROPERTIES *pProps
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetPhbaStatus(
                IMA_OID phbaOid,
                IMA_PHBA_STATUS *pStatus
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetPhbaDownloadProperties(
                IMA_OID phbaOid,
                IMA_PHBA_DOWNLOAD_PROPERTIES *pProps
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_IsPhbaDownloadFile(
                IMA_OID phbaOid,
                const IMA_WCHAR *pFileName,
                IMA_PHBA_DOWNLOAD_IMAGE_PROPERTIES *pProps
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_PhbaDownload(
                IMA_OID phbaOid,
                IMA_PHBA_DOWNLOAD_IMAGE_TYPE imageType,
                const IMA_WCHAR *pFileName
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

IMA_API IMA_STATUS IMA_GetPnpOidList(
                IMA_OID pnpOid,
                IMA_OID_LIST **ppList
)
{
        /*
         * Always return the same object ID for the pnp as the spec
         * states that this function will always return a list of at least
         * one element
         */
        pnpOid.objectType = IMA_OBJECT_TYPE_PNP;
        pnpOid.ownerId = pluginOwnerId;
        pnpOid.objectSequenceNumber = ISCSI_INITIATOR_OID;

        *ppList = (IMA_OID_LIST*)calloc(1, sizeof (IMA_OID_LIST) +
            (1* sizeof (IMA_OID)));

        if (*ppList == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }

        (*ppList)->oidCount = 1;
        (void) memcpy(&(*ppList)->oids[0], &pnpOid, sizeof (pnpOid));
        return (IMA_STATUS_SUCCESS);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetPnpProperties(
                IMA_OID pnpOid,
                IMA_PNP_PROPERTIES *pProps
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetPnpStatistics(
                IMA_OID pnpOid,
                IMA_PNP_STATISTICS *pStats
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetIpProperties(
                IMA_OID oid,
                IMA_IP_PROPERTIES *pProps
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_SetDefaultGateway(
                IMA_OID oid,
                IMA_IP_ADDRESS defaultGateway
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_SetDnsServerAddress(
                IMA_OID oid,
                const IMA_IP_ADDRESS *pPrimaryDnsServerAddress,
                const IMA_IP_ADDRESS *pAlternateDnsServerAddress
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_SetSubnetMask(
                IMA_OID oid,
                IMA_IP_ADDRESS subnetMask
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_SetIpConfigMethod(
                IMA_OID oid,
                IMA_BOOL enableDhcpIpConfiguration
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

IMA_API IMA_STATUS IMA_RegisterForObjectPropertyChanges(
                IMA_OBJECT_PROPERTY_FN pClientFn
)
{
        pObjectPropertyCallback = pClientFn;
        return (IMA_STATUS_SUCCESS);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_DeregisterForObjectPropertyChanges(
                IMA_OBJECT_PROPERTY_FN pClientFn
)
{
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_RegisterForObjectVisibilityChanges(
                IMA_OBJECT_VISIBILITY_FN pClientFn
)
{
        pObjectVisibilityCallback = pClientFn;
        return (IMA_STATUS_SUCCESS);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_DeregisterForObjectVisibilityChanges(
                IMA_OBJECT_VISIBILITY_FN pClientFn
)
{
        return (IMA_STATUS_SUCCESS);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetNetworkPortStatus(
                IMA_OID portOid,
                IMA_NETWORK_PORT_STATUS *pStaus
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetNetworkPortalOidList(
                IMA_OID pnpOid,
                IMA_OID_LIST **ppList
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetNetworkPortalProperties(
                IMA_OID networkPortalOid,
                IMA_NETWORK_PORTAL_PROPERTIES *pProps
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_SetNetworkPortalIpAddress(
                IMA_OID networkPortalOid,
                const IMA_IP_ADDRESS NewIpAddress
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_RemoveStaleData(
                IMA_OID lhbaOid
)
{
        return (IMA_ERROR_NOT_SUPPORTED);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetIpsecProperties(
                IMA_OID oid,
                IMA_IPSEC_PROPERTIES *pProps
)
{
        pProps->ipsecSupported = IMA_TRUE;
        pProps->implementedInHardware = IMA_FALSE;
        pProps->implementedInSoftware = IMA_TRUE;

        return (IMA_STATUS_SUCCESS);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetLhbaProperties(
                IMA_OID lhbaOid,
                IMA_LHBA_PROPERTIES *pProps
)
{

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

        if (lhbaObjectId.objectSequenceNumber != ISCSI_INITIATOR_OID) {
                return (IMA_ERROR_OBJECT_NOT_FOUND);
        }

        (void) memset(pProps, 0, sizeof (IMA_LHBA_PROPERTIES));
        (void) mbstowcs(pProps->osDeviceName, OS_DEVICE_NAME,
            OS_DEVICE_NAME_LEN);
        pProps->luExposingSupported = IMA_FALSE;
        pProps->isDestroyable = IMA_FALSE;
        pProps->staleDataRemovable = IMA_FALSE;
        pProps->staleDataSize = 0;
        pProps->initiatorAuthMethodsSettable = IMA_TRUE;
        pProps->targetAuthMethodsSettable = IMA_FALSE;

        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS IMA_GetLnpOidList(
                IMA_OID_LIST **ppList
)
{
        *ppList = (IMA_OID_LIST *) calloc(1, (sizeof (IMA_OID_LIST)));
        if (*ppList == NULL) {
                return (IMA_ERROR_INSUFFICIENT_MEMORY);
        }
        (*ppList)->oidCount = 0;

        return (IMA_STATUS_SUCCESS);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetLnpProperties(
                IMA_OID lnpOid,
                IMA_LNP_PROPERTIES *pProps
)
{
        return (IMA_ERROR_OBJECT_NOT_FOUND);
}

#define IMA_DISK_DEVICE_NAME_PREFIX     "/dev/rdsk/"
#define IMA_TAPE_DEVICE_NAME_PREFIX     "/dev/rmt/"
static int
get_lun_devlink(di_devlink_t link, void *osDeviceName)
{
        if ((strncmp(IMA_DISK_DEVICE_NAME_PREFIX, di_devlink_path(link),
            strlen(IMA_DISK_DEVICE_NAME_PREFIX)) == 0) ||
            (strncmp(IMA_TAPE_DEVICE_NAME_PREFIX, di_devlink_path(link),
            strlen(IMA_TAPE_DEVICE_NAME_PREFIX)) == 0)) {
                (void) mbstowcs((wchar_t *)osDeviceName, di_devlink_path(link),
                    MAXPATHLEN);
                return (DI_WALK_TERMINATE);
        }

        return (DI_WALK_CONTINUE);
}

/* ARGSUSED */
IMA_API IMA_STATUS IMA_GetPluginProperties(
        IMA_OID pluginOid,
        IMA_PLUGIN_PROPERTIES *pProps
)
{
        pProps->supportedImaVersion = 1;
        libSwprintf(pProps->vendor, L"%ls", LIBRARY_PROPERTY_VENDOR);
        libSwprintf(pProps->implementationVersion, L"%ls",
            LIBRARY_PROPERTY_IMPLEMENTATION_VERSION);
        libSwprintf(pProps->fileName, L"%ls", LIBRARY_FILE_NAME);
        GetBuildTime(&(pProps->buildTime));
        pProps->lhbasCanBeCreatedAndDestroyed = IMA_FALSE;
        return (IMA_STATUS_SUCCESS);
}

IMA_STATUS getDiscoveryAddressPropertiesList(
    SUN_IMA_DISC_ADDR_PROP_LIST **ppList
)
{
        int                 fd;
        int                 i;
        int                 discovery_addr_list_size;
        iscsi_addr_list_t   *ialp, al_info;

        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(&al_info, 0, sizeof (al_info));
        al_info.al_vers = ISCSI_INTERFACE_VERSION;
        al_info.al_in_cnt = 0;

        /*
         * Issue ISCSI_DISCOVERY_ADDR_LIST_GET ioctl to obtain the number of
         * discovery addresses.
         */
        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, &al_info) != 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);
        }

        discovery_addr_list_size = sizeof (iscsi_addr_list_t);
        if (al_info.al_out_cnt > 1) {
                discovery_addr_list_size += (sizeof (iscsi_addr_t) *
                    al_info.al_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 = al_info.al_out_cnt;

        /*
         * Issue ISCSI_DISCOVERY_ADDR_LIST_GET ioctl again to obtain the
         * discovery addresses.
         */
        if (ioctl(fd, ISCSI_DISCOVERY_ADDR_LIST_GET, ialp) != 0) {
                free(ialp);
                (void) close(fd);
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_DISCOVERY_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 {
                        /* Should not happen */
                        syslog(LOG_USER|LOG_DEBUG,
                        "ISCSI_STATIC_GET returned bad address");
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }

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

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

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


/* ARGSUSED */
IMA_STATUS sendTargets(
    IMA_TARGET_ADDRESS address,
    SUN_IMA_DISC_ADDRESS_KEY_PROPERTIES **ppList
)
{
        char    *colonPos;
        char    discAddrStr[SUN_IMA_IP_ADDRESS_LEN];
        int     fd;
        int     ctr;
        int     stl_sz;
        iscsi_sendtgts_list_t   *stl_hdr = NULL;
        IMA_BOOL                retry = IMA_TRUE;

#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;

        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);
        }


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

        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);
        }

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);
                        syslog(LOG_USER|LOG_DEBUG,
                        "ISCSI_STATIC_GET returned bad address");
                        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_API IMA_STATUS SUN_IMA_GetTunableProperties(
        IMA_OID oid,
        ISCSI_TUNABLE_PARAM *param)
{
        int fd;
        iscsi_tunable_object_t pg;

        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(&pg, 0, sizeof (iscsi_tunable_object_t));
        pg.t_param = param->tunable_objectType;
        pg.t_oid = (uint32_t)oid.objectSequenceNumber;
        if (ioctl(fd, ISCSI_TUNABLE_PARAM_GET, &pg) == -1) {
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TUNABLE_PARAM_GET ioctl failed, errno: %d", errno);
                (void) close(fd);
                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
        } else {
                long long value;
                char tmp[MAX_LONG_LONG_STRING_LEN], *ptr = NULL;
                if (pg.t_set == B_FALSE) {
                        /* default value */
                        (void) close(fd);
                        return (IMA_STATUS_SUCCESS);
                }
                value = (long long)pg.t_value.v_integer;
                ptr = lltostr(value, &tmp[MAX_LONG_LONG_STRING_LEN -1]);
                if ((ptr != NULL) && (ptr != tmp)) {
                        tmp[MAX_LONG_LONG_STRING_LEN - 1] = '\0';
                } else {
                        (void) close(fd);
                        return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
                switch (param->tunable_objectType) {
                        case ISCSI_RX_TIMEOUT_VALUE:
                                (void) strlcpy(param->tunable_objectValue,
                                    ptr, strlen(ptr) + 1);
                                break;
                        case ISCSI_CONN_DEFAULT_LOGIN_MAX:
                                (void) strlcpy(param->tunable_objectValue,
                                    ptr, strlen(ptr) + 1);
                                break;
                        case ISCSI_LOGIN_POLLING_DELAY:
                                (void) strlcpy(param->tunable_objectValue,
                                    ptr, strlen(ptr) + 1);
                                break;
                        default:
                                break;
                }
        }
        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}

IMA_API IMA_STATUS SUN_IMA_SetTunableProperties(
        IMA_OID oid,
        ISCSI_TUNABLE_PARAM *param)
{
        int fd;
        iscsi_tunable_object_t  ps;

        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(&ps, 0, sizeof (iscsi_tunable_object_t));
        ps.t_oid = oid.objectSequenceNumber;
        ps.t_param = param->tunable_objectType;
        switch (param->tunable_objectType) {
                long tmp;
                case ISCSI_RX_TIMEOUT_VALUE:
                case ISCSI_CONN_DEFAULT_LOGIN_MAX:
                case ISCSI_LOGIN_POLLING_DELAY:
                        tmp = strtol(param->tunable_objectValue,
                            NULL, 10);
                        if (((tmp == 0) && (errno == EINVAL)) ||
                            ((tmp == LONG_MAX) && (errno == ERANGE)) ||
                            ((tmp == LONG_MIN) && (errno == ERANGE))) {
                                (void) close(fd);
                                return (IMA_ERROR_INVALID_PARAMETER);
                        }
                        ps.t_value.v_integer = (uint32_t)tmp;
                        break;
                default:
                        break;
        }
        if (ioctl(fd, ISCSI_TUNABLE_PARAM_SET, &ps)) {
                int tmpErrno = errno;
                syslog(LOG_USER|LOG_DEBUG,
                    "ISCSI_TUNABLE_PARAM_SET ioctl failed, errno: %d", errno);
                (void) close(fd);
                switch (tmpErrno) {
                        case ENOTSUP :
                                return (IMA_ERROR_NOT_SUPPORTED);
                        default:
                                return (IMA_ERROR_UNEXPECTED_OS_ERROR);
                }
        }
        (void) close(fd);
        return (IMA_STATUS_SUCCESS);
}